Skip to content

Commit 62a8b33

Browse files
committed
iv overflow detection: differentiate between limit checked IV and other
ivs
1 parent 219e600 commit 62a8b33

File tree

1 file changed

+87
-64
lines changed

1 file changed

+87
-64
lines changed

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/loop/CountedLoopInfo.java

Lines changed: 87 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import jdk.graal.compiler.nodes.util.IntegerHelper;
5656
import jdk.graal.compiler.nodes.util.SignedIntegerHelper;
5757
import jdk.graal.compiler.nodes.util.UnsignedIntegerHelper;
58+
import jdk.graal.compiler.phases.common.util.LoopUtility;
5859
import jdk.vm.ci.meta.DeoptimizationAction;
5960
import jdk.vm.ci.meta.DeoptimizationReason;
6061
import jdk.vm.ci.meta.JavaKind;
@@ -619,70 +620,92 @@ public boolean counterNeverOverflows() {
619620
}
620621

621622
public boolean ivCanNeverOverflow(InductionVariable iv) {
622-
if (!isLimitIncluded && iv.isConstantStride() && Loop.absStrideIsOne(iv)) {
623-
return true;
624-
}
625-
if (loop.loopBegin().isProtectedNonOverflowingUnsigned()) {
626-
return true;
627-
}
628-
// @formatter:off
629-
/*
630-
* Following comment reasons about the simplest possible loop form:
631-
*
632-
* for(i = 0;i < end;i += stride)
633-
*
634-
* The problem is we want to create an overflow guard for the loop that can be hoisted
635-
* before the loop, i.e., the overflow guard must not have loop variant inputs else it must
636-
* be scheduled inside the loop. This means we cannot refer explicitly to the induction
637-
* variable's phi but must establish a relation between end, stride and max (max integer
638-
* range for a given loop) that is sufficient for most cases.
639-
*
640-
* We know that a head counted loop with a stride > 1 may overflow if the stride is big
641-
* enough that end + stride will be > MAX, i.e. it overflows into negative value range.
642-
*
643-
* It is important that "end" in this context is the checked value of the loop condition:
644-
* i.e., an arbitrary value. There is no relation between end and MAX established except
645-
* that based on the integer representation we know that end <= MAX.
646-
*
647-
* A loop can overflow if the last checked value of the iv allows an overflow in the next
648-
* iteration: the value range for which an overflow can happen is [MAX-(stride-1),MAX] e.g.
649-
*
650-
* MAX=10, stride = 3, overflow if number > 10
651-
* end = MAX -> 10 -> 10 + 3 = 13 -> overflow
652-
* end = MAX-1 -> 9 -> 9 + 3 = 12 -> overflow
653-
* end = MAX-2 -> 8 -> 8 + 3 = 11 -> overflow
654-
* end = MAX-3 -> 7 -> 7 + 3 = 10 -> No overflow at MAX - stride
655-
*
656-
* Note that this guard is pessimistic, i.e., it marks loops as potentially overflowing that
657-
* are actually not overflowing. Consider the following loop:
658-
*
659-
* <pre>
660-
* for(i = MAX-56; i < MAX, i += 8)
661-
* </pre>
662-
*
663-
* where i in last loop body visit = MAX - 8, i after = MAX, no overflow
664-
*
665-
* which is wrongly detected as overflowing since "end" is element of [MAX-(stride-1),MAX]
666-
* which is [MAX-7,MAX] and end is MAX. We handle such cases with a speculation and disable
667-
* counted loop detection on subsequent compilations. We can only avoid such false positive
668-
* detections by actually computing the number of iterations with a division, however we try
669-
* to avoid that since that may be part of the fast path.
670-
*
671-
* And additional backup strategy could be to actually emit the precise guard inside the
672-
* loop if the deopt already failed, but we refrain from this for now for simplicity
673-
* reasons.
674-
*/
675-
// @formatter:on
676-
IntegerStamp endStamp = (IntegerStamp) getTripCountLimit().stamp(NodeView.DEFAULT);
677-
ValueNode strideNode = iv.strideNode();
678-
IntegerStamp strideStamp = (IntegerStamp) strideNode.stamp(NodeView.DEFAULT);
679-
IntegerHelper integerHelper = getCounterIntegerHelper();
680-
if (getDirection() == InductionVariable.Direction.Up) {
681-
long max = integerHelper.maxValue();
682-
return integerHelper.compare(endStamp.upperBound(), max - (strideStamp.upperBound() - 1) - (isLimitIncluded ? 1 : 0)) <= 0;
683-
} else if (getDirection() == InductionVariable.Direction.Down) {
684-
long min = integerHelper.minValue();
685-
return integerHelper.compare(min + (1 - strideStamp.lowerBound()) + (isLimitIncluded ? 1 : 0), endStamp.lowerBound()) <= 0;
623+
if (iv == getLimitCheckedIV()) {
624+
if (!isLimitIncluded && iv.isConstantStride() && Loop.absStrideIsOne(iv)) {
625+
return true;
626+
}
627+
if (loop.loopBegin().isProtectedNonOverflowingUnsigned()) {
628+
return true;
629+
}
630+
// @formatter:off
631+
/*
632+
* Following comment reasons about the simplest possible loop form:
633+
*
634+
* for(i = 0;i < end;i += stride)
635+
*
636+
* The problem is we want to create an overflow guard for the loop that can be hoisted
637+
* before the loop, i.e., the overflow guard must not have loop variant inputs else it must
638+
* be scheduled inside the loop. This means we cannot refer explicitly to the induction
639+
* variable's phi but must establish a relation between end, stride and max (max integer
640+
* range for a given loop) that is sufficient for most cases.
641+
*
642+
* We know that a head counted loop with a stride > 1 may overflow if the stride is big
643+
* enough that end + stride will be > MAX, i.e. it overflows into negative value range.
644+
*
645+
* It is important that "end" in this context is the checked value of the loop condition:
646+
* i.e., an arbitrary value. There is no relation between end and MAX established except
647+
* that based on the integer representation we know that end <= MAX.
648+
*
649+
* A loop can overflow if the last checked value of the iv allows an overflow in the next
650+
* iteration: the value range for which an overflow can happen is [MAX-(stride-1),MAX] e.g.
651+
*
652+
* MAX=10, stride = 3, overflow if number > 10
653+
* end = MAX -> 10 -> 10 + 3 = 13 -> overflow
654+
* end = MAX-1 -> 9 -> 9 + 3 = 12 -> overflow
655+
* end = MAX-2 -> 8 -> 8 + 3 = 11 -> overflow
656+
* end = MAX-3 -> 7 -> 7 + 3 = 10 -> No overflow at MAX - stride
657+
*
658+
* Note that this guard is pessimistic, i.e., it marks loops as potentially overflowing that
659+
* are actually not overflowing. Consider the following loop:
660+
*
661+
* <pre>
662+
* for(i = MAX-56; i < MAX, i += 8)
663+
* </pre>
664+
*
665+
* where i in last loop body visit = MAX - 8, i after = MAX, no overflow
666+
*
667+
* which is wrongly detected as overflowing since "end" is element of [MAX-(stride-1),MAX]
668+
* which is [MAX-7,MAX] and end is MAX. We handle such cases with a speculation and disable
669+
* counted loop detection on subsequent compilations. We can only avoid such false positive
670+
* detections by actually computing the number of iterations with a division, however we try
671+
* to avoid that since that may be part of the fast path.
672+
*
673+
* And additional backup strategy could be to actually emit the precise guard inside the
674+
* loop if the deopt already failed, but we refrain from this for now for simplicity
675+
* reasons.
676+
*/
677+
// @formatter:on
678+
IntegerStamp endStamp = (IntegerStamp) getTripCountLimit().stamp(NodeView.DEFAULT);
679+
ValueNode strideNode = getLimitCheckedIV().strideNode();
680+
IntegerStamp strideStamp = (IntegerStamp) strideNode.stamp(NodeView.DEFAULT);
681+
IntegerHelper integerHelper = getCounterIntegerHelper();
682+
if (getDirection() == InductionVariable.Direction.Up) {
683+
long max = integerHelper.maxValue();
684+
return integerHelper.compare(endStamp.upperBound(), max - (strideStamp.upperBound() - 1) - (isLimitIncluded ? 1 : 0)) <= 0;
685+
} else if (getDirection() == InductionVariable.Direction.Down) {
686+
long min = integerHelper.minValue();
687+
return integerHelper.compare(min + (1 - strideStamp.lowerBound()) + (isLimitIncluded ? 1 : 0), endStamp.lowerBound()) <= 0;
688+
}
689+
return false;
690+
} else {
691+
/*
692+
* All over IVs: This IV is not compared against limit and thus we cannot play the trick
693+
* comparing against the end stamp. We have to compute (if possible) the extremum value
694+
* and use that.
695+
*/
696+
if (iv.isConstantInit() && isConstantMaxTripCount() && iv.isConstantStride()) {
697+
try {
698+
final int bits = IntegerStamp.getBits(iv.valueNode().stamp(NodeView.DEFAULT));
699+
long tripCountMinus1 = LoopUtility.subtractExact(bits, maxTripCountNode().asJavaConstant().asLong(), 1);
700+
long stripTimesTripCount = LoopUtility.multiplyExact(bits, iv.constantStride(), tripCountMinus1);
701+
@SuppressWarnings("unused")
702+
long extremum = LoopUtility.addExact(bits, stripTimesTripCount, iv.initNode().asJavaConstant().asLong());
703+
return true;
704+
} catch (ArithmeticException e) {
705+
// overflow
706+
return false;
707+
}
708+
}
686709
}
687710
return false;
688711
}

0 commit comments

Comments
 (0)