src/share/vm/opto/ifnode.cpp
Index Unified diffs Context diffs Sdiffs Patch New Old Previous File Next File hotspot Cdiff src/share/vm/opto/ifnode.cpp

src/share/vm/opto/ifnode.cpp

Print this page
rev 7552 : 8066103: C2's range check smearing allows out of bound array accesses
Summary: range check smearing uncorrectly adjust first range check in a list of range checks to cover all of them
Reviewed-by:
rev 7553 : reviews
rev 7554 : John's review

*** 872,889 **** // then we are guaranteed to fail, so just start interpreting there. // We 'expand' the top 3 range checks to include all post-dominating // checks. // The top 3 range checks seen ! struct RangeCheck prev_checks[3]; int nb_checks = 0; // Low and high offsets seen so far jint off_lo = offset1; jint off_hi = offset1; ! // Scan for the top 2 checks and collect range of offsets for (int dist = 0; dist < 999; dist++) { // Range-Check scan limit if (dom->Opcode() == Op_If && // Not same opcode? prev_dom->in(0) == dom) { // One path of test does dominate? if (dom == this) return NULL; // dead loop // See if this is a range check --- 872,892 ---- // then we are guaranteed to fail, so just start interpreting there. // We 'expand' the top 3 range checks to include all post-dominating // checks. // The top 3 range checks seen ! const int NRC =3; ! RangeCheck prev_checks[NRC]; int nb_checks = 0; // Low and high offsets seen so far jint off_lo = offset1; jint off_hi = offset1; ! bool found_immediate_dominator = false; ! ! // Scan for the top checks and collect range of offsets for (int dist = 0; dist < 999; dist++) { // Range-Check scan limit if (dom->Opcode() == Op_If && // Not same opcode? prev_dom->in(0) == dom) { // One path of test does dominate? if (dom == this) return NULL; // dead loop // See if this is a range check
*** 892,949 **** int flip2 = dom->as_If()->is_range_check(range2, index2, offset2); // See if this is a _matching_ range check, checking against // the same array bounds. if (flip2 == flip1 && range2 == range1 && index2 == index1 && dom->outcnt() == 2) { // Gather expanded bounds off_lo = MIN2(off_lo,offset2); off_hi = MAX2(off_hi,offset2); ! // Record top 3 range checks ! prev_checks[nb_checks%3].ctl = prev_dom; ! prev_checks[nb_checks%3].off = offset2; nb_checks++; } } prev_dom = dom; dom = up_one_dom(dom); if (!dom) break; } ! // Attempt to widen the dominating range check to cover some later // ones. Since range checks "fail" by uncommon-trapping to the ! // interpreter, widening a check can make us speculative enter the ! // interpreter. If we see range-check deopt's, do not widen! if (!phase->C->allow_range_check_smearing()) return NULL; // Didn't find prior covering check, so cannot remove anything. if (nb_checks == 0) { return NULL; } // Constant indices only need to check the upper bound. // Non-constant indices must check both low and high. ! int chk0 = (nb_checks - 1) % 3; if (index1) { ! struct RangeCheck rc0 = prev_checks[chk0]; if (nb_checks == 1) { if (rc0.ctl->in(0)->in(1) == in(1)) { // If we match the test exactly, then the top test covers ! // both our lower and upper bounds. Valid only if there's ! // a single dominating range check: for all we know this ! // range check was widened and accesses that depend on it ! // also depend on the previous range checks to be correct. ! adjust_check(rc0.ctl, range1, index1, flip1, off_lo, igvn); prev_dom = rc0.ctl; } else { return NULL; } } else { // If the top range check's constant is the min or max of // all constants we widen the next one to cover the whole // range of constants. ! int chk1 = (nb_checks - 2) % 3; ! struct RangeCheck rc1 = prev_checks[chk1]; if (rc0.off == off_lo) { adjust_check(rc1.ctl, range1, index1, flip1, off_hi, igvn); prev_dom = rc1.ctl; } else if (rc0.off == off_hi) { adjust_check(rc1.ctl, range1, index1, flip1, off_lo, igvn); --- 895,960 ---- int flip2 = dom->as_If()->is_range_check(range2, index2, offset2); // See if this is a _matching_ range check, checking against // the same array bounds. if (flip2 == flip1 && range2 == range1 && index2 == index1 && dom->outcnt() == 2) { + if (nb_checks == 0 && dom->in(1) == in(1)) { + // Found an immediately dominating test at the same offset. + // This kind of back-to-back test can be eliminated locally, + // and there is no need to search further for dominating tests. + assert(offset2 == offset1, "Same test but different offsets"); + found_immediate_dominator = true; + break; + } // Gather expanded bounds off_lo = MIN2(off_lo,offset2); off_hi = MAX2(off_hi,offset2); ! // Record top NRC range checks ! prev_checks[nb_checks%NRC].ctl = prev_dom; ! prev_checks[nb_checks%NRC].off = offset2; nb_checks++; } } prev_dom = dom; dom = up_one_dom(dom); if (!dom) break; } ! if (!found_immediate_dominator) { // Attempt to widen the dominating range check to cover some later // ones. Since range checks "fail" by uncommon-trapping to the ! // interpreter, widening a check can make us speculatively enter ! // the interpreter. If we see range-check deopt's, do not widen! if (!phase->C->allow_range_check_smearing()) return NULL; // Didn't find prior covering check, so cannot remove anything. if (nb_checks == 0) { return NULL; } // Constant indices only need to check the upper bound. // Non-constant indices must check both low and high. ! int chk0 = (nb_checks - 1) % NRC; if (index1) { ! RangeCheck rc0 = prev_checks[chk0]; if (nb_checks == 1) { if (rc0.ctl->in(0)->in(1) == in(1)) { // If we match the test exactly, then the top test covers ! // both our lower and upper bounds. Valid only if there's no ! // other range check between us and the top test: for all we ! // know this range check was widened and accesses that ! // depend on it also depend on the previous range checks to ! // be correct. prev_dom = rc0.ctl; } else { return NULL; } } else { // If the top range check's constant is the min or max of // all constants we widen the next one to cover the whole // range of constants. ! int chk1 = (nb_checks - 2) % NRC; ! RangeCheck rc1 = prev_checks[chk1]; if (rc0.off == off_lo) { adjust_check(rc1.ctl, range1, index1, flip1, off_hi, igvn); prev_dom = rc1.ctl; } else if (rc0.off == off_hi) { adjust_check(rc1.ctl, range1, index1, flip1, off_lo, igvn);
*** 955,966 **** // accesses it protects to successfully read/write out of // bounds. if (nb_checks == 2) { return NULL; } ! int chk2 = (nb_checks - 3) % 3; ! struct RangeCheck rc2 = prev_checks[chk2]; // The top range check a+i covers interval: -a <= i < length-a // The second range check b+i covers interval: -b <= i < length-b if (rc1.off <= rc0.off) { // if b <= a, we change the second range check to: // -min_of_all_constants <= i < length-min_of_all_constants --- 966,977 ---- // accesses it protects to successfully read/write out of // bounds. if (nb_checks == 2) { return NULL; } ! int chk2 = (nb_checks - 3) % NRC; ! RangeCheck rc2 = prev_checks[chk2]; // The top range check a+i covers interval: -a <= i < length-a // The second range check b+i covers interval: -b <= i < length-b if (rc1.off <= rc0.off) { // if b <= a, we change the second range check to: // -min_of_all_constants <= i < length-min_of_all_constants
*** 990,1006 **** } prev_dom = rc2.ctl; } } } else { ! struct RangeCheck rc0 = prev_checks[chk0]; // 'Widen' the offset of the 1st and only covering check adjust_check(rc0.ctl, range1, index1, flip1, off_hi, igvn); // Test is now covered by prior checks, dominate it out prev_dom = rc0.ctl; } ! } else { // Scan for an equivalent test Node *cmp; int dist = 0; // Cutoff limit for search --- 1001,1017 ---- } prev_dom = rc2.ctl; } } } else { ! RangeCheck rc0 = prev_checks[chk0]; // 'Widen' the offset of the 1st and only covering check adjust_check(rc0.ctl, range1, index1, flip1, off_hi, igvn); // Test is now covered by prior checks, dominate it out prev_dom = rc0.ctl; } ! } } else { // Scan for an equivalent test Node *cmp; int dist = 0; // Cutoff limit for search
src/share/vm/opto/ifnode.cpp
Index Unified diffs Context diffs Sdiffs Patch New Old Previous File Next File