Bug type: miscompilation
Component: SCCPSolver
Bug location: llvm/lib/Transforms/Utils/SCCPSolver.cpp: SCCPInstVisitor::handleCallResult
SCCPInstVisitor::handleExtractOfWithOverflow
SCCPInstVisitor::visitBinaryOperator
SCCPInstVisitor::visitCastInst
getConstantRange
Issue title: nuw/nsw should not be added to trunc instruction when there are undef bits in the source value
Labels: miscompilation
llvm:transforms
Reported by: weiguozhi

Run ipsccp on the code
```
; opt -passes=ipsccp -S test.ll

define void @foo(i1 %cond1, i32 %p1) {
entry:
  br label %loop

loop:
  %phi1 = phi i64 [undef, %entry], [%phi2, %latch]
  br i1 %cond1, label %then2, label %else2

then2:
  %and = and i64 %phi1, -1095216660481     ; 0xFFFFFF00FFFFFFFF
  br label %latch

else2:
  %ext = zext i32 %p1 to i64 
  %or  = or i64 %ext, 4294967296           ; 0x100000000
  br label %latch

latch:
  %phi2 = phi i64 [%and, %then2], [%or, %else2]
  %shr = lshr i64 %phi2, 32
  %res = trunc i64 %shr to i8
  %cond2 = call i1 @bar(i8 %res)
  br i1 %cond2, label %loop, label %exit

exit:
  ret void
}

declare i1 @bar(i8)
```
LLVM adds nuw/nsw flags to the trunc instruction
```
%res = trunc nuw nsw i64 %shr to i8
```
It means all high bits of %shr are 0. 
But %phi1 may be undef, bits [32..39] of %and are cleared, bits[40..63] can still be undef, and later it is moved to bits[8..31] of %shr, so not all high bits of %shr are 0.


Comments: author: nikic
body: We do exclude ranges with undef here: https://github.com/llvm/llvm-project/blob/3061fed2f05cde574c84a26c702b1e7932fa4d7e/llvm/lib/Transforms/Utils/SCCPSolver.cpp#L118-L119 Maybe the undef flag doesn't get propagated properly somewhere...
author: nikic
body: Simplified test case:
```llvm
define i8 @src(i1 %cond, i8 %a) {
entry: 
  br i1 %cond, label %if, label %join
   
if:
  %a.ext = zext i8 %a to i16 
  br label %join 

join:
  %phi = phi i16 [ undef, %entry ], [ %a.ext, %if ] 
  %and = and i16 %phi, u0x0000ffff 
  %trunc = trunc i16 %and to i8
  ret i8 %trunc
} 
```
This is indeed an issue with the propagation of the undef flag.

Fix patch:
commit 27392a35ef052aa515f38359de89aced11a4363d
Author: Nikita Popov <npopov@redhat.com>
Date:   Thu May 23 15:14:34 2024 +0200

    [SCCP] Don't allow undef ranges when performing operations (#93163)
    
    When performing some range operation (e.g. and) on a constant range that
    includes undef, we currently just ignore the undef value, which is
    obviously incorrect. Instead, we can do one of two things:
     * Say that the result range also includes undef.
     * Treat undef as a full range.
    
    This patch goes with the second approach -- I'd expect it to be a bit
    better overall, e.g. it allows preserving the fact that a zext of a
    range with undef isn't a full range.
    
    Fixes https://github.com/llvm/llvm-project/issues/93096.

diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
index ce40e8b31b76..4f36bac11e34 100644
--- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp
+++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
@@ -43,7 +43,7 @@ static ValueLatticeElement::MergeOptions getMaxWidenStepsOpts() {
 }
 
 static ConstantRange getConstantRange(const ValueLatticeElement &LV, Type *Ty,
-                                      bool UndefAllowed = true) {
+                                      bool UndefAllowed) {
   assert(Ty->isIntOrIntVectorTy() && "Should be int or int vector");
   if (LV.isConstantRange(UndefAllowed))
     return LV.getConstantRange();
@@ -1297,7 +1297,8 @@ void SCCPInstVisitor::visitCastInst(CastInst &I) {
 
   if (I.getDestTy()->isIntegerTy() && I.getSrcTy()->isIntOrIntVectorTy()) {
     auto &LV = getValueState(&I);
-    ConstantRange OpRange = getConstantRange(OpSt, I.getSrcTy());
+    ConstantRange OpRange =
+        getConstantRange(OpSt, I.getSrcTy(), /*UndefAllowed=*/false);
 
     Type *DestTy = I.getDestTy();
     // Vectors where all elements have the same known constant range are treated
@@ -1329,8 +1330,8 @@ void SCCPInstVisitor::handleExtractOfWithOverflow(ExtractValueInst &EVI,
     return; // Wait to resolve.
 
   Type *Ty = LHS->getType();
-  ConstantRange LR = getConstantRange(L, Ty);
-  ConstantRange RR = getConstantRange(R, Ty);
+  ConstantRange LR = getConstantRange(L, Ty, /*UndefAllowed=*/false);
+  ConstantRange RR = getConstantRange(R, Ty, /*UndefAllowed=*/false);
   if (Idx == 0) {
     ConstantRange Res = LR.binaryOp(WO->getBinaryOp(), RR);
     mergeInValue(&EVI, ValueLatticeElement::getRange(Res));
@@ -1534,8 +1535,10 @@ void SCCPInstVisitor::visitBinaryOperator(Instruction &I) {
     return markOverdefined(&I);
 
   // Try to simplify to a constant range.
-  ConstantRange A = getConstantRange(V1State, I.getType());
-  ConstantRange B = getConstantRange(V2State, I.getType());
+  ConstantRange A =
+      getConstantRange(V1State, I.getType(), /*UndefAllowed=*/false);
+  ConstantRange B =
+      getConstantRange(V2State, I.getType(), /*UndefAllowed=*/false);
 
   auto *BO = cast<BinaryOperator>(&I);
   ConstantRange R = ConstantRange::getEmpty(I.getType()->getScalarSizeInBits());
@@ -1818,7 +1821,8 @@ void SCCPInstVisitor::handleCallResult(CallBase &CB) {
 
         // Combine range info for the original value with the new range from the
         // condition.
-        auto CopyOfCR = getConstantRange(CopyOfVal, CopyOf->getType());
+        auto CopyOfCR = getConstantRange(CopyOfVal, CopyOf->getType(),
+                                         /*UndefAllowed=*/true);
         auto NewCR = ImposedCR.intersectWith(CopyOfCR);
         // If the existing information is != x, do not use the information from
         // a chained predicate, as the != x information is more likely to be
@@ -1863,7 +1867,8 @@ void SCCPInstVisitor::handleCallResult(CallBase &CB) {
         const ValueLatticeElement &State = getValueState(Op);
         if (State.isUnknownOrUndef())
           return;
-        OpRanges.push_back(getConstantRange(State, Op->getType()));
+        OpRanges.push_back(
+            getConstantRange(State, Op->getType(), /*UndefAllowed=*/false));
       }
 
       ConstantRange Result =


Regression tests:
file: llvm/test/Transforms/SCCP/ip-add-range-to-call.ll
commands: opt -passes=ipsccp -S %s
tests: test_name: caller5
test_body: declare hidden i32 @callee5(i32, i32)

define i32 @caller5() {
  %c1 = call i32 @callee5(i32 10, i32 100)
  %c2 = call i32 @callee5(i32 20, i32 200)
  %a = add i32 %c1, %c2
  ret i32 %a
}

file: llvm/test/Transforms/SCCP/range-with-undef.ll
commands: opt -S -passes=ipsccp < %s
tests: test_name: test_with_overflow
test_body: define i9 @test_with_overflow(i1 %cond, i8 %a) {
entry:
  br i1 %cond, label %if, label %join

if:                                               ; preds = %entry
  %a.ext = zext i8 %a to i16
  br label %join

join:                                             ; preds = %if, %entry
  %phi = phi i16 [ undef, %entry ], [ %a.ext, %if ]
  %wo = call { i16, i1 } @llvm.uadd.with.overflow.i16(i16 %phi, i16 1)
  %add = extractvalue { i16, i1 } %wo, 0
  %trunc = trunc i16 %add to i9
  ret i9 %trunc
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare { i16, i1 } @llvm.uadd.with.overflow.i16(i16, i16) #0

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }

test_name: test_binop
test_body: define i8 @test_binop(i1 %cond, i8 %a) {
entry:
  br i1 %cond, label %if, label %join

if:                                               ; preds = %entry
  %a.ext = zext i8 %a to i16
  br label %join

join:                                             ; preds = %if, %entry
  %phi = phi i16 [ undef, %entry ], [ %a.ext, %if ]
  %and = and i16 %phi, -1
  %trunc = trunc i16 %and to i8
  ret i8 %trunc
}

test_name: test_cast
test_body: define i8 @test_cast(i1 %cond, i8 %a) {
entry:
  br i1 %cond, label %if, label %join

if:                                               ; preds = %entry
  %a.ext = zext i8 %a to i16
  br label %join

join:                                             ; preds = %if, %entry
  %phi = phi i16 [ undef, %entry ], [ %a.ext, %if ]
  %zext = zext i16 %phi to i32
  %trunc = trunc i32 %zext to i8
  ret i8 %trunc
}

test_name: test_intrin
test_body: define i8 @test_intrin(i1 %cond, i8 %a) {
entry:
  br i1 %cond, label %if, label %join

if:                                               ; preds = %entry
  %a.ext = zext i8 %a to i16
  br label %join

join:                                             ; preds = %if, %entry
  %phi = phi i16 [ undef, %entry ], [ %a.ext, %if ]
  %umax = call i16 @llvm.umax.i16(i16 %phi, i16 42)
  %trunc = trunc i16 %umax to i8
  ret i8 %trunc
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i16 @llvm.umax.i16(i16, i16) #0

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }