[llvm] 700febc - [InstCombine] Fix incorrect is_zero_poison when folding select+ctlz to cttz (#202388)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 8 13:48:02 PDT 2026


Author: Justin Lebar
Date: 2026-06-08T20:47:58Z
New Revision: 700febc04caa41923eaf4b3911db26cb13482a83

URL: https://github.com/llvm/llvm-project/commit/700febc04caa41923eaf4b3911db26cb13482a83
DIFF: https://github.com/llvm/llvm-project/commit/700febc04caa41923eaf4b3911db26cb13482a83.diff

LOG: [InstCombine] Fix incorrect is_zero_poison when folding select+ctlz to cttz (#202388)

foldSelectCtlzToCttz folds

    %lz = call i32 @llvm.ctlz.i32(i32 (x & -x), i1 is_zero_poison)
    %r  = select (icmp eq x, 0), i32 32, i32 (xor %lz, 31)

into

    %r = call i32 @llvm.cttz.i32(i32 x, i1 is_zero_poison)

The original select's result is defined when x is zero, even if
is_zero_poison is true.  Therefore in the new cttz call, we need to pass
false for the second param, we can't reuse is_zero_poison.

Added: 
    

Modified: 
    llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
    llvm/test/Transforms/InstCombine/select-ctlz-to-cttz.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 730dc53fcc557..df9ba225657d3 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1496,8 +1496,6 @@ static Value *foldAbsDiff(ICmpInst *Cmp, Value *TVal, Value *FVal,
 /// Fold the following code sequence:
 /// \code
 ///   int a = ctlz(x & -x);
-//    x ? 31 - a : a;
-//    // or
 //    x ? 31 - a : 32;
 /// \code
 ///
@@ -1522,7 +1520,7 @@ static Instruction *foldSelectCtlzToCttz(ICmpInst *ICI, Value *TrueVal,
   if (!match(Ctlz, m_Ctlz(m_Value(), m_Value())))
     return nullptr;
 
-  if (TrueVal != Ctlz && !match(TrueVal, m_SpecificInt(BitWidth)))
+  if (!match(TrueVal, m_SpecificInt(BitWidth)))
     return nullptr;
 
   Value *X = ICI->getOperand(0);
@@ -1530,9 +1528,11 @@ static Instruction *foldSelectCtlzToCttz(ICmpInst *ICI, Value *TrueVal,
   if (!match(II->getOperand(0), m_c_And(m_Specific(X), m_Neg(m_Specific(X)))))
     return nullptr;
 
+  // The original select returns the constant bitwidth when x == 0, so the
+  // result is defined there; the cttz must use is_zero_poison = false.
   Function *F = Intrinsic::getOrInsertDeclaration(
       II->getModule(), Intrinsic::cttz, II->getType());
-  return CallInst::Create(F, {X, II->getArgOperand(1)});
+  return CallInst::Create(F, {X, Builder.getFalse()});
 }
 
 /// Attempt to fold a cttz/ctlz followed by a icmp plus select into a single

diff  --git a/llvm/test/Transforms/InstCombine/select-ctlz-to-cttz.ll b/llvm/test/Transforms/InstCombine/select-ctlz-to-cttz.ll
index fa689108ce187..d1f25b19d35a0 100644
--- a/llvm/test/Transforms/InstCombine/select-ctlz-to-cttz.ll
+++ b/llvm/test/Transforms/InstCombine/select-ctlz-to-cttz.ll
@@ -56,6 +56,29 @@ define i32 @select_clz_to_ctz_constant_for_zero(i32 %a) {
   ret i32 %cond
 }
 
+; The zero case selects the constant 32 (defined), but the ctlz has
+; is_zero_poison=true. The result cttz must use is_zero_poison=false, otherwise
+; the a==0 case becomes poison instead of 32. The extra use of the ctlz blocks
+; the standalone xor->cttz rewrite so the select reaches foldSelectCtlzToCttz.
+define i32 @select_clz_to_ctz_constant_for_zero_poison_flag(i32 %a) {
+; CHECK-LABEL: @select_clz_to_ctz_constant_for_zero_poison_flag(
+; CHECK-NEXT:    [[SUB:%.*]] = sub i32 0, [[A:%.*]]
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[A]], [[SUB]]
+; CHECK-NEXT:    [[LZ:%.*]] = tail call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 [[AND]], i1 true)
+; CHECK-NEXT:    call void @use(i32 [[LZ]])
+; CHECK-NEXT:    [[COND:%.*]] = call range(i32 0, 33) i32 @llvm.cttz.i32(i32 [[A]], i1 false)
+; CHECK-NEXT:    ret i32 [[COND]]
+;
+  %sub = sub i32 0, %a
+  %and = and i32 %sub, %a
+  %lz = tail call i32 @llvm.ctlz.i32(i32 %and, i1 true)
+  call void @use(i32 %lz)
+  %tobool = icmp eq i32 %a, 0
+  %sub1 = xor i32 %lz, 31
+  %cond = select i1 %tobool, i32 32, i32 %sub1
+  ret i32 %cond
+}
+
 define <2 x i32> @select_clz_to_ctz_vec(<2 x i32> %a) {
 ; CHECK-LABEL: @select_clz_to_ctz_vec(
 ; CHECK-NEXT:    [[COND:%.*]] = call range(i32 0, 33) <2 x i32> @llvm.cttz.v2i32(<2 x i32> [[A:%.*]], i1 true)


        


More information about the llvm-commits mailing list