[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
Wed Aug 12 21:26:19 PDT 2026
https://github.com/milkHongYe updated https://github.com/llvm/llvm-project/pull/214517
>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 1/2] [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)
>From a2b3ba4a10e7a5c3e188111ca573dc73ee7edbea Mon Sep 17 00:00:00 2001
From: milkHongYe <1483685464 at qq.com>
Date: Thu, 13 Aug 2026 12:15:59 +0800
Subject: [PATCH 2/2] [InstCombine] Add some tests for shl-cttz optimizaion
---
llvm/test/Transforms/InstCombine/shift.ll | 40 +++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/llvm/test/Transforms/InstCombine/shift.ll b/llvm/test/Transforms/InstCombine/shift.ll
index 4c05a56b1dbc0..074ff7a8ed993 100644
--- a/llvm/test/Transforms/InstCombine/shift.ll
+++ b/llvm/test/Transforms/InstCombine/shift.ll
@@ -2107,6 +2107,46 @@ define i32 @shl2_cttz(i32 %x) {
ret i32 %shl
}
+; C=4: different constant, should also fold
+define i32 @shl4_cttz(i32 %x) {
+; CHECK-LABEL: @shl4_cttz(
+; CHECK-NEXT: [[NEG:%.*]] = sub i32 0, [[X:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[X]], [[NEG]]
+; CHECK-NEXT: [[SHL:%.*]] = shl i32 [[TMP1]], 2
+; CHECK-NEXT: ret i32 [[SHL]]
+;
+ %tz = call i32 @llvm.cttz.i32(i32 %x, i1 true)
+ %shl = shl i32 4, %tz
+ ret i32 %shl
+}
+
+; negative test: cttz has multiple uses, should not fold
+define i32 @shl2_cttz_multiuse(i32 %x) {
+; CHECK-LABEL: @shl2_cttz_multiuse(
+; CHECK-NEXT: [[TZ:%.*]] = call range(i32 0, 33) i32 @llvm.cttz.i32(i32 [[X:%.*]], i1 true)
+; CHECK-NEXT: call void @use_i32(i32 [[TZ]])
+; CHECK-NEXT: [[SHL:%.*]] = shl i32 2, [[TZ]]
+; CHECK-NEXT: ret i32 [[SHL]]
+;
+ %tz = call i32 @llvm.cttz.i32(i32 %x, i1 true)
+ call void @use_i32(i32 %tz)
+ %shl = shl i32 2, %tz
+ ret i32 %shl
+}
+
+; negative test: is_zero_poison = false, should not fold
+define i32 @shl2_cttz_zero_poison_false(i32 %x) {
+; CHECK-LABEL: @shl2_cttz_zero_poison_false(
+; 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 false)
+ %shl = shl i32 2, %tz
+ ret i32 %shl
+}
+
; shift (X, amt | bitwidth - 1) -> shift (X, bitwidth - 1)
define i6 @shl_or7_eq_shl7(i6 %x, i6 %c) {
; CHECK-LABEL: @shl_or7_eq_shl7(
More information about the llvm-commits
mailing list