Bug type: miscompilation
Component: IR
LazyValueInfo
ValueTracking
Bug location: llvm/include/llvm/Analysis/ValueTracking.h: isSafeToSpeculativelyExecuteWithVariableReplaced
llvm/lib/Analysis/LazyValueInfo.cpp: LazyValueInfoImpl::getValueAtUse
llvm/lib/Analysis/ValueTracking.cpp: llvm::isNotCrossLaneOperation
llvm::isSafeToSpeculativelyExecuteWithOpcode
llvm/lib/IR/Instruction.cpp: Instruction::dropUBImplyingAttrsAndMetadata
Instruction::dropUBImplyingAttrsAndUnknownMetadata
Instruction::isExact
Issue title: [CVP] Miscompilation at -O3
Labels: miscompilation
llvm:analysis
generated by fuzzer
Reported by: dtcxzyw

Reproducer: https://alive2.llvm.org/ce/z/x4KrLX
```

----------------------------------------
define i8 @func_124(i16 %.promoted) {
entry:
  %inc20.i = or i16 %.promoted, 1
  %conv.i.i = trunc i16 %inc20.i to i8
  %narrow.i.i.i = smin i8 %conv.i.i, 0
  assume_welldefined i8 %narrow.i.i.i
  %tobool.not.i46 = icmp eq i16 %.promoted, 0
  br i1 %tobool.not.i46, label %func_127.exit, label %land.rhs.i47

land.rhs.i47:
  br label %func_127.exit

func_127.exit:
  %conv5.i.i.pn.in = phi i8 [ %narrow.i.i.i, %entry ], [ 0, %land.rhs.i47 ]
  ret i8 %conv5.i.i.pn.in
}
=>
define i8 @func_124(i16 %.promoted) {
entry:
  %inc20.i = or i16 %.promoted, 1
  %conv.i.i = trunc nsw nuw i16 %inc20.i to i8
  %narrow.i.i.i = smin i8 %conv.i.i, 0
  assume_welldefined i8 %narrow.i.i.i
  %tobool.not.i46 = icmp eq i16 %.promoted, 0
  br i1 %tobool.not.i46, label %func_127.exit, label %land.rhs.i47

land.rhs.i47:
  br label %func_127.exit

func_127.exit:
  %conv5.i.i.pn.in = phi i8 [ %narrow.i.i.i, %entry ], [ 0, %land.rhs.i47 ]
  %#range_0_%conv5.i.i.pn.in = !range i8 %conv5.i.i.pn.in, i8 128, i8 1
  ret i8 %#range_0_%conv5.i.i.pn.in
}
Transformation doesn't verify!

ERROR: Source is more defined than target

Example:
i16 %.promoted = #x0080 (128)

Source:
i16 %inc20.i = #x0081 (129)
i8 %conv.i.i = #x81 (129, -127)
i8 %narrow.i.i.i = #x81 (129, -127)
i1 %tobool.not.i46 = #x0 (0)
  >> Jump to %land.rhs.i47
  >> Jump to %func_127.exit
i8 %conv5.i.i.pn.in = #x00 (0)

Target:
i16 %inc20.i = #x0081 (129)
i8 %conv.i.i = poison
i8 %narrow.i.i.i = poison
void = UB triggered!


Summary:
  0 correct transformations
  1 incorrect transformations
  0 failed-to-prove transformations
  0 Alive2 errors
```


Comments: author: dtcxzyw
body: Related code: https://github.com/llvm/llvm-project/blob/60b3a5b7e7db40ec74d3c4839a3fae8b73d98851/llvm/lib/Analysis/LazyValueInfo.cpp#L1694-L1705

Fix patch:
commit 830cf36bd4c491ef28d9bba74737e324639bb4cd
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Wed Apr 30 11:53:38 2025 +0800

    [LVI][ValueTracking] Take UB-implying attributes into account in `isSafeToSpeculativelyExecute` (#137604)
    
    Closes https://github.com/llvm/llvm-project/issues/137582.
    
    In the original case, LVI uses the edge information in `%entry ->
    %if.end` to get a more precise result. However, since the call to `smin`
    has an `noundef` return attribute, an immediate UB will be triggered
    after optimization.
    
    Currently, `isSafeToSpeculativelyExecuteWithOpcode(%min)` returns true
    because
    https://github.com/llvm/llvm-project/commit/6a288c1e32351d4be3b7630841af078fa1c3bb8b
    only checks whether the function is speculatable. However, it is not
    enough in this case.
    
    This patch takes UB-implying attributes into account if
    `IgnoreUBImplyingAttrs` is set to false. If it is set to true, the
    caller is responsible for correctly propagating UB-implying attributes.

diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index f927838c843a..61dbb07e7128 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -539,6 +539,12 @@ bool isNotCrossLaneOperation(const Instruction *I);
 /// move the instruction as long as the correct dominance relationships for
 /// the operands and users hold.
 ///
+/// If \p UseVariableInfo is true, the information from non-constant operands
+/// will be taken into account.
+///
+/// If \p IgnoreUBImplyingAttrs is true, UB-implying attributes will be ignored.
+/// The caller is responsible for correctly propagating them after hoisting.
+///
 /// This method can return true for instructions that read memory;
 /// for such instructions, moving them may change the resulting value.
 bool isSafeToSpeculativelyExecute(const Instruction *I,
@@ -546,24 +552,28 @@ bool isSafeToSpeculativelyExecute(const Instruction *I,
                                   AssumptionCache *AC = nullptr,
                                   const DominatorTree *DT = nullptr,
                                   const TargetLibraryInfo *TLI = nullptr,
-                                  bool UseVariableInfo = true);
+                                  bool UseVariableInfo = true,
+                                  bool IgnoreUBImplyingAttrs = true);
 
 inline bool isSafeToSpeculativelyExecute(const Instruction *I,
                                          BasicBlock::iterator CtxI,
                                          AssumptionCache *AC = nullptr,
                                          const DominatorTree *DT = nullptr,
                                          const TargetLibraryInfo *TLI = nullptr,
-                                         bool UseVariableInfo = true) {
+                                         bool UseVariableInfo = true,
+                                         bool IgnoreUBImplyingAttrs = true) {
   // Take an iterator, and unwrap it into an Instruction *.
-  return isSafeToSpeculativelyExecute(I, &*CtxI, AC, DT, TLI, UseVariableInfo);
+  return isSafeToSpeculativelyExecute(I, &*CtxI, AC, DT, TLI, UseVariableInfo,
+                                      IgnoreUBImplyingAttrs);
 }
 
 /// Don't use information from its non-constant operands. This helper is used
 /// when its operands are going to be replaced.
-inline bool
-isSafeToSpeculativelyExecuteWithVariableReplaced(const Instruction *I) {
+inline bool isSafeToSpeculativelyExecuteWithVariableReplaced(
+    const Instruction *I, bool IgnoreUBImplyingAttrs = true) {
   return isSafeToSpeculativelyExecute(I, nullptr, nullptr, nullptr, nullptr,
-                                      /*UseVariableInfo=*/false);
+                                      /*UseVariableInfo=*/false,
+                                      IgnoreUBImplyingAttrs);
 }
 
 /// This returns the same result as isSafeToSpeculativelyExecute if Opcode is
@@ -586,7 +596,8 @@ isSafeToSpeculativelyExecuteWithVariableReplaced(const Instruction *I) {
 bool isSafeToSpeculativelyExecuteWithOpcode(
     unsigned Opcode, const Instruction *Inst, const Instruction *CtxI = nullptr,
     AssumptionCache *AC = nullptr, const DominatorTree *DT = nullptr,
-    const TargetLibraryInfo *TLI = nullptr, bool UseVariableInfo = true);
+    const TargetLibraryInfo *TLI = nullptr, bool UseVariableInfo = true,
+    bool IgnoreUBImplyingAttrs = true);
 
 /// Returns true if the result or effects of the given instructions \p I
 /// depend values not reachable through the def use graph.
diff --git a/llvm/include/llvm/IR/Instruction.h b/llvm/include/llvm/IR/Instruction.h
index 900384432d75..d8069b2fb02a 100644
--- a/llvm/include/llvm/IR/Instruction.h
+++ b/llvm/include/llvm/IR/Instruction.h
@@ -585,6 +585,10 @@ public:
   /// This should be used when speculating instructions.
   void dropUBImplyingAttrsAndMetadata();
 
+  /// Return true if this instruction has UB-implying attributes
+  /// that can cause immediate undefined behavior.
+  bool hasUBImplyingAttrs() const LLVM_READONLY;
+
   /// Determine whether the exact flag is set.
   bool isExact() const LLVM_READONLY;
 
diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp
index e49e004a48a5..2a562484fc37 100644
--- a/llvm/lib/Analysis/LazyValueInfo.cpp
+++ b/llvm/lib/Analysis/LazyValueInfo.cpp
@@ -1701,7 +1701,8 @@ ValueLatticeElement LazyValueInfoImpl::getValueAtUse(const Use &U) {
     // of a cycle, we might end up reasoning about values from different cycle
     // iterations (PR60629).
     if (!CurrI->hasOneUse() ||
-        !isSafeToSpeculativelyExecuteWithVariableReplaced(CurrI))
+        !isSafeToSpeculativelyExecuteWithVariableReplaced(
+            CurrI, /*IgnoreUBImplyingAttrs=*/false))
       break;
     CurrU = &*CurrI->use_begin();
   }
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 1d3f8b7207a6..256e77b40a97 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -7201,20 +7201,19 @@ bool llvm::isNotCrossLaneOperation(const Instruction *I) {
          !isa<CallBase, BitCastInst, ExtractElementInst>(I);
 }
 
-bool llvm::isSafeToSpeculativelyExecute(const Instruction *Inst,
-                                        const Instruction *CtxI,
-                                        AssumptionCache *AC,
-                                        const DominatorTree *DT,
-                                        const TargetLibraryInfo *TLI,
-                                        bool UseVariableInfo) {
+bool llvm::isSafeToSpeculativelyExecute(
+    const Instruction *Inst, const Instruction *CtxI, AssumptionCache *AC,
+    const DominatorTree *DT, const TargetLibraryInfo *TLI, bool UseVariableInfo,
+    bool IgnoreUBImplyingAttrs) {
   return isSafeToSpeculativelyExecuteWithOpcode(Inst->getOpcode(), Inst, CtxI,
-                                                AC, DT, TLI, UseVariableInfo);
+                                                AC, DT, TLI, UseVariableInfo,
+                                                IgnoreUBImplyingAttrs);
 }
 
 bool llvm::isSafeToSpeculativelyExecuteWithOpcode(
     unsigned Opcode, const Instruction *Inst, const Instruction *CtxI,
     AssumptionCache *AC, const DominatorTree *DT, const TargetLibraryInfo *TLI,
-    bool UseVariableInfo) {
+    bool UseVariableInfo, bool IgnoreUBImplyingAttrs) {
 #ifndef NDEBUG
   if (Inst->getOpcode() != Opcode) {
     // Check that the operands are actually compatible with the Opcode override.
@@ -7287,7 +7286,11 @@ bool llvm::isSafeToSpeculativelyExecuteWithOpcode(
 
     // The called function could have undefined behavior or side-effects, even
     // if marked readnone nounwind.
-    return Callee && Callee->isSpeculatable();
+    if (!Callee || !Callee->isSpeculatable())
+      return false;
+    // Since the operands may be changed after hoisting, undefined behavior may
+    // be triggered by some UB-implying attributes.
+    return IgnoreUBImplyingAttrs || !CI->hasUBImplyingAttrs();
   }
   case Instruction::VAArg:
   case Instruction::Alloca:
diff --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp
index e55a4b41e4d0..6f858110fb8c 100644
--- a/llvm/lib/IR/Instruction.cpp
+++ b/llvm/lib/IR/Instruction.cpp
@@ -532,8 +532,8 @@ void Instruction::dropUBImplyingAttrsAndUnknownMetadata(
   if (!CB)
     return;
   // For call instructions, we also need to drop parameter and return attributes
-  // that are can cause UB if the call is moved to a location where the
-  // attribute is not valid.
+  // that can cause UB if the call is moved to a location where the attribute is
+  // not valid.
   AttributeList AL = CB->getAttributes();
   if (AL.isEmpty())
     return;
@@ -554,6 +554,20 @@ void Instruction::dropUBImplyingAttrsAndMetadata() {
   dropUBImplyingAttrsAndUnknownMetadata(KnownIDs);
 }
 
+bool Instruction::hasUBImplyingAttrs() const {
+  auto *CB = dyn_cast<CallBase>(this);
+  if (!CB)
+    return false;
+  // For call instructions, we also need to check parameter and return
+  // attributes that can cause UB.
+  for (unsigned ArgNo = 0; ArgNo < CB->arg_size(); ArgNo++)
+    if (CB->isPassingUndefUB(ArgNo))
+      return true;
+  return CB->hasRetAttr(Attribute::NoUndef) ||
+         CB->hasRetAttr(Attribute::Dereferenceable) ||
+         CB->hasRetAttr(Attribute::DereferenceableOrNull);
+}
+
 bool Instruction::isExact() const {
   return cast<PossiblyExactOperator>(this)->isExact();
 }


Regression tests:
file: llvm/test/Transforms/CorrelatedValuePropagation/pr137582.ll
commands: opt < %s -passes=correlated-propagation -S
tests: test_name: test
test_body: define i8 @test(i16 %x) {
entry:
  %or = or i16 %x, 1
  %conv = trunc i16 %or to i8
  %min = call noundef i8 @llvm.smin.i8(i8 %conv, i8 0)
  %cond = icmp eq i16 %x, 0
  br i1 %cond, label %if.end, label %if.then

if.then:                                          ; preds = %entry
  br label %if.end

if.end:                                           ; preds = %if.then, %entry
  %res = phi i8 [ %min, %entry ], [ 0, %if.then ]
  ret i8 %res
}

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

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