[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 07:07:21 PST 2024
https://github.com/dtcxzyw updated https://github.com/llvm/llvm-project/pull/117152
>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 5ba4943d99916f287ade5b4a2cd6c427970f2883 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Thu, 21 Nov 2024 23:06:52 +0800
Subject: [PATCH 2/2] [SCEV] Do not allow refinement in the rewriting of
BEValue
---
llvm/include/llvm/Analysis/ScalarEvolution.h | 3 ++
llvm/lib/Analysis/ScalarEvolution.cpp | 47 ++++++++++---------
llvm/test/Analysis/ScalarEvolution/fold.ll | 2 +-
.../test/Analysis/ScalarEvolution/pr117133.ll | 2 +-
.../udiv-of-x-xsmaxone-fold.ll | 4 +-
.../Transforms/IndVarSimplify/pr117133.ll | 2 +-
6 files changed, 34 insertions(+), 26 deletions(-)
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 885c5985f9d23a..b20c6a13cb6bd7 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -2187,6 +2187,9 @@ class ScalarEvolution {
bool isGuaranteedToTransferExecutionTo(const Instruction *A,
const Instruction *B);
+ /// Returns true if \p Op is guaranteed not to cause immediate UB.
+ bool isGuaranteedNotToCauseUB(const SCEV *Op);
+
/// Returns true if \p Op is guaranteed to not be poison.
static bool isGuaranteedNotToBePoison(const SCEV *Op);
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 376f260846bbaa..091a0762fad212 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -4307,15 +4307,7 @@ ScalarEvolution::getSequentialMinMaxExpr(SCEVTypes Kind,
}
for (unsigned i = 1, e = Ops.size(); i != e; ++i) {
- bool MayBeUB = SCEVExprContains(Ops[i], [this](const SCEV *S) {
- auto *UDiv = dyn_cast<SCEVUDivExpr>(S);
- // The UDiv may be UB if the divisor is poison or zero. Unless the divisor
- // is a non-zero constant, we have to assume the UDiv may be UB.
- return UDiv && (!isKnownNonZero(UDiv->getOperand(1)) ||
- !isGuaranteedNotToBePoison(UDiv->getOperand(1)));
- });
-
- if (MayBeUB)
+ if (!isGuaranteedNotToCauseUB(Ops[i]))
continue;
// We can replace %x umin_seq %y with %x umin %y if either:
// * %y being poison implies %x is also poison.
@@ -5933,18 +5925,21 @@ const SCEV *ScalarEvolution::createAddRecFromPHI(PHINode *PN) {
// We can generalize this saying that i is the shifted value of BEValue
// by one iteration:
// PHI(f(0), f({1,+,1})) --> f({0,+,1})
- const SCEV *Shifted = SCEVShiftRewriter::rewrite(BEValue, L, *this);
- const SCEV *Start = SCEVInitRewriter::rewrite(Shifted, L, *this, false);
- if (Shifted != getCouldNotCompute() &&
- Start != getCouldNotCompute()) {
- const SCEV *StartVal = getSCEV(StartValueV);
- if (Start == StartVal) {
- // Okay, for the entire analysis of this edge we assumed the PHI
- // to be symbolic. We now need to go back and purge all of the
- // entries for the scalars that use the symbolic expression.
- forgetMemoizedResults(SymbolicName);
- insertValueToMap(PN, Shifted);
- return Shifted;
+
+ // Do not allow refinement in rewriting of BEValue.
+ if (isGuaranteedNotToCauseUB(BEValue)) {
+ const SCEV *Shifted = SCEVShiftRewriter::rewrite(BEValue, L, *this);
+ const SCEV *Start = SCEVInitRewriter::rewrite(Shifted, L, *this, false);
+ if (Shifted != getCouldNotCompute() && Start != getCouldNotCompute()) {
+ const SCEV *StartVal = getSCEV(StartValueV);
+ if (Start == StartVal) {
+ // Okay, for the entire analysis of this edge we assumed the PHI
+ // to be symbolic. We now need to go back and purge all of the
+ // entries for the scalars that use the symbolic expression.
+ forgetMemoizedResults(SymbolicName);
+ insertValueToMap(PN, Shifted);
+ return Shifted;
+ }
}
}
}
@@ -7322,6 +7317,16 @@ bool ScalarEvolution::isGuaranteedNotToBePoison(const SCEV *Op) {
return PC.MaybePoison.empty();
}
+bool ScalarEvolution::isGuaranteedNotToCauseUB(const SCEV *Op) {
+ return !SCEVExprContains(Op, [this](const SCEV *S) {
+ auto *UDiv = dyn_cast<SCEVUDivExpr>(S);
+ // The UDiv may be UB if the divisor is poison or zero. Unless the divisor
+ // is a non-zero constant, we have to assume the UDiv may be UB.
+ return UDiv && (!isKnownNonZero(UDiv->getOperand(1)) ||
+ !isGuaranteedNotToBePoison(UDiv->getOperand(1)));
+ });
+}
+
bool ScalarEvolution::isSCEVExprNeverPoison(const Instruction *I) {
// Only proceed if we can prove that I does not yield poison.
if (!programUndefinedIfPoison(I))
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