Skip to content

Commit 7f06d8a

Browse files
authored
[SCEV] Retain SCEVSequentialMinMaxExpr if an operand may trigger UB. (llvm#110824)
Retain SCEVSequentialMinMaxExpr if an operand may trigger UB, e.g. if there is an UDiv operand that may divide by 0 or poison PR: llvm#110824
1 parent 9cc6d6e commit 7f06d8a

File tree

5 files changed

+36
-15
lines changed

5 files changed

+36
-15
lines changed

llvm/include/llvm/Analysis/ScalarEvolution.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2168,6 +2168,9 @@ class ScalarEvolution {
21682168
bool isGuaranteedToTransferExecutionTo(const Instruction *A,
21692169
const Instruction *B);
21702170

2171+
/// Returns true if \p Op is guaranteed to not be poison.
2172+
static bool isGuaranteedNotToBePoison(const SCEV *Op);
2173+
21712174
/// Return true if the SCEV corresponding to \p I is never poison. Proving
21722175
/// this is more complex than proving that just \p I is never poison, since
21732176
/// SCEV commons expressions across control flow, and you can have cases

llvm/include/llvm/Analysis/ScalarEvolutionExpressions.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,8 +511,9 @@ class SCEVUMinExpr : public SCEVMinMaxExpr {
511511
/// This node is the base class for sequential/in-order min/max selections.
512512
/// Note that their fundamental difference from SCEVMinMaxExpr's is that they
513513
/// are early-returning upon reaching saturation point.
514-
/// I.e. given `0 umin_seq poison`, the result will be `0`,
515-
/// while the result of `0 umin poison` is `poison`.
514+
/// I.e. given `0 umin_seq poison`, the result will be `0`, while the result of
515+
/// `0 umin poison` is `poison`. When returning early, later expressions are not
516+
/// executed, so `0 umin_seq (%x u/ 0)` does not result in undefined behavior.
516517
class SCEVSequentialMinMaxExpr : public SCEVNAryExpr {
517518
friend class ScalarEvolution;
518519

llvm/lib/Analysis/ScalarEvolution.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4200,7 +4200,7 @@ bool ScalarEvolution::canReuseInstruction(
42004200

42014201
// Either the value can't be poison, or the S would also be poison if it
42024202
// is.
4203-
if (PoisonVals.contains(V) || isGuaranteedNotToBePoison(V))
4203+
if (PoisonVals.contains(V) || ::isGuaranteedNotToBePoison(V))
42044204
continue;
42054205

42064206
auto *I = dyn_cast<Instruction>(V);
@@ -4303,6 +4303,16 @@ ScalarEvolution::getSequentialMinMaxExpr(SCEVTypes Kind,
43034303
}
43044304

43054305
for (unsigned i = 1, e = Ops.size(); i != e; ++i) {
4306+
bool MayBeUB = SCEVExprContains(Ops[i], [this](const SCEV *S) {
4307+
auto *UDiv = dyn_cast<SCEVUDivExpr>(S);
4308+
// The UDiv may be UB if the divisor is poison or zero. Unless the divisor
4309+
// is a non-zero constant, we have to assume the UDiv may be UB.
4310+
return UDiv && (!isKnownNonZero(UDiv->getOperand(1)) ||
4311+
!isGuaranteedNotToBePoison(UDiv->getOperand(1)));
4312+
});
4313+
4314+
if (MayBeUB)
4315+
continue;
43064316
// We can replace %x umin_seq %y with %x umin %y if either:
43074317
// * %y being poison implies %x is also poison.
43084318
// * %x cannot be the saturating value (e.g. zero for umin).
@@ -7298,6 +7308,11 @@ bool ScalarEvolution::isGuaranteedToTransferExecutionTo(const Instruction *A,
72987308
return false;
72997309
}
73007310

7311+
bool ScalarEvolution::isGuaranteedNotToBePoison(const SCEV *Op) {
7312+
SCEVPoisonCollector PC(/* LookThroughMaybePoisonBlocking */ true);
7313+
visitAll(Op, PC);
7314+
return PC.MaybePoison.empty();
7315+
}
73017316

73027317
bool ScalarEvolution::isSCEVExprNeverPoison(const Instruction *I) {
73037318
// Only proceed if we can prove that I does not yield poison.

llvm/test/Analysis/ScalarEvolution/umin-seq-operand-may-trigger-ub.ll

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@
44
; The UDiv in the latch may never be executed. The backedge-taken-count
55
; expressions must account for the fact that evaluating the UDiv
66
; unconditionally may trigger UB.
7-
; FIXME: umin_seq should be used instead of umin for BTCs.
87
define i64 @multi_exit_exit_count_with_udiv_by_value_in_latch(ptr %dst, i64 %N) {
98
; CHECK-LABEL: 'multi_exit_exit_count_with_udiv_by_value_in_latch'
109
; CHECK-NEXT: Determining loop execution counts for: @multi_exit_exit_count_with_udiv_by_value_in_latch
11-
; CHECK-NEXT: Loop %loop.header: <multiple exits> backedge-taken count is ((42 /u %N) umin (0 smax %N))
10+
; CHECK-NEXT: Loop %loop.header: <multiple exits> backedge-taken count is ((0 smax %N) umin_seq (42 /u %N))
1211
; CHECK-NEXT: exit count for loop.header: (0 smax %N)
1312
; CHECK-NEXT: exit count for loop.latch: (42 /u %N)
1413
; CHECK-NEXT: Loop %loop.header: constant max backedge-taken count is i64 42
15-
; CHECK-NEXT: Loop %loop.header: symbolic max backedge-taken count is ((42 /u %N) umin (0 smax %N))
14+
; CHECK-NEXT: Loop %loop.header: symbolic max backedge-taken count is ((0 smax %N) umin_seq (42 /u %N))
1615
; CHECK-NEXT: symbolic max exit count for loop.header: (0 smax %N)
1716
; CHECK-NEXT: symbolic max exit count for loop.latch: (42 /u %N)
1817
; CHECK-NEXT: Loop %loop.header: Trip multiple is 1
@@ -41,7 +40,6 @@ exit:
4140
; The UDiv in the latch may never be executed. The backedge-taken-count
4241
; expressions must account for the fact that evaluating the UDiv
4342
; unconditionally may trigger UB.
44-
; FIXME: umin_seq should be used instead of umin for BTCs.
4543
define i64 @multi_exit_exit_count_with_udiv_by_value_in_latch_different_bounds(ptr %dst, i64 %N, i64 %M) {
4644
; CHECK-LABEL: 'multi_exit_exit_count_with_udiv_by_value_in_latch_different_bounds'
4745
; CHECK-NEXT: Determining loop execution counts for: @multi_exit_exit_count_with_udiv_by_value_in_latch_different_bounds

llvm/test/Transforms/LoopVectorize/trip-count-expansion-may-introduce-ub.ll

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -463,9 +463,10 @@ define i64 @multi_exit_4_exit_count_with_udiv_by_value_in_latch(ptr %dst, i64 %N
463463
; CHECK-LABEL: define i64 @multi_exit_4_exit_count_with_udiv_by_value_in_latch(
464464
; CHECK-SAME: ptr [[DST:%.*]], i64 [[N:%.*]]) {
465465
; CHECK-NEXT: entry:
466-
; CHECK-NEXT: [[SMAX:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 0)
467466
; CHECK-NEXT: [[TMP0:%.*]] = udiv i64 42, [[N]]
468-
; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[SMAX]], i64 [[TMP0]])
467+
; CHECK-NEXT: [[TMP8:%.*]] = freeze i64 [[TMP0]]
468+
; CHECK-NEXT: [[SMAX:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 0)
469+
; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[TMP8]], i64 [[SMAX]])
469470
; CHECK-NEXT: [[TMP1:%.*]] = add nuw nsw i64 [[UMIN]], 1
470471
; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ule i64 [[TMP1]], 4
471472
; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label [[SCALAR_PH:%.*]], label [[VECTOR_PH:%.*]]
@@ -598,9 +599,10 @@ define i64 @multi_exit_4_exit_count_with_udiv_by_frozen_value_in_latch(ptr %dst,
598599
; CHECK-SAME: ptr [[DST:%.*]], i64 [[N:%.*]]) {
599600
; CHECK-NEXT: entry:
600601
; CHECK-NEXT: [[FR_N:%.*]] = freeze i64 [[N]]
601-
; CHECK-NEXT: [[TMP2:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 0)
602602
; CHECK-NEXT: [[TMP0:%.*]] = udiv i64 42, [[FR_N]]
603-
; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[TMP2]], i64 [[TMP0]])
603+
; CHECK-NEXT: [[TMP1:%.*]] = freeze i64 [[TMP0]]
604+
; CHECK-NEXT: [[SMAX:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 0)
605+
; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[TMP1]], i64 [[SMAX]])
604606
; CHECK-NEXT: [[TMP3:%.*]] = add nuw nsw i64 [[UMIN]], 1
605607
; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ule i64 [[TMP3]], 4
606608
; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label [[SCALAR_PH:%.*]], label [[VECTOR_PH:%.*]]
@@ -786,12 +788,13 @@ define i64 @multi_exit_4_exit_count_with_urem_by_value_in_latch(ptr %dst, i64 %N
786788
; CHECK-LABEL: define i64 @multi_exit_4_exit_count_with_urem_by_value_in_latch(
787789
; CHECK-SAME: ptr [[DST:%.*]], i64 [[N:%.*]]) {
788790
; CHECK-NEXT: entry:
789-
; CHECK-NEXT: [[SMAX:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 0)
790791
; CHECK-NEXT: [[TMP0:%.*]] = udiv i64 42, [[N]]
791792
; CHECK-NEXT: [[TMP1:%.*]] = mul nuw i64 [[N]], [[TMP0]]
792793
; CHECK-NEXT: [[TMP2:%.*]] = sub i64 42, [[TMP1]]
793794
; CHECK-NEXT: [[SMAX1:%.*]] = call i64 @llvm.smax.i64(i64 [[TMP2]], i64 0)
794-
; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[SMAX]], i64 [[SMAX1]])
795+
; CHECK-NEXT: [[TMP10:%.*]] = freeze i64 [[SMAX1]]
796+
; CHECK-NEXT: [[SMAX2:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 0)
797+
; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[TMP10]], i64 [[SMAX2]])
795798
; CHECK-NEXT: [[TMP3:%.*]] = add nuw i64 [[UMIN]], 1
796799
; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ule i64 [[TMP3]], 4
797800
; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label [[SCALAR_PH:%.*]], label [[VECTOR_PH:%.*]]
@@ -1004,9 +1007,10 @@ define i64 @multi_exit_4_exit_count_with_udiv_by_value_in_latch1(ptr %dst, i64 %
10041007
; CHECK-LABEL: define i64 @multi_exit_4_exit_count_with_udiv_by_value_in_latch1(
10051008
; CHECK-SAME: ptr [[DST:%.*]], i64 [[N:%.*]]) {
10061009
; CHECK-NEXT: entry:
1007-
; CHECK-NEXT: [[SMAX:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 0)
10081010
; CHECK-NEXT: [[TMP0:%.*]] = udiv i64 42, [[N]]
1009-
; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[SMAX]], i64 [[TMP0]])
1011+
; CHECK-NEXT: [[TMP8:%.*]] = freeze i64 [[TMP0]]
1012+
; CHECK-NEXT: [[SMAX:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 0)
1013+
; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[TMP8]], i64 [[SMAX]])
10101014
; CHECK-NEXT: [[TMP1:%.*]] = add nuw nsw i64 [[UMIN]], 1
10111015
; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ule i64 [[TMP1]], 4
10121016
; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label [[SCALAR_PH:%.*]], label [[VECTOR_PH:%.*]]

0 commit comments

Comments
 (0)