Bug type: miscompilation
Component: InstCombine
Bug location: llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp: foldCttzCtlz
Issue title: [InstCombine] `noundef` attribute should be dropped after setting `is_zero_poison` to true
Labels: miscompilation
llvm:instcombine
Reported by: dtcxzyw

Reproducer: https://alive2.llvm.org/ce/z/7-TgAf
```
define i32 @src(i32 %x, i32 %y) {
entry:
  %cttz = call noundef i32 @llvm.cttz.i32(i32 %y, i1 false)
  %res = lshr i32 %x, %cttz
  ret i32 %res
}

define i32 @tgt(i32 %x, i32 %y) {
entry:
  %cttz = call noundef i32 @llvm.cttz.i32(i32 %y, i1 true)
  %res = lshr i32 %x, %cttz
  ret i32 %res
}

define i32 @src2(i32 %x) {
  %ct = call noundef i32 @llvm.ctlz.i32(i32 %x, i1 false)
  %cmp = icmp ne i32 %x, 0
  %res = select i1 %cmp, i32 %ct, i32 123
  ret i32 %res
}

define i32 @tgt2(i32 %x) {
  %ct = call noundef i32 @llvm.ctlz.i32(i32 %x, i1 true)
  %cmp = icmp ne i32 %x, 0
  %res = select i1 %cmp, i32 %ct, i32 123
  ret i32 %res
}
```
`noundef` attribute should be dropped. Otherwise we will convert a poison value to immediate UB.



Fix patch:
commit a59976bea8ad76f18119a11391dc8ba3e6ba07d5
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Tue Nov 19 20:06:34 2024 +0800

    [InstCombine] Drop noundef attributes in `foldCttzCtlz` (#116718)
    
    Closes https://github.com/llvm/llvm-project/issues/112068.

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 6cff3c7af91e..42c0acd1e45e 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -505,8 +505,10 @@ static Instruction *foldCttzCtlz(IntrinsicInst &II, InstCombinerImpl &IC) {
 
   // If ctlz/cttz is only used as a shift amount, set is_zero_poison to true.
   if (II.hasOneUse() && match(Op1, m_Zero()) &&
-      match(II.user_back(), m_Shift(m_Value(), m_Specific(&II))))
+      match(II.user_back(), m_Shift(m_Value(), m_Specific(&II)))) {
+    II.dropUBImplyingAttrsAndMetadata();
     return IC.replaceOperand(II, 1, IC.Builder.getTrue());
+  }
 
   Constant *C;
 


Regression tests:
file: llvm/test/Transforms/InstCombine/shift-cttz-ctlz.ll
commands: opt < %s -passes=instcombine -S
tests: test_name: shl_cttz_false_multiuse
test_body: define i32 @shl_cttz_false_multiuse(i32 %x, i32 %y) {
entry:
  %cttz = call i32 @llvm.cttz.i32(i32 %y, i1 false)
  call void @use(i32 %cttz)
  %res = shl i32 %x, %cttz
  ret i32 %res
}

declare void @use(i32)

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.cttz.i32(i32, i1 immarg) #0

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

test_name: shl_cttz_false_noundef
test_body: define i32 @shl_cttz_false_noundef(i32 %x, i32 %y) {
entry:
  %cttz = call noundef i32 @llvm.cttz.i32(i32 %y, i1 false)
  %res = shl i32 %x, %cttz
  ret i32 %res
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.cttz.i32(i32, i1 immarg) #0

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

test_name: ashr_cttz_false
test_body: define i32 @ashr_cttz_false(i32 %x, i32 %y) {
entry:
  %cttz = call i32 @llvm.cttz.i32(i32 %y, i1 false)
  %res = ashr i32 %x, %cttz
  ret i32 %res
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.cttz.i32(i32, i1 immarg) #0

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

test_name: shl_cttz_false
test_body: define i32 @shl_cttz_false(i32 %x, i32 %y) {
entry:
  %cttz = call i32 @llvm.cttz.i32(i32 %y, i1 false)
  %res = shl i32 %x, %cttz
  ret i32 %res
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.cttz.i32(i32, i1 immarg) #0

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

test_name: shl_cttz_as_lhs
test_body: define i32 @shl_cttz_as_lhs(i32 %x, i32 %y) {
entry:
  %cttz = call i32 @llvm.cttz.i32(i32 %y, i1 false)
  %res = shl i32 %cttz, %x
  ret i32 %res
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.cttz.i32(i32, i1 immarg) #0

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

test_name: shl_ctlz_false
test_body: define i32 @shl_ctlz_false(i32 %x, i32 %y) {
entry:
  %cttz = call i32 @llvm.ctlz.i32(i32 %y, i1 false)
  %res = shl i32 %x, %cttz
  ret i32 %res
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.ctlz.i32(i32, i1 immarg) #0

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

test_name: lshr_cttz_false
test_body: define i32 @lshr_cttz_false(i32 %x, i32 %y) {
entry:
  %cttz = call i32 @llvm.cttz.i32(i32 %y, i1 false)
  %res = lshr i32 %x, %cttz
  ret i32 %res
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.cttz.i32(i32, i1 immarg) #0

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