[llvm] [InstCombine] Fold shl of constant by cttz into multiply of lowest set bit (PR #214517)

via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 6 08:51:47 PDT 2026


https://github.com/milkHongYe created https://github.com/llvm/llvm-project/pull/214517

Currently, `C << cttz(X, true)` where C is a scalar constant generates a DeBruijn lookup table on RV64I (13 instructions).
And this patch adds a fold in InstCombine:
  `C << cttz(X, true)  -->  (-X & X) * C`
This reduces the instruction count from 13 to 3 on RV64I.
The fold requires:
  - C is a scalar constant (m_APInt)
  - cttz is_zero_poison = true, guaranteeing X != 0
  - cttz has a single use (to avoid increasing instruction count)


>From eaa2f15c096b73e480a94755de27fc8752a5f5a7 Mon Sep 17 00:00:00 2001
From: milkHongYe <1483685464 at qq.com>
Date: Thu, 6 Aug 2026 23:28:11 +0800
Subject: [PATCH] [InstCombine] Fold shl of constant by cttz into multiply of
 lowest set bit

---
 .../Transforms/InstCombine/InstCombineShifts.cpp    | 13 +++++++++++++
 llvm/test/Transforms/InstCombine/shift.ll           |  5 +++--
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp
index 241bc3bba51c2..5eaa326faaf10 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp
@@ -1382,6 +1382,19 @@ Instruction *InstCombinerImpl::visitShl(BinaryOperator &I) {
     }
   }
 
+  // C << (cttz X, true) --> (-X & X) * C  (C must be a scalar constant, cttz
+  // must have one use)
+  {
+    Value *X;
+    const APInt *C;
+    if (match(Op0, m_APInt(C)) &&
+        match(Op1, m_OneUse(m_Cttz(m_Value(X), m_One())))) {
+      Value *NegX = Builder.CreateNeg(X, "neg");
+      Value *LowBit = Builder.CreateAnd(NegX, X);
+      return BinaryOperator::CreateMul(LowBit, Op0);
+    }
+  }
+
   return nullptr;
 }
 
diff --git a/llvm/test/Transforms/InstCombine/shift.ll b/llvm/test/Transforms/InstCombine/shift.ll
index d63ba48830068..4c05a56b1dbc0 100644
--- a/llvm/test/Transforms/InstCombine/shift.ll
+++ b/llvm/test/Transforms/InstCombine/shift.ll
@@ -2097,8 +2097,9 @@ define i32 @shl1_cttz_extra_use(i32 %x) {
 
 define i32 @shl2_cttz(i32 %x) {
 ; CHECK-LABEL: @shl2_cttz(
-; CHECK-NEXT:    [[TZ:%.*]] = call range(i32 0, 33) i32 @llvm.cttz.i32(i32 [[X:%.*]], i1 true)
-; CHECK-NEXT:    [[SHL:%.*]] = shl i32 2, [[TZ]]
+; CHECK-NEXT:    [[NEG:%.*]] = sub i32 0, [[X:%.*]]
+; CHECK-NEXT:    [[TMP1:%.*]] = and i32 [[X]], [[NEG]]
+; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[TMP1]], 1
 ; CHECK-NEXT:    ret i32 [[SHL]]
 ;
   %tz = call i32 @llvm.cttz.i32(i32 %x, i1 true)



More information about the llvm-commits mailing list