-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[SCEV] Do not allow refinement in the rewriting of BEValue #117152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-llvm-transforms @llvm/pr-subscribers-llvm-analysis Author: Yingwei Zheng (dtcxzyw) ChangesSee the following case:
llvm-project/llvm/lib/Analysis/ScalarEvolution.cpp Lines 5926 to 5950 in d7d6fb1
It converts phi(0, ({1,+,1}<nuw><nsw><%b1> /u 0)) into phi(0 / 0, ({1,+,1}<nuw><nsw><%b1> /u 0)) . Then it simplifies the expr into {0,+,1}<nuw><nsw><%b1> /u 0 .
As we did in acd700a, this patch disallows udiv simplification if we cannot prove that the denominator is a well-defined non-zero value. Fixes #117133. Full diff: https://github.com/llvm/llvm-project/pull/117152.diff 5 Files Affected:
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 376f260846bbaa..4d0fcfef78f1c2 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -3422,9 +3422,14 @@ const SCEV *ScalarEvolution::getUDivExpr(const SCEV *LHS,
if (const SCEV *S = UniqueSCEVs.FindNodeOrInsertPos(ID, IP))
return S;
+ // If the denominator is zero, the udiv will trap.
+ auto IsValidDenominator = [&] {
+ return isGuaranteedNotToBePoison(RHS) && isKnownNonZero(RHS);
+ };
+
// 0 udiv Y == 0
if (const SCEVConstant *LHSC = dyn_cast<SCEVConstant>(LHS))
- if (LHSC->getValue()->isZero())
+ if (LHSC->getValue()->isZero() && IsValidDenominator())
return LHS;
if (const SCEVConstant *RHSC = dyn_cast<SCEVConstant>(RHS)) {
@@ -3560,7 +3565,7 @@ const SCEV *ScalarEvolution::getUDivExpr(const SCEV *LHS,
if (MME && MME->getNumOperands() == 2 &&
isa<SCEVConstant>(MME->getOperand(0)) &&
cast<SCEVConstant>(MME->getOperand(0))->getAPInt() == -NegC &&
- MME->getOperand(1) == RHS)
+ MME->getOperand(1) == RHS && IsValidDenominator())
return getZero(LHS->getType());
}
}
diff --git a/llvm/test/Analysis/ScalarEvolution/fold.ll b/llvm/test/Analysis/ScalarEvolution/fold.ll
index 670523ca1bb5b9..5fde3b18da9fe9 100644
--- a/llvm/test/Analysis/ScalarEvolution/fold.ll
+++ b/llvm/test/Analysis/ScalarEvolution/fold.ll
@@ -214,7 +214,7 @@ define i64 @test10(i64 %a, i64 %b) {
ret i64 %t2
}
-define i64 @test11(i64 %a) {
+define i64 @test11(i64 noundef range(i64 1, 0) %a) {
; CHECK-LABEL: 'test11'
; CHECK-NEXT: Classifying expressions for: @test11
; CHECK-NEXT: %t0 = udiv i64 0, %a
diff --git a/llvm/test/Analysis/ScalarEvolution/pr117133.ll b/llvm/test/Analysis/ScalarEvolution/pr117133.ll
new file mode 100644
index 00000000000000..75f15055ce3d94
--- /dev/null
+++ b/llvm/test/Analysis/ScalarEvolution/pr117133.ll
@@ -0,0 +1,53 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -disable-output "-passes=print<scalar-evolution>" < %s 2>&1 | FileCheck %s
+
+define i32 @widget() {
+; CHECK-LABEL: 'widget'
+; CHECK-NEXT: Classifying expressions for: @widget
+; CHECK-NEXT: %phi = phi i32 [ 0, %b ], [ %udiv6, %b5 ]
+; CHECK-NEXT: --> %phi U: [0,1) S: [0,1) Exits: <<Unknown>> LoopDispositions: { %b1: Variant }
+; CHECK-NEXT: %phi2 = phi i32 [ 1, %b ], [ %add, %b5 ]
+; CHECK-NEXT: --> {1,+,1}<nuw><nsw><%b1> U: [1,2) S: [1,2) Exits: <<Unknown>> LoopDispositions: { %b1: Computable }
+; CHECK-NEXT: %udiv = udiv i32 10, %phi2
+; CHECK-NEXT: --> (10 /u {1,+,1}<nuw><nsw><%b1>) U: [10,11) S: [10,11) Exits: <<Unknown>> LoopDispositions: { %b1: Computable }
+; CHECK-NEXT: %urem = urem i32 %udiv, 10
+; 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 }
+; CHECK-NEXT: %udiv6 = udiv i32 %phi2, 0
+; CHECK-NEXT: --> ({1,+,1}<nuw><nsw><%b1> /u 0) U: empty-set S: empty-set Exits: <<Unknown>> LoopDispositions: { %b1: Computable }
+; CHECK-NEXT: %add = add i32 %phi2, 1
+; CHECK-NEXT: --> {2,+,1}<nuw><nsw><%b1> U: [2,3) S: [2,3) Exits: <<Unknown>> LoopDispositions: { %b1: Computable }
+; CHECK-NEXT: Determining loop execution counts for: @widget
+; CHECK-NEXT: Loop %b1: <multiple exits> Unpredictable backedge-taken count.
+; CHECK-NEXT: exit count for b1: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: exit count for b3: i32 0
+; CHECK-NEXT: Loop %b1: constant max backedge-taken count is i32 0
+; CHECK-NEXT: Loop %b1: symbolic max backedge-taken count is i32 0
+; CHECK-NEXT: symbolic max exit count for b1: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: symbolic max exit count for b3: i32 0
+;
+b:
+ br label %b1
+
+b1: ; preds = %b5, %b
+ %phi = phi i32 [ 0, %b ], [ %udiv6, %b5 ]
+ %phi2 = phi i32 [ 1, %b ], [ %add, %b5 ]
+ %icmp = icmp eq i32 %phi, 0
+ br i1 %icmp, label %b3, label %b8
+
+b3: ; preds = %b1
+ %udiv = udiv i32 10, %phi2
+ %urem = urem i32 %udiv, 10
+ %icmp4 = icmp eq i32 %urem, 0
+ br i1 %icmp4, label %b7, label %b5
+
+b5: ; preds = %b3
+ %udiv6 = udiv i32 %phi2, 0
+ %add = add i32 %phi2, 1
+ br label %b1
+
+b7: ; preds = %b3
+ ret i32 5
+
+b8: ; preds = %b1
+ ret i32 7
+}
diff --git a/llvm/test/Analysis/ScalarEvolution/udiv-of-x-xsmaxone-fold.ll b/llvm/test/Analysis/ScalarEvolution/udiv-of-x-xsmaxone-fold.ll
index 9405c0f726ac7f..5694a505ccb316 100644
--- a/llvm/test/Analysis/ScalarEvolution/udiv-of-x-xsmaxone-fold.ll
+++ b/llvm/test/Analysis/ScalarEvolution/udiv-of-x-xsmaxone-fold.ll
@@ -1,7 +1,7 @@
; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py
; RUN: opt -disable-output -passes="print<scalar-evolution>" < %s 2>&1 | FileCheck %s
-define i32 @test_expr_with_constant_1(i32 %x) {
+define i32 @test_expr_with_constant_1(i32 noundef range(i32 1, 0) %x) {
; CHECK-LABEL: 'test_expr_with_constant_1'
; CHECK-NEXT: Classifying expressions for: @test_expr_with_constant_1
; CHECK-NEXT: %smax = tail call i32 @llvm.smax.i32(i32 %x, i32 1)
@@ -20,7 +20,7 @@ entry:
}
; Non-1 constant: (-2 + (2 smax %x)) /u %x
-define i32 @test_expr_with_constant_2(i32 %x) {
+define i32 @test_expr_with_constant_2(i32 noundef range(i32 1, 0) %x) {
; CHECK-LABEL: 'test_expr_with_constant_2'
; CHECK-NEXT: Classifying expressions for: @test_expr_with_constant_2
; CHECK-NEXT: %smax = tail call i32 @llvm.smax.i32(i32 %x, i32 2)
diff --git a/llvm/test/Transforms/IndVarSimplify/pr117133.ll b/llvm/test/Transforms/IndVarSimplify/pr117133.ll
new file mode 100644
index 00000000000000..31545ecba94cdb
--- /dev/null
+++ b/llvm/test/Transforms/IndVarSimplify/pr117133.ll
@@ -0,0 +1,45 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -passes=indvars < %s | FileCheck %s
+
+
+define i32 @widget() {
+; CHECK-LABEL: define i32 @widget() {
+; CHECK-NEXT: [[B:.*:]]
+; CHECK-NEXT: br label %[[B1:.*]]
+; CHECK: [[B1]]:
+; CHECK-NEXT: br i1 true, label %[[B3:.*]], label %[[B8:.*]]
+; CHECK: [[B3]]:
+; CHECK-NEXT: br i1 true, label %[[B7:.*]], label %[[B5:.*]]
+; CHECK: [[B5]]:
+; CHECK-NEXT: br label %[[B1]]
+; CHECK: [[B7]]:
+; CHECK-NEXT: ret i32 5
+; CHECK: [[B8]]:
+; CHECK-NEXT: ret i32 7
+;
+b:
+ br label %b1
+
+b1:
+ %phi = phi i32 [ 0, %b ], [ %udiv6, %b5 ]
+ %phi2 = phi i32 [ 1, %b ], [ %add, %b5 ]
+ %icmp = icmp eq i32 %phi, 0
+ br i1 %icmp, label %b3, label %b8
+
+b3:
+ %udiv = udiv i32 10, %phi2
+ %urem = urem i32 %udiv, 10
+ %icmp4 = icmp eq i32 %urem, 0
+ br i1 %icmp4, label %b7, label %b5
+
+b5:
+ %udiv6 = udiv i32 %phi2, 0
+ %add = add i32 %phi2, 1
+ br label %b1
+
+b7:
+ ret i32 5
+
+b8:
+ ret i32 7
+}
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that this approach is correct. If I understand correctly, you're basically trying to add the invariant that simplification of SCEV expressions must preserve undefined behavior. But to achieve that, this would be only a very partial implementation, e.g. we could also lose it through %div * 0
or %div - %div
or any number of other ways.
I think the problem here is rather in that particular addrec construction code. We basically have a derefinement issue here, similar to what we see in select simplification.
We could hit similar derefinement issues with poison rather than UB, probably some example involving umin_seq(%maybe_poison, 0)
. And with @fhahn's patches for per-use nowrap flags, this would become a more significant problem.
So I think what we need to guard against is derefinement when replacing the start value.
Yeah. For this case,
Do we need an |
Assuming this special case isn't particularly important, can we get away with just checking that the SCEV doesn't contain UB / poison? |
db15881
to
5ba4943
Compare
ping @dtcxzyw ! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be great to construct a test case where poison is problematic.
} | ||
|
||
; Don't fold %indvar2 into (zext {0,+,1}) * %a | ||
define i64 @test_poisonous(i64 %a, i32 %n) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before:
Printing analysis 'Scalar Evolution Analysis' for function 'test':
Classifying expressions for: @test
%indvar1 = phi i32 [ 0, %entry ], [ %indvar1.next, %loop.body ]
--> {0,+,1}<%loop.body> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop.body: Computable }
%indvar2 = phi i64 [ 0, %entry ], [ %mul, %loop.body ]
--> ((zext i32 {0,+,1}<%loop.body> to i64) * %a) U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop.body: Computable }
%indvar1.next = add i32 %indvar1, 1
--> {1,+,1}<%loop.body> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop.body: Computable }
%ext = zext i32 %indvar1.next to i64
--> (zext i32 {1,+,1}<%loop.body> to i64) U: [0,4294967296) S: [0,4294967296) Exits: <<Unknown>> LoopDispositions: { %loop.body: Computable }
%mul = mul i64 %ext, %a
--> ((zext i32 {1,+,1}<%loop.body> to i64) * %a) U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop.body: Computable }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
/cherry-pick f7ef072 |
Failed to cherry-pick: f7ef072 https://github.com/llvm/llvm-project/actions/runs/12105303593 Please manually backport the fix and push it to your github fork. Once this is done, please create a pull request |
This is a followup to llvm#117152. That patch introduced a check for UB/poison on BEValue. However, the SCEV we're actually going to use is Shifted. In some cases, it's possible for Shifted to contain UB, while BEValue doesn't. In the test case the values are: BEValue: (-1 * (zext i8 (-83 + ((-83 /u {1,+,1}<%loop>) * {-1,+,-1}<%loop>)) to i32))<nuw><nsw> Shifted: (-173 + (-1 * (zext i8 ((-83 /u {0,+,1}<%loop>) * {0,+,-1}<%loop>) to i32))<nuw><nsw>)<nuw><nsw> Fixes llvm#123550.
This is a followup to #117152. That patch introduced a check for UB/poison on BEValue. However, the SCEV we're actually going to use is Shifted. In some cases, it's possible for Shifted to contain UB, while BEValue doesn't. In the test case the values are: BEValue: (-1 * (zext i8 (-83 + ((-83 /u {1,+,1}<%loop>) * {-1,+,-1}<%loop>)) to i32))<nuw><nsw> Shifted: (-173 + (-1 * (zext i8 ((-83 /u {0,+,1}<%loop>) * {0,+,-1}<%loop>) to i32))<nuw><nsw>)<nuw><nsw> Fixes #123550.
This is a followup to llvm#117152. That patch introduced a check for UB/poison on BEValue. However, the SCEV we're actually going to use is Shifted. In some cases, it's possible for Shifted to contain UB, while BEValue doesn't. In the test case the values are: BEValue: (-1 * (zext i8 (-83 + ((-83 /u {1,+,1}<%loop>) * {-1,+,-1}<%loop>)) to i32))<nuw><nsw> Shifted: (-173 + (-1 * (zext i8 ((-83 /u {0,+,1}<%loop>) * {0,+,-1}<%loop>) to i32))<nuw><nsw>)<nuw><nsw> Fixes llvm#123550. (cherry picked from commit 07efe2c)
See the following case:
ScalarEvolution::createAddRecFromPHI
gives a wrong SCEV result for%phi
:llvm-project/llvm/lib/Analysis/ScalarEvolution.cpp
Lines 5926 to 5950 in d7d6fb1
It converts
phi(0, ({1,+,1}<nuw><nsw><%b1> /u 0))
intophi(0 / 0, ({1,+,1}<nuw><nsw><%b1> /u 0))
. Then it simplifies the expr into{0,+,1}<nuw><nsw><%b1> /u 0
.As we did in acd700a, this patch disallows udiv simplification if we cannot prove that the denominator is a well-defined non-zero value.
Fixes #117133.