Bug type: miscompilation
Component: ScalarEvolution
Bug location: llvm/lib/Analysis/ScalarEvolution.cpp: ScalarEvolution::createAddRecFromPHI
ScalarEvolution::getSequentialMinMaxExpr
ScalarEvolution::isGuaranteedNotToBePoison
ScalarEvolution::isSCEVExprNeverPoison
Issue title: [indvars] Miscompile when loop body has an operation with Undefined Behaviour
Labels: miscompilation
llvm:SCEV
Reported by: Nirhar

Here is the problematic IR:
```
define i32 @widget() {
bb:
  br label %bb1

bb1:                                              ; preds = %bb5, %bb
  %phi = phi i32 [ 0, %bb ], [ %udiv6, %bb5 ]
  %phi2 = phi i32 [ 1, %bb ], [ %add, %bb5 ]
  %icmp = icmp eq i32 %phi, 0
  br i1 %icmp, label %bb3, label %bb8

bb3:                                              ; preds = %bb1
  %udiv = udiv i32 10, %phi2
  %urem = urem i32 %udiv, 10
  %icmp4 = icmp eq i32 %urem, 0
  br i1 %icmp4, label %bb7, label %bb5

bb5:                                              ; preds = %bb3
  %udiv6 = udiv i32 %phi2, 0
  %add = add i32 %phi2, 1
  br label %bb1

bb7:                                              ; preds = %bb3
  ret i32 5

bb8:                                              ; preds = %bb1
  ret i32 7
}
```
produces incorrect IR when `indvars` pass is run. I suspect this is because of the `%udiv6 = udiv i32 %phi2, 0` divide by zero operation. 
Look at the indvars transformation here: https://godbolt.org/z/cz1r5178h
The original IR must return 5, while the transformed IR returns 7

Proof of wrong transformation: https://alive2.llvm.org/ce/z/vPFhzg

Comments: author: nikic
body: Is this a fuzzer-generated test case?
author: Nirhar
body: > Is this a fuzzer-generated test case?

Hi @nikic , yes this test was fuzzer-generated.

Fix patch:
commit f7ef0721d60f85e1f699f8d1b83d4402ae19b122
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Sun Dec 1 20:11:09 2024 +0800

    [SCEV] Do not allow refinement in the rewriting of BEValue (#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.

diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 885c5985f9d2..b20c6a13cb6b 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -2187,6 +2187,9 @@ private:
   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 c3f296b9ff33..882e938e69c0 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,22 @@ 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() &&
+          ::impliesPoison(BEValue, Start)) {
+        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;
+        }
       }
     }
   }
@@ -7324,6 +7320,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))


Regression tests:
file: llvm/test/Analysis/ScalarEvolution/pr117133.ll
commands: opt -S -disable-output "-passes=print<scalar-evolution>" < %s 2>&1
tests: test_name: test_poisonous
test_body: define i64 @test_poisonous(i64 %a, i32 %n) {
entry:
  br label %loop.body

loop.body:                                        ; preds = %loop.body, %entry
  %indvar1 = phi i32 [ 0, %entry ], [ %indvar1.next, %loop.body ]
  %indvar2 = phi i64 [ 0, %entry ], [ %mul, %loop.body ]
  %indvar1.next = add i32 %indvar1, 1
  %ext = zext i32 %indvar1.next to i64
  %mul = mul i64 %ext, %a
  %exitcond = icmp sgt i32 %indvar1.next, %n
  br i1 %exitcond, label %loop.exit, label %loop.body

loop.exit:                                        ; preds = %loop.body
  ret i64 %mul
}

test_name: widget
test_body: 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
}

file: llvm/test/Transforms/IndVarSimplify/pr117133.ll
commands: opt -S -passes=indvars < %s
tests: test_name: widget
test_body: 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
}