Skip to content

Commit 6f08a0f

Browse files
committed
release/19.x: [SCEV] Do not allow refinement in the rewriting of BEValue (llvm#117152)
1 parent 086d8e6 commit 6f08a0f

File tree

5 files changed

+184
-17
lines changed

5 files changed

+184
-17
lines changed

llvm/include/llvm/Analysis/ScalarEvolution.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2132,6 +2132,12 @@ class ScalarEvolution {
21322132
bool isGuaranteedToTransferExecutionTo(const Instruction *A,
21332133
const Instruction *B);
21342134

2135+
/// Returns true if \p Op is guaranteed not to cause immediate UB.
2136+
bool isGuaranteedNotToCauseUB(const SCEV *Op);
2137+
2138+
/// Returns true if \p Op is guaranteed to not be poison.
2139+
static bool isGuaranteedNotToBePoison(const SCEV *Op);
2140+
21352141
/// Return true if the SCEV corresponding to \p I is never poison. Proving
21362142
/// this is more complex than proving that just \p I is never poison, since
21372143
/// SCEV commons expressions across control flow, and you can have cases

llvm/lib/Analysis/ScalarEvolution.cpp

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4217,7 +4217,7 @@ bool ScalarEvolution::canReuseInstruction(
42174217

42184218
// Either the value can't be poison, or the S would also be poison if it
42194219
// is.
4220-
if (PoisonVals.contains(V) || isGuaranteedNotToBePoison(V))
4220+
if (PoisonVals.contains(V) || ::isGuaranteedNotToBePoison(V))
42214221
continue;
42224222

42234223
auto *I = dyn_cast<Instruction>(V);
@@ -4320,6 +4320,8 @@ ScalarEvolution::getSequentialMinMaxExpr(SCEVTypes Kind,
43204320
}
43214321

43224322
for (unsigned i = 1, e = Ops.size(); i != e; ++i) {
4323+
if (!isGuaranteedNotToCauseUB(Ops[i]))
4324+
continue;
43234325
// We can replace %x umin_seq %y with %x umin %y if either:
43244326
// * %y being poison implies %x is also poison.
43254327
// * %x cannot be the saturating value (e.g. zero for umin).
@@ -5936,18 +5938,22 @@ const SCEV *ScalarEvolution::createAddRecFromPHI(PHINode *PN) {
59365938
// We can generalize this saying that i is the shifted value of BEValue
59375939
// by one iteration:
59385940
// PHI(f(0), f({1,+,1})) --> f({0,+,1})
5939-
const SCEV *Shifted = SCEVShiftRewriter::rewrite(BEValue, L, *this);
5940-
const SCEV *Start = SCEVInitRewriter::rewrite(Shifted, L, *this, false);
5941-
if (Shifted != getCouldNotCompute() &&
5942-
Start != getCouldNotCompute()) {
5943-
const SCEV *StartVal = getSCEV(StartValueV);
5944-
if (Start == StartVal) {
5945-
// Okay, for the entire analysis of this edge we assumed the PHI
5946-
// to be symbolic. We now need to go back and purge all of the
5947-
// entries for the scalars that use the symbolic expression.
5948-
forgetMemoizedResults(SymbolicName);
5949-
insertValueToMap(PN, Shifted);
5950-
return Shifted;
5941+
5942+
// Do not allow refinement in rewriting of BEValue.
5943+
if (isGuaranteedNotToCauseUB(BEValue)) {
5944+
const SCEV *Shifted = SCEVShiftRewriter::rewrite(BEValue, L, *this);
5945+
const SCEV *Start = SCEVInitRewriter::rewrite(Shifted, L, *this, false);
5946+
if (Shifted != getCouldNotCompute() && Start != getCouldNotCompute() &&
5947+
::impliesPoison(BEValue, Start)) {
5948+
const SCEV *StartVal = getSCEV(StartValueV);
5949+
if (Start == StartVal) {
5950+
// Okay, for the entire analysis of this edge we assumed the PHI
5951+
// to be symbolic. We now need to go back and purge all of the
5952+
// entries for the scalars that use the symbolic expression.
5953+
forgetMemoizedResults(SymbolicName);
5954+
insertValueToMap(PN, Shifted);
5955+
return Shifted;
5956+
}
59515957
}
59525958
}
59535959
}
@@ -7319,6 +7325,21 @@ bool ScalarEvolution::isGuaranteedToTransferExecutionTo(const Instruction *A,
73197325
return false;
73207326
}
73217327

7328+
bool ScalarEvolution::isGuaranteedNotToBePoison(const SCEV *Op) {
7329+
SCEVPoisonCollector PC(/* LookThroughMaybePoisonBlocking */ true);
7330+
visitAll(Op, PC);
7331+
return PC.MaybePoison.empty();
7332+
}
7333+
7334+
bool ScalarEvolution::isGuaranteedNotToCauseUB(const SCEV *Op) {
7335+
return !SCEVExprContains(Op, [this](const SCEV *S) {
7336+
auto *UDiv = dyn_cast<SCEVUDivExpr>(S);
7337+
// The UDiv may be UB if the divisor is poison or zero. Unless the divisor
7338+
// is a non-zero constant, we have to assume the UDiv may be UB.
7339+
return UDiv && (!isKnownNonZero(UDiv->getOperand(1)) ||
7340+
!isGuaranteedNotToBePoison(UDiv->getOperand(1)));
7341+
});
7342+
}
73227343

73237344
bool ScalarEvolution::isSCEVExprNeverPoison(const Instruction *I) {
73247345
// Only proceed if we can prove that I does not yield poison.
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt -S -disable-output "-passes=print<scalar-evolution>" < %s 2>&1 | FileCheck %s
3+
4+
define i32 @widget() {
5+
; CHECK-LABEL: 'widget'
6+
; CHECK-NEXT: Classifying expressions for: @widget
7+
; CHECK-NEXT: %phi = phi i32 [ 0, %b ], [ %udiv6, %b5 ]
8+
; CHECK-NEXT: --> %phi U: [0,1) S: [0,1) Exits: <<Unknown>> LoopDispositions: { %b1: Variant }
9+
; CHECK-NEXT: %phi2 = phi i32 [ 1, %b ], [ %add, %b5 ]
10+
; CHECK-NEXT: --> {1,+,1}<nuw><nsw><%b1> U: [1,2) S: [1,2) Exits: <<Unknown>> LoopDispositions: { %b1: Computable }
11+
; CHECK-NEXT: %udiv = udiv i32 10, %phi2
12+
; CHECK-NEXT: --> (10 /u {1,+,1}<nuw><nsw><%b1>) U: [10,11) S: [10,11) Exits: <<Unknown>> LoopDispositions: { %b1: Computable }
13+
; CHECK-NEXT: %urem = urem i32 %udiv, 10
14+
; CHECK-NEXT: --> ((-10 * ((10 /u {1,+,1}<nuw><nsw><%b1>) /u 10))<nuw><nsw> + (10 /u {1,+,1}<nuw><nsw><%b1>)) U: [0,1) S: [0,1) Exits: <<Unknown>> LoopDispositions: { %b1: Computable }
15+
; CHECK-NEXT: %udiv6 = udiv i32 %phi2, 0
16+
; CHECK-NEXT: --> ({1,+,1}<nuw><nsw><%b1> /u 0) U: empty-set S: empty-set Exits: <<Unknown>> LoopDispositions: { %b1: Computable }
17+
; CHECK-NEXT: %add = add i32 %phi2, 1
18+
; CHECK-NEXT: --> {2,+,1}<nuw><nsw><%b1> U: [2,3) S: [2,3) Exits: <<Unknown>> LoopDispositions: { %b1: Computable }
19+
; CHECK-NEXT: Determining loop execution counts for: @widget
20+
; CHECK-NEXT: Loop %b1: <multiple exits> Unpredictable backedge-taken count.
21+
; CHECK-NEXT: exit count for b1: ***COULDNOTCOMPUTE***
22+
; CHECK-NEXT: exit count for b3: i32 0
23+
; CHECK-NEXT: Loop %b1: constant max backedge-taken count is i32 0
24+
; CHECK-NEXT: Loop %b1: symbolic max backedge-taken count is i32 0
25+
; CHECK-NEXT: symbolic max exit count for b1: ***COULDNOTCOMPUTE***
26+
; CHECK-NEXT: symbolic max exit count for b3: i32 0
27+
;
28+
b:
29+
br label %b1
30+
31+
b1: ; preds = %b5, %b
32+
%phi = phi i32 [ 0, %b ], [ %udiv6, %b5 ]
33+
%phi2 = phi i32 [ 1, %b ], [ %add, %b5 ]
34+
%icmp = icmp eq i32 %phi, 0
35+
br i1 %icmp, label %b3, label %b8
36+
37+
b3: ; preds = %b1
38+
%udiv = udiv i32 10, %phi2
39+
%urem = urem i32 %udiv, 10
40+
%icmp4 = icmp eq i32 %urem, 0
41+
br i1 %icmp4, label %b7, label %b5
42+
43+
b5: ; preds = %b3
44+
%udiv6 = udiv i32 %phi2, 0
45+
%add = add i32 %phi2, 1
46+
br label %b1
47+
48+
b7: ; preds = %b3
49+
ret i32 5
50+
51+
b8: ; preds = %b1
52+
ret i32 7
53+
}
54+
55+
; Don't fold %indvar2 into (zext {0,+,1}) * %a
56+
define i64 @test_poisonous(i64 %a, i32 %n) {
57+
; CHECK-LABEL: 'test_poisonous'
58+
; CHECK-NEXT: Classifying expressions for: @test_poisonous
59+
; CHECK-NEXT: %indvar1 = phi i32 [ 0, %entry ], [ %indvar1.next, %loop.body ]
60+
; CHECK-NEXT: --> {0,+,1}<%loop.body> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop.body: Computable }
61+
; CHECK-NEXT: %indvar2 = phi i64 [ 0, %entry ], [ %mul, %loop.body ]
62+
; CHECK-NEXT: --> %indvar2 U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop.body: Variant }
63+
; CHECK-NEXT: %indvar1.next = add i32 %indvar1, 1
64+
; CHECK-NEXT: --> {1,+,1}<%loop.body> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop.body: Computable }
65+
; CHECK-NEXT: %ext = zext i32 %indvar1.next to i64
66+
; CHECK-NEXT: --> (zext i32 {1,+,1}<%loop.body> to i64) U: [0,4294967296) S: [0,4294967296) Exits: <<Unknown>> LoopDispositions: { %loop.body: Computable }
67+
; CHECK-NEXT: %mul = mul i64 %ext, %a
68+
; CHECK-NEXT: --> ((zext i32 {1,+,1}<%loop.body> to i64) * %a) U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop.body: Computable }
69+
; CHECK-NEXT: Determining loop execution counts for: @test_poisonous
70+
; CHECK-NEXT: Loop %loop.body: Unpredictable backedge-taken count.
71+
; CHECK-NEXT: Loop %loop.body: Unpredictable constant max backedge-taken count.
72+
; CHECK-NEXT: Loop %loop.body: Unpredictable symbolic max backedge-taken count.
73+
; CHECK-NEXT: Loop %loop.body: Predicated backedge-taken count is (-1 + (1 smax (1 + (sext i32 %n to i64))<nsw>))<nsw>
74+
; CHECK-NEXT: Predicates:
75+
; CHECK-NEXT: {1,+,1}<%loop.body> Added Flags: <nssw>
76+
; CHECK-NEXT: Loop %loop.body: Predicated symbolic max backedge-taken count is (-1 + (1 smax (1 + (sext i32 %n to i64))<nsw>))<nsw>
77+
; CHECK-NEXT: Predicates:
78+
; CHECK-NEXT: {1,+,1}<%loop.body> Added Flags: <nssw>
79+
;
80+
entry:
81+
br label %loop.body
82+
83+
loop.body:
84+
%indvar1 = phi i32 [ 0, %entry ], [ %indvar1.next, %loop.body ]
85+
%indvar2 = phi i64 [ 0, %entry ], [ %mul, %loop.body ]
86+
%indvar1.next = add i32 %indvar1, 1
87+
%ext = zext i32 %indvar1.next to i64
88+
%mul = mul i64 %ext, %a
89+
%exitcond = icmp sgt i32 %indvar1.next, %n
90+
br i1 %exitcond, label %loop.exit, label %loop.body
91+
92+
loop.exit:
93+
ret i64 %mul
94+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt -S -passes=indvars < %s | FileCheck %s
3+
4+
define i32 @widget() {
5+
; CHECK-LABEL: define i32 @widget() {
6+
; CHECK-NEXT: [[B:.*:]]
7+
; CHECK-NEXT: br label %[[B1:.*]]
8+
; CHECK: [[B1]]:
9+
; CHECK-NEXT: br i1 true, label %[[B3:.*]], label %[[B8:.*]]
10+
; CHECK: [[B3]]:
11+
; CHECK-NEXT: br i1 true, label %[[B7:.*]], label %[[B5:.*]]
12+
; CHECK: [[B5]]:
13+
; CHECK-NEXT: br label %[[B1]]
14+
; CHECK: [[B7]]:
15+
; CHECK-NEXT: ret i32 5
16+
; CHECK: [[B8]]:
17+
; CHECK-NEXT: ret i32 7
18+
;
19+
b:
20+
br label %b1
21+
22+
b1:
23+
%phi = phi i32 [ 0, %b ], [ %udiv6, %b5 ]
24+
%phi2 = phi i32 [ 1, %b ], [ %add, %b5 ]
25+
%icmp = icmp eq i32 %phi, 0
26+
br i1 %icmp, label %b3, label %b8
27+
28+
b3:
29+
%udiv = udiv i32 10, %phi2
30+
%urem = urem i32 %udiv, 10
31+
%icmp4 = icmp eq i32 %urem, 0
32+
br i1 %icmp4, label %b7, label %b5
33+
34+
b5:
35+
%udiv6 = udiv i32 %phi2, 0
36+
%add = add i32 %phi2, 1
37+
br label %b1
38+
39+
b7:
40+
ret i32 5
41+
42+
b8:
43+
ret i32 7
44+
}

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -465,9 +465,10 @@ define i64 @multi_exit_4_exit_count_with_udiv_by_value_in_latch(ptr %dst, i64 %N
465465
; CHECK-LABEL: define i64 @multi_exit_4_exit_count_with_udiv_by_value_in_latch(
466466
; CHECK-SAME: ptr [[DST:%.*]], i64 [[N:%.*]]) {
467467
; CHECK-NEXT: entry:
468-
; CHECK-NEXT: [[SMAX:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 0)
469468
; CHECK-NEXT: [[TMP0:%.*]] = udiv i64 42, [[N]]
470-
; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[SMAX]], i64 [[TMP0]])
469+
; CHECK-NEXT: [[TMP8:%.*]] = freeze i64 [[TMP0]]
470+
; CHECK-NEXT: [[SMAX:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 0)
471+
; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[TMP8]], i64 [[SMAX]])
471472
; CHECK-NEXT: [[TMP1:%.*]] = add nuw nsw i64 [[UMIN]], 1
472473
; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ule i64 [[TMP1]], 4
473474
; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label [[SCALAR_PH:%.*]], label [[VECTOR_PH:%.*]]
@@ -653,12 +654,13 @@ define i64 @multi_exit_4_exit_count_with_urem_by_value_in_latch(ptr %dst, i64 %N
653654
; CHECK-LABEL: define i64 @multi_exit_4_exit_count_with_urem_by_value_in_latch(
654655
; CHECK-SAME: ptr [[DST:%.*]], i64 [[N:%.*]]) {
655656
; CHECK-NEXT: entry:
656-
; CHECK-NEXT: [[SMAX:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 0)
657657
; CHECK-NEXT: [[TMP0:%.*]] = udiv i64 42, [[N]]
658658
; CHECK-NEXT: [[TMP1:%.*]] = mul nuw i64 [[N]], [[TMP0]]
659659
; CHECK-NEXT: [[TMP2:%.*]] = sub i64 42, [[TMP1]]
660660
; CHECK-NEXT: [[SMAX1:%.*]] = call i64 @llvm.smax.i64(i64 [[TMP2]], i64 0)
661-
; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[SMAX]], i64 [[SMAX1]])
661+
; CHECK-NEXT: [[TMP10:%.*]] = freeze i64 [[SMAX1]]
662+
; CHECK-NEXT: [[SMAX2:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 0)
663+
; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[TMP10]], i64 [[SMAX2]])
662664
; CHECK-NEXT: [[TMP3:%.*]] = add nuw i64 [[UMIN]], 1
663665
; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ule i64 [[TMP3]], 4
664666
; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label [[SCALAR_PH:%.*]], label [[VECTOR_PH:%.*]]

0 commit comments

Comments
 (0)