[llvm] [SCEV] Add non-poison/non-zero checks on denominators (PR #117152)
Yingwei Zheng via llvm-commits
llvm-commits at lists.llvm.org
Thu Nov 21 04:42:39 PST 2024
https://github.com/dtcxzyw created https://github.com/llvm/llvm-project/pull/117152
See the following case:
```
; bin/opt -passes="print<scalar-evolution>" test.ll --disable-output
define i32 @widget() {
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
}
```
```
%phi2 = phi i32 [ 1, %b ], [ %add, %b5 ] --> {1,+,1}<nuw><nsw><%b1>
%udiv6 = udiv i32 %phi2, 0 --> ({1,+,1}<nuw><nsw><%b1> /u 0)
%phi = phi i32 [ 0, %b ], [ %udiv6, %b5 ] --> ({0,+,1}<nuw><nsw><%b1> /u 0)
```
`ScalarEvolution::createAddRecFromPHI` gives a wrong SCEV result for `%phi`:
https://github.com/llvm/llvm-project/blob/d7d6fb1804415b0f3e7f1cc9290bfb3d711cb707/llvm/lib/Analysis/ScalarEvolution.cpp#L5926-L5950
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 https://github.com/llvm/llvm-project/commit/acd700a24b6f767413db3d525e06d03e4245aa40, this patch disallows udiv simplification if we cannot prove that the denominator is a well-defined non-zero value.
Fixes https://github.com/llvm/llvm-project/issues/117133.
>From 8aecde3e8d931073e1ab2c94cd04bf9084f24cfe Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Thu, 21 Nov 2024 20:16:58 +0800
Subject: [PATCH 1/2] [SCEV] Add pre-commit tests. NFC.
---
.../test/Analysis/ScalarEvolution/pr117133.ll | 53 +++++++++++++++++++
.../Transforms/IndVarSimplify/pr117133.ll | 45 ++++++++++++++++
2 files changed, 98 insertions(+)
create mode 100644 llvm/test/Analysis/ScalarEvolution/pr117133.ll
create mode 100644 llvm/test/Transforms/IndVarSimplify/pr117133.ll
diff --git a/llvm/test/Analysis/ScalarEvolution/pr117133.ll b/llvm/test/Analysis/ScalarEvolution/pr117133.ll
new file mode 100644
index 00000000000000..ab8c952b0e5c84
--- /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: --> ({0,+,1}<nuw><nsw><%b1> /u 0) U: empty-set S: empty-set Exits: <<Unknown>> LoopDispositions: { %b1: Computable }
+; 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/Transforms/IndVarSimplify/pr117133.ll b/llvm/test/Transforms/IndVarSimplify/pr117133.ll
new file mode 100644
index 00000000000000..bb9f1cf4d82cdf
--- /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 false, 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
+}
>From 69157f7ba0fcc5bf0587a72f2582321ffbf52525 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Thu, 21 Nov 2024 20:29:29 +0800
Subject: [PATCH 2/2] [SCEV] Add non-poison/non-zero checks on denominators
---
llvm/lib/Analysis/ScalarEvolution.cpp | 9 +++++++--
llvm/test/Analysis/ScalarEvolution/fold.ll | 2 +-
llvm/test/Analysis/ScalarEvolution/pr117133.ll | 2 +-
.../Analysis/ScalarEvolution/udiv-of-x-xsmaxone-fold.ll | 4 ++--
llvm/test/Transforms/IndVarSimplify/pr117133.ll | 2 +-
5 files changed, 12 insertions(+), 7 deletions(-)
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
index ab8c952b0e5c84..75f15055ce3d94 100644
--- a/llvm/test/Analysis/ScalarEvolution/pr117133.ll
+++ b/llvm/test/Analysis/ScalarEvolution/pr117133.ll
@@ -5,7 +5,7 @@ define i32 @widget() {
; CHECK-LABEL: 'widget'
; CHECK-NEXT: Classifying expressions for: @widget
; CHECK-NEXT: %phi = phi i32 [ 0, %b ], [ %udiv6, %b5 ]
-; CHECK-NEXT: --> ({0,+,1}<nuw><nsw><%b1> /u 0) U: empty-set S: empty-set Exits: <<Unknown>> LoopDispositions: { %b1: Computable }
+; 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
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
index bb9f1cf4d82cdf..31545ecba94cdb 100644
--- a/llvm/test/Transforms/IndVarSimplify/pr117133.ll
+++ b/llvm/test/Transforms/IndVarSimplify/pr117133.ll
@@ -7,7 +7,7 @@ define i32 @widget() {
; CHECK-NEXT: [[B:.*:]]
; CHECK-NEXT: br label %[[B1:.*]]
; CHECK: [[B1]]:
-; CHECK-NEXT: br i1 false, label %[[B3:.*]], label %[[B8:.*]]
+; CHECK-NEXT: br i1 true, label %[[B3:.*]], label %[[B8:.*]]
; CHECK: [[B3]]:
; CHECK-NEXT: br i1 true, label %[[B7:.*]], label %[[B5:.*]]
; CHECK: [[B5]]:
More information about the llvm-commits
mailing list