Skip to content

Commit aef6140

Browse files
committed
[60281] IV overflow detection: differentiate between limit checked IV and other ivs.
PullRequest: graal/19515
2 parents df11f89 + d8e6f1d commit aef6140

File tree

2 files changed

+82
-48
lines changed

2 files changed

+82
-48
lines changed

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

Lines changed: 72 additions & 48 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,62 +620,85 @@ public boolean counterNeverOverflows() {
619620
}
620621

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

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/common/util/LoopUtility.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import jdk.graal.compiler.nodes.cfg.HIRBlock;
5454
import jdk.graal.compiler.nodes.extended.OpaqueValueNode;
5555
import jdk.graal.compiler.nodes.loop.BasicInductionVariable;
56+
import jdk.graal.compiler.nodes.loop.CountedLoopInfo;
5657
import jdk.graal.compiler.nodes.loop.InductionVariable;
5758
import jdk.graal.compiler.nodes.loop.Loop;
5859
import jdk.graal.compiler.nodes.loop.LoopsData;
@@ -61,6 +62,15 @@
6162

6263
public class LoopUtility {
6364

65+
public static long tripCountSignedExact(CountedLoopInfo loop) {
66+
ValueNode maxTripCountNode = loop.maxTripCountNode();
67+
final long maxTripCountAsSigned = maxTripCountNode.asJavaConstant().asLong();
68+
if (maxTripCountAsSigned < 0) {
69+
throw new ArithmeticException("Unsigned value " + maxTripCountAsSigned + " overflows signed range");
70+
}
71+
return maxTripCountAsSigned;
72+
}
73+
6474
public static long addExact(int bits, long a, long b) {
6575
if (bits == 8) {
6676
byte ba = NumUtil.safeToByteAE(a);

0 commit comments

Comments
 (0)