[llvm] [Instcombine] Fold a zero-select hexadecimal digit count into ctlz(x | 1) (PR #215073)

Jeong Jihyeon via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 18 21:22:10 PDT 2026


https://github.com/JihyeonJeong129 updated https://github.com/llvm/llvm-project/pull/215073

>From 33e94428f727c3b978e0dde1cc284c15857afd02 Mon Sep 17 00:00:00 2001
From: Jihyeon Jeong <jh.jeong129 at gmail.com>
Date: Wed, 19 Aug 2026 04:18:10 +0000
Subject: [PATCH 1/2] [InstCombine] Add pre-commit tests for guarded ctlz fold

---
 .../InstCombine/select-cmp-cttz-ctlz.ll       | 139 +++++++++++++++++-
 1 file changed, 137 insertions(+), 2 deletions(-)

diff --git a/llvm/test/Transforms/InstCombine/select-cmp-cttz-ctlz.ll b/llvm/test/Transforms/InstCombine/select-cmp-cttz-ctlz.ll
index 5d76cfb73f2a4..94e6c70cb7ebe 100644
--- a/llvm/test/Transforms/InstCombine/select-cmp-cttz-ctlz.ll
+++ b/llvm/test/Transforms/InstCombine/select-cmp-cttz-ctlz.ll
@@ -744,8 +744,8 @@ define i32 @test_ctlz_sub_zero_poison(i32 %arg) {
   ret i32 %sel
 }
 
-define i32 @test_ctlz_sub_wrong_const(i32 %arg) {
-; CHECK-LABEL: @test_ctlz_sub_wrong_const(
+define i32 @fold_ctlz_sub_one(i32 %arg) {
+; CHECK-LABEL: @fold_ctlz_sub_one(
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[ARG:%.*]], 0
 ; CHECK-NEXT:    [[CTZ:%.*]] = call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 [[ARG]], i1 true)
 ; CHECK-NEXT:    [[SUB:%.*]] = sub nuw nsw i32 32, [[CTZ]]
@@ -1009,6 +1009,141 @@ define <2 x i32> @test_cttz_not_bw_odd_mul_vec_nonsplat(<2 x i32> %x) {
   ret <2 x i32> %res
 }
 
+define i64 @fold_ctlz_hex_digits(i64 noundef %x) {
+; CHECK-LABEL: @fold_ctlz_hex_digits(
+; CHECK-NEXT:    [[ISZERO:%.*]] = icmp eq i64 [[X:%.*]], 0
+; CHECK-NEXT:    [[LZ:%.*]] = call range(i64 0, 65) i64 @llvm.ctlz.i64(i64 [[X]], i1 true)
+; CHECK-NEXT:    [[BIAS:%.*]] = sub nuw nsw i64 67, [[LZ]]
+; CHECK-NEXT:    [[DIGITS:%.*]] = lshr i64 [[BIAS]], 2
+; CHECK-NEXT:    [[RESULT:%.*]] = select i1 [[ISZERO]], i64 1, i64 [[DIGITS]]
+; CHECK-NEXT:    ret i64 [[RESULT]]
+;
+  %iszero = icmp eq i64 %x, 0
+  %lz = call i64 @llvm.ctlz.i64(i64 %x, i1 true)
+  %bias = sub nuw nsw i64 67, %lz
+  %digits = lshr i64 %bias, 2
+  %result = select i1 %iszero, i64 1, i64 %digits
+  ret i64 %result
+}
+
+define i64 @fold_ctlz_hex_digits_ne_commuted(i64 noundef %x) {
+; CHECK-LABEL: @fold_ctlz_hex_digits_ne_commuted(
+; CHECK-NEXT:    [[ISNOTZERO_NOT:%.*]] = icmp eq i64 [[X:%.*]], 0
+; CHECK-NEXT:    [[LZ:%.*]] = call range(i64 0, 65) i64 @llvm.ctlz.i64(i64 [[X]], i1 true)
+; CHECK-NEXT:    [[BIAS:%.*]] = sub nuw nsw i64 67, [[LZ]]
+; CHECK-NEXT:    [[DIGITS:%.*]] = lshr i64 [[BIAS]], 2
+; CHECK-NEXT:    [[RESULT:%.*]] = select i1 [[ISNOTZERO_NOT]], i64 1, i64 [[DIGITS]]
+; CHECK-NEXT:    ret i64 [[RESULT]]
+;
+  %isnotzero = icmp ne i64 0, %x
+  %lz = call i64 @llvm.ctlz.i64(i64 %x, i1 true)
+  %bias = sub nuw nsw i64 67, %lz
+  %digits = lshr i64 %bias, 2
+  %result = select i1 %isnotzero, i64 %digits, i64 1
+  ret i64 %result
+}
+
+define i64 @fold_ctlz_hex_digits_exact(i64 noundef %x) {
+; CHECK-LABEL: @fold_ctlz_hex_digits_exact(
+; CHECK-NEXT:    [[ISZERO:%.*]] = icmp eq i64 [[X:%.*]], 0
+; CHECK-NEXT:    [[LZ:%.*]] = call range(i64 0, 65) i64 @llvm.ctlz.i64(i64 [[X]], i1 true)
+; CHECK-NEXT:    [[BIAS:%.*]] = sub nuw nsw i64 67, [[LZ]]
+; CHECK-NEXT:    [[DIGITS1:%.*]] = lshr exact i64 [[BIAS]], 2
+; CHECK-NEXT:    [[DIGITS:%.*]] = select i1 [[ISZERO]], i64 1, i64 [[DIGITS1]]
+; CHECK-NEXT:    ret i64 [[DIGITS]]
+;
+  %iszero = icmp eq i64 %x, 0
+  %lz = call i64 @llvm.ctlz.i64(i64 %x, i1 true)
+  %bias = sub nuw nsw i64 67, %lz
+  %digits = lshr exact i64 %bias, 2
+  %result = select i1 %iszero, i64 1, i64 %digits
+  ret i64 %result
+}
+
+; The transform requires is_zero_poison=true.
+define i64 @do_not_fold_ctlz_hex_digits_defined_zero(i64 noundef %x) {
+; CHECK-LABEL: @do_not_fold_ctlz_hex_digits_defined_zero(
+; CHECK-NEXT:    [[ISZERO:%.*]] = icmp eq i64 [[X:%.*]], 0
+; CHECK-NEXT:    [[LZ:%.*]] = call range(i64 0, 65) i64 @llvm.ctlz.i64(i64 [[X]], i1 false)
+; CHECK-NEXT:    [[BIAS:%.*]] = sub nuw nsw i64 67, [[LZ]]
+; CHECK-NEXT:    [[DIGITS:%.*]] = lshr i64 [[BIAS]], 2
+; CHECK-NEXT:    [[RESULT:%.*]] = select i1 [[ISZERO]], i64 1, i64 [[DIGITS]]
+; CHECK-NEXT:    ret i64 [[RESULT]]
+;
+  %iszero = icmp eq i64 %x, 0
+  %lz = call i64 @llvm.ctlz.i64(i64 %x, i1 false)
+  %bias = sub nuw nsw i64 67, %lz
+  %digits = lshr i64 %bias, 2
+  %result = select i1 %iszero, i64 1, i64 %digits
+  ret i64 %result
+}
+
+; The expression evaluated at X == 0 does not match the selected zero value.
+define i64 @do_not_fold_ctlz_hex_digits_wrong_zero_value(i64 noundef %x) {
+; CHECK-LABEL: @do_not_fold_ctlz_hex_digits_wrong_zero_value(
+; CHECK-NEXT:    [[ISZERO:%.*]] = icmp eq i64 [[X:%.*]], 0
+; CHECK-NEXT:    [[LZ:%.*]] = call range(i64 0, 65) i64 @llvm.ctlz.i64(i64 [[X]], i1 true)
+; CHECK-NEXT:    [[BIAS:%.*]] = sub nuw nsw i64 67, [[LZ]]
+; CHECK-NEXT:    [[DIGITS:%.*]] = lshr i64 [[BIAS]], 2
+; CHECK-NEXT:    [[RESULT:%.*]] = select i1 [[ISZERO]], i64 2, i64 [[DIGITS]]
+; CHECK-NEXT:    ret i64 [[RESULT]]
+;
+  %iszero = icmp eq i64 %x, 0
+  %lz = call i64 @llvm.ctlz.i64(i64 %x, i1 true)
+  %bias = sub nuw nsw i64 67, %lz
+  %digits = lshr i64 %bias, 2
+  %result = select i1 %iszero, i64 2, i64 %digits
+  ret i64 %result
+}
+
+; The generic equivalence fold drops nuw because it would be poison at X == 0.
+define i16 @fold_ctlz_drop_nuw_on_zero(i16 noundef %x) {
+; CHECK-LABEL: @fold_ctlz_drop_nuw_on_zero(
+; CHECK-NEXT:    [[LZ:%.*]] = call range(i16 0, 17) i16 @llvm.ctlz.i16(i16 [[X:%.*]], i1 false)
+; CHECK-NEXT:    [[BIAS:%.*]] = sub nsw i16 10, [[LZ]]
+; CHECK-NEXT:    [[DIGITS:%.*]] = lshr i16 [[BIAS]], 1
+; CHECK-NEXT:    ret i16 [[DIGITS]]
+;
+  %iszero = icmp eq i16 %x, 0
+  %lz = call i16 @llvm.ctlz.i16(i16 %x, i1 true)
+  %bias = sub nuw i16 10, %lz
+  %digits = lshr i16 %bias, 1
+  %result = select i1 %iszero, i16 32765, i16 %digits
+  ret i16 %result
+}
+
+; The generic equivalence fold drops nsw because it would be poison at X == 0.
+define i16 @fold_ctlz_drop_nsw_on_zero(i16 noundef %x) {
+; CHECK-LABEL: @fold_ctlz_drop_nsw_on_zero(
+; CHECK-NEXT:    [[LZ:%.*]] = call range(i16 0, 17) i16 @llvm.ctlz.i16(i16 [[X:%.*]], i1 false)
+; CHECK-NEXT:    [[BIAS:%.*]] = sub nuw i16 -32760, [[LZ]]
+; CHECK-NEXT:    [[DIGITS:%.*]] = lshr i16 [[BIAS]], 1
+; CHECK-NEXT:    ret i16 [[DIGITS]]
+;
+  %iszero = icmp eq i16 %x, 0
+  %lz = call i16 @llvm.ctlz.i16(i16 %x, i1 true)
+  %bias = sub nsw i16 -32760, %lz
+  %digits = lshr i16 %bias, 1
+  %result = select i1 %iszero, i16 16380, i16 %digits
+  ret i16 %result
+}
+
+; The generic equivalence fold drops exact because it would be poison at X == 0.
+define i16 @fold_ctlz_drop_exact_on_zero(i16 noundef %x) {
+; CHECK-LABEL: @fold_ctlz_drop_exact_on_zero(
+; CHECK-NEXT:    [[LZ:%.*]] = call range(i16 0, 17) i16 @llvm.ctlz.i16(i16 [[X:%.*]], i1 false)
+; CHECK-NEXT:    [[BIAS:%.*]] = sub nuw nsw i16 20, [[LZ]]
+; CHECK-NEXT:    [[DIGITS:%.*]] = lshr i16 [[BIAS]], 2
+; CHECK-NEXT:    ret i16 [[DIGITS]]
+;
+  %iszero = icmp eq i16 %x, 0
+  %lz = call i16 @llvm.ctlz.i16(i16 %x, i1 true)
+  %bias = sub i16 20, %lz
+  %digits = lshr exact i16 %bias, 2
+  %result = select i1 %iszero, i16 1, i16 %digits
+  ret i16 %result
+}
+
 declare i16 @llvm.ctlz.i16(i16, i1)
 declare i32 @llvm.ctlz.i32(i32, i1)
 declare i64 @llvm.ctlz.i64(i64, i1)

>From 826eeeb695d16cca0cf29998ca1a9c6a6308bcfb Mon Sep 17 00:00:00 2001
From: Jihyeon Jeong <jh.jeong129 at gmail.com>
Date: Wed, 19 Aug 2026 04:19:12 +0000
Subject: [PATCH 2/2] [InstCombine] Fold select guarding zero-poison ctlz

---
 .../InstCombine/InstCombineSelect.cpp         | 43 +++++++++++++++++++
 .../InstCombine/select-cmp-cttz-ctlz.ll       | 37 ++++++++--------
 2 files changed, 61 insertions(+), 19 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 39f0d11df4db9..b8f7d3f10da66 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1767,6 +1767,49 @@ Instruction *InstCombinerImpl::foldSelectValueEquivalence(SelectInst &Sel,
   // We have an 'EQ' comparison, so the select's false value will propagate.
   // Example:
   // (X == 42) ? 43 : (X + 1) --> (X == 42) ? (X + 1) : (X + 1) --> X + 1
+
+  // ctlz(X, true) is poison for X == 0, but ctlz(X | 1, true) is defined and
+  // has the same value for every non-zero X. See whether replacing the ctlz
+  // with ctlz(1, true) makes the false arm equivalent to the zero arm, and let
+  // the generic simplifier handle the surrounding expression and poison flags.
+  auto FoldZeroPoisonCtlz = [&](Value *X, Value *Zero) -> Instruction * {
+    if (!X->getType()->isIntegerTy() || !match(Zero, m_Zero()))
+      return nullptr;
+
+    for (User *U : X->users()) {
+      auto *II = dyn_cast<IntrinsicInst>(U);
+      if (!II || II->getIntrinsicID() != Intrinsic::ctlz ||
+          II->getArgOperand(0) != X || !match(II->getArgOperand(1), m_One()))
+        continue;
+
+      Constant *CtlzOfOne = ConstantInt::get(
+          X->getType(), X->getType()->getIntegerBitWidth() - 1);
+      SmallVector<Instruction *> CtlzDropFlags;
+      if (simplifyWithOpReplaced(FalseVal, II, CtlzOfOne, SQ,
+                                 /* AllowRefinement=*/false,
+                                 &CtlzDropFlags) != TrueVal)
+        continue;
+
+      Builder.SetInsertPoint(II);
+      Value *NonZero = Builder.CreateOr(X, ConstantInt::get(X->getType(), 1),
+                                        "ctlz.nonzero");
+      replaceOperand(*II, 0, NonZero);
+      II->dropPoisonGeneratingAnnotations();
+      Worklist.add(II);
+      for (Instruction *I : CtlzDropFlags) {
+        I->dropPoisonGeneratingAnnotations();
+        Worklist.add(I);
+      }
+      return replaceInstUsesWith(Sel, FalseVal);
+    }
+    return nullptr;
+  };
+
+  if (Instruction *R = FoldZeroPoisonCtlz(CmpLHS, CmpRHS))
+    return R;
+  if (Instruction *R = FoldZeroPoisonCtlz(CmpRHS, CmpLHS))
+    return R;
+
   SmallVector<Instruction *> DropFlags;
   if ((CanReplaceCmpLHSWithRHS &&
        simplifyWithOpReplaced(FalseVal, CmpLHS, CmpRHS, SQ,
diff --git a/llvm/test/Transforms/InstCombine/select-cmp-cttz-ctlz.ll b/llvm/test/Transforms/InstCombine/select-cmp-cttz-ctlz.ll
index 94e6c70cb7ebe..122a551a6a439 100644
--- a/llvm/test/Transforms/InstCombine/select-cmp-cttz-ctlz.ll
+++ b/llvm/test/Transforms/InstCombine/select-cmp-cttz-ctlz.ll
@@ -746,11 +746,10 @@ define i32 @test_ctlz_sub_zero_poison(i32 %arg) {
 
 define i32 @fold_ctlz_sub_one(i32 %arg) {
 ; CHECK-LABEL: @fold_ctlz_sub_one(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[ARG:%.*]], 0
-; CHECK-NEXT:    [[CTZ:%.*]] = call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 [[ARG]], i1 true)
+; CHECK-NEXT:    [[CTLZ_NONZERO:%.*]] = or i32 [[ARG:%.*]], 1
+; CHECK-NEXT:    [[CTZ:%.*]] = call range(i32 0, 32) i32 @llvm.ctlz.i32(i32 [[CTLZ_NONZERO]], i1 true)
 ; CHECK-NEXT:    [[SUB:%.*]] = sub nuw nsw i32 32, [[CTZ]]
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], i32 1, i32 [[SUB]]
-; CHECK-NEXT:    ret i32 [[SEL]]
+; CHECK-NEXT:    ret i32 [[SUB]]
 ;
   %cmp = icmp eq i32 %arg, 0
   %ctz = call i32 @llvm.ctlz.i32(i32 %arg, i1 true)
@@ -1011,12 +1010,11 @@ define <2 x i32> @test_cttz_not_bw_odd_mul_vec_nonsplat(<2 x i32> %x) {
 
 define i64 @fold_ctlz_hex_digits(i64 noundef %x) {
 ; CHECK-LABEL: @fold_ctlz_hex_digits(
-; CHECK-NEXT:    [[ISZERO:%.*]] = icmp eq i64 [[X:%.*]], 0
-; CHECK-NEXT:    [[LZ:%.*]] = call range(i64 0, 65) i64 @llvm.ctlz.i64(i64 [[X]], i1 true)
+; CHECK-NEXT:    [[CTLZ_NONZERO:%.*]] = or i64 [[X:%.*]], 1
+; CHECK-NEXT:    [[LZ:%.*]] = call range(i64 0, 64) i64 @llvm.ctlz.i64(i64 [[CTLZ_NONZERO]], i1 true)
 ; CHECK-NEXT:    [[BIAS:%.*]] = sub nuw nsw i64 67, [[LZ]]
 ; CHECK-NEXT:    [[DIGITS:%.*]] = lshr i64 [[BIAS]], 2
-; CHECK-NEXT:    [[RESULT:%.*]] = select i1 [[ISZERO]], i64 1, i64 [[DIGITS]]
-; CHECK-NEXT:    ret i64 [[RESULT]]
+; CHECK-NEXT:    ret i64 [[DIGITS]]
 ;
   %iszero = icmp eq i64 %x, 0
   %lz = call i64 @llvm.ctlz.i64(i64 %x, i1 true)
@@ -1028,12 +1026,11 @@ define i64 @fold_ctlz_hex_digits(i64 noundef %x) {
 
 define i64 @fold_ctlz_hex_digits_ne_commuted(i64 noundef %x) {
 ; CHECK-LABEL: @fold_ctlz_hex_digits_ne_commuted(
-; CHECK-NEXT:    [[ISNOTZERO_NOT:%.*]] = icmp eq i64 [[X:%.*]], 0
-; CHECK-NEXT:    [[LZ:%.*]] = call range(i64 0, 65) i64 @llvm.ctlz.i64(i64 [[X]], i1 true)
+; CHECK-NEXT:    [[CTLZ_NONZERO:%.*]] = or i64 [[X:%.*]], 1
+; CHECK-NEXT:    [[LZ:%.*]] = call range(i64 0, 64) i64 @llvm.ctlz.i64(i64 [[CTLZ_NONZERO]], i1 true)
 ; CHECK-NEXT:    [[BIAS:%.*]] = sub nuw nsw i64 67, [[LZ]]
 ; CHECK-NEXT:    [[DIGITS:%.*]] = lshr i64 [[BIAS]], 2
-; CHECK-NEXT:    [[RESULT:%.*]] = select i1 [[ISNOTZERO_NOT]], i64 1, i64 [[DIGITS]]
-; CHECK-NEXT:    ret i64 [[RESULT]]
+; CHECK-NEXT:    ret i64 [[DIGITS]]
 ;
   %isnotzero = icmp ne i64 0, %x
   %lz = call i64 @llvm.ctlz.i64(i64 %x, i1 true)
@@ -1045,11 +1042,10 @@ define i64 @fold_ctlz_hex_digits_ne_commuted(i64 noundef %x) {
 
 define i64 @fold_ctlz_hex_digits_exact(i64 noundef %x) {
 ; CHECK-LABEL: @fold_ctlz_hex_digits_exact(
-; CHECK-NEXT:    [[ISZERO:%.*]] = icmp eq i64 [[X:%.*]], 0
-; CHECK-NEXT:    [[LZ:%.*]] = call range(i64 0, 65) i64 @llvm.ctlz.i64(i64 [[X]], i1 true)
+; CHECK-NEXT:    [[CTLZ_NONZERO:%.*]] = or i64 [[X:%.*]], 1
+; CHECK-NEXT:    [[LZ:%.*]] = call range(i64 0, 64) i64 @llvm.ctlz.i64(i64 [[CTLZ_NONZERO]], i1 true)
 ; CHECK-NEXT:    [[BIAS:%.*]] = sub nuw nsw i64 67, [[LZ]]
-; CHECK-NEXT:    [[DIGITS1:%.*]] = lshr exact i64 [[BIAS]], 2
-; CHECK-NEXT:    [[DIGITS:%.*]] = select i1 [[ISZERO]], i64 1, i64 [[DIGITS1]]
+; CHECK-NEXT:    [[DIGITS:%.*]] = lshr i64 [[BIAS]], 2
 ; CHECK-NEXT:    ret i64 [[DIGITS]]
 ;
   %iszero = icmp eq i64 %x, 0
@@ -1099,7 +1095,8 @@ define i64 @do_not_fold_ctlz_hex_digits_wrong_zero_value(i64 noundef %x) {
 ; The generic equivalence fold drops nuw because it would be poison at X == 0.
 define i16 @fold_ctlz_drop_nuw_on_zero(i16 noundef %x) {
 ; CHECK-LABEL: @fold_ctlz_drop_nuw_on_zero(
-; CHECK-NEXT:    [[LZ:%.*]] = call range(i16 0, 17) i16 @llvm.ctlz.i16(i16 [[X:%.*]], i1 false)
+; CHECK-NEXT:    [[CTLZ_NONZERO:%.*]] = or i16 [[X:%.*]], 1
+; CHECK-NEXT:    [[LZ:%.*]] = call range(i16 0, 16) i16 @llvm.ctlz.i16(i16 [[CTLZ_NONZERO]], i1 true)
 ; CHECK-NEXT:    [[BIAS:%.*]] = sub nsw i16 10, [[LZ]]
 ; CHECK-NEXT:    [[DIGITS:%.*]] = lshr i16 [[BIAS]], 1
 ; CHECK-NEXT:    ret i16 [[DIGITS]]
@@ -1115,7 +1112,8 @@ define i16 @fold_ctlz_drop_nuw_on_zero(i16 noundef %x) {
 ; The generic equivalence fold drops nsw because it would be poison at X == 0.
 define i16 @fold_ctlz_drop_nsw_on_zero(i16 noundef %x) {
 ; CHECK-LABEL: @fold_ctlz_drop_nsw_on_zero(
-; CHECK-NEXT:    [[LZ:%.*]] = call range(i16 0, 17) i16 @llvm.ctlz.i16(i16 [[X:%.*]], i1 false)
+; CHECK-NEXT:    [[CTLZ_NONZERO:%.*]] = or i16 [[X:%.*]], 1
+; CHECK-NEXT:    [[LZ:%.*]] = call range(i16 0, 16) i16 @llvm.ctlz.i16(i16 [[CTLZ_NONZERO]], i1 true)
 ; CHECK-NEXT:    [[BIAS:%.*]] = sub nuw i16 -32760, [[LZ]]
 ; CHECK-NEXT:    [[DIGITS:%.*]] = lshr i16 [[BIAS]], 1
 ; CHECK-NEXT:    ret i16 [[DIGITS]]
@@ -1131,7 +1129,8 @@ define i16 @fold_ctlz_drop_nsw_on_zero(i16 noundef %x) {
 ; The generic equivalence fold drops exact because it would be poison at X == 0.
 define i16 @fold_ctlz_drop_exact_on_zero(i16 noundef %x) {
 ; CHECK-LABEL: @fold_ctlz_drop_exact_on_zero(
-; CHECK-NEXT:    [[LZ:%.*]] = call range(i16 0, 17) i16 @llvm.ctlz.i16(i16 [[X:%.*]], i1 false)
+; CHECK-NEXT:    [[CTLZ_NONZERO:%.*]] = or i16 [[X:%.*]], 1
+; CHECK-NEXT:    [[LZ:%.*]] = call range(i16 0, 16) i16 @llvm.ctlz.i16(i16 [[CTLZ_NONZERO]], i1 true)
 ; CHECK-NEXT:    [[BIAS:%.*]] = sub nuw nsw i16 20, [[LZ]]
 ; CHECK-NEXT:    [[DIGITS:%.*]] = lshr i16 [[BIAS]], 2
 ; CHECK-NEXT:    ret i16 [[DIGITS]]



More information about the llvm-commits mailing list