[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