Bug type: miscompilation
Component: InstCombine
Bug location: llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp: foldSelectCttzCtlz
Issue title: [InstCombine] `noundef` attribute should be dropped
Labels: miscompilation
llvm:instcombine
Reported by: dtcxzyw

Alive2: https://alive2.llvm.org/ce/z/wKsUWK
```
define i32 @src(i32 noundef %x) {
  %ct = call noundef i32 @llvm.cttz.i32(i32 %x, i1 false)
  %cmp = icmp ne i32 %x, 0
  %res = select i1 %cmp, i32 %ct, i32 123
  ret i32 %res
}
define i32 @tgt(i32 noundef %x) {
  %ct = call noundef range(i32 0, 33) i32 @llvm.cttz.i32(i32 %x, i1 true)
  %cmp.not = icmp eq i32 %x, 0
  %res = select i1 %cmp.not, i32 123, i32 %ct
  ret i32 %res
}
```



Fix patch:
commit a37dbc1f51c70d92fd209c2e52a9d794eb15e4e7
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Mon Jan 6 00:04:28 2025 +0800

    [InstCombine] Drop noundef in `foldSelectCttzCtlz` (#121692)
    
    Close https://github.com/llvm/llvm-project/issues/121428

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index e7a8e947705f..a18b927678ef 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1225,8 +1225,12 @@ static Value *foldSelectCttzCtlz(ICmpInst *ICI, Value *TrueVal, Value *FalseVal,
   // zext/trunc) have one use (ending at the select), the cttz/ctlz result will
   // not be used if the input is zero. Relax to 'zero is poison' for that case.
   if (II->hasOneUse() && SelectArg->hasOneUse() &&
-      !match(II->getArgOperand(1), m_One()))
+      !match(II->getArgOperand(1), m_One())) {
     II->setArgOperand(1, ConstantInt::getTrue(II->getContext()));
+    // noundef attribute on the intrinsic may no longer be valid.
+    II->dropUBImplyingAttrsAndMetadata();
+    IC.addToWorklist(II);
+  }
 
   return nullptr;
 }


Regression tests:
file: llvm/test/Transforms/InstCombine/select-cmp-cttz-ctlz.ll
commands: opt -passes=instcombine -S < %s
tests: test_name: test_cttz_not_bw_multiuse
test_body: define i32 @test_cttz_not_bw_multiuse(i32 %x) {
  %ct = tail call i32 @llvm.cttz.i32(i32 %x, i1 false)
  %cmp = icmp ne i32 %x, 0
  %sel = select i1 %cmp, i32 %ct, i32 123
  %res = or i32 %sel, %ct
  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: test_cttz_not_bw
test_body: define i32 @test_cttz_not_bw(i32 %x) {
  %ct = tail call i32 @llvm.cttz.i32(i32 %x, i1 false)
  %cmp = icmp ne i32 %x, 0
  %res = select i1 %cmp, i32 %ct, i32 123
  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: test_cttz_not_bw_noundef
test_body: define i32 @test_cttz_not_bw_noundef(i32 %x) {
  %ct = tail call noundef i32 @llvm.cttz.i32(i32 %x, i1 false)
  %cmp = icmp ne i32 %x, 0
  %res = select i1 %cmp, i32 %ct, i32 123
  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) }