[llvm] [InstCombine] Restrict `foldBitCeil` to power-of-two integer widths (PR #173849)

Yunbo Ni via llvm-commits llvm-commits at lists.llvm.org
Mon Dec 29 01:20:25 PST 2025


https://github.com/cardigan1008 created https://github.com/llvm/llvm-project/pull/173849

The masking rewrite in `foldBitCeil` assumes a power-of-two bitwidth.

For non-power-of-two integer types, `(-ctlz) & (BitWidth - 1)` is not equivalent to `BitWidth - ctlz` and can miscompile.

This patch restricts the transform to power-of-two bitwidths. 

Alive2 proof: https://alive2.llvm.org/ce/z/i2E6zT

Fixes #173787 


>From 71ea73f7b3820cc416eff1668675a85991648b18 Mon Sep 17 00:00:00 2001
From: cardigan1008 <ybni at cse.cuhk.edu.hk>
Date: Mon, 29 Dec 2025 16:41:17 +0800
Subject: [PATCH 1/2] [InstCombine][NFC] Add pre-commit test case.

---
 llvm/test/Transforms/InstCombine/bit_ceil.ll | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/bit_ceil.ll b/llvm/test/Transforms/InstCombine/bit_ceil.ll
index 09f90ee05735d..caf1944a05d36 100644
--- a/llvm/test/Transforms/InstCombine/bit_ceil.ll
+++ b/llvm/test/Transforms/InstCombine/bit_ceil.ll
@@ -337,6 +337,23 @@ define i32 @test_drop_range_attr(i32 %x) {
   ret i32 %sel
 }
 
+define i33 @test_bit_ceil_i33_non_pow2(i33 %x) {
+; CHECK-LABEL: @test_bit_ceil_i33_non_pow2(
+; CHECK-NEXT:    [[CTLZ:%.*]] = call range(i33 0, 34) i33 @llvm.ctlz.i33(i33 [[X:%.*]], i1 false)
+; CHECK-NEXT:    [[TMP1:%.*]] = sub nsw i33 0, [[CTLZ]]
+; CHECK-NEXT:    [[TMP2:%.*]] = and i33 [[TMP1]], 32
+; CHECK-NEXT:    [[SEL:%.*]] = shl nuw i33 1, [[TMP2]]
+; CHECK-NEXT:    ret i33 [[SEL]]
+;
+  %ctlz = call i33 @llvm.ctlz.i33(i33 %x, i1 false)
+  %sub = sub i33 33, %ctlz
+  %shl = shl i33 1, %sub
+  %dec = add i33 %x, -1
+  %ult = icmp ult i33 %dec, -2
+  %sel = select i1 %ult, i33 %shl, i33 1
+  ret i33 %sel
+}
+
 define i32 @bit_ceil_plus_nsw(i32 %x) {
 ; CHECK-LABEL: @bit_ceil_plus_nsw(
 ; CHECK-NEXT:  entry:
@@ -378,5 +395,6 @@ entry:
 }
 
 declare i32 @llvm.ctlz.i32(i32, i1 immarg)
+declare i33 @llvm.ctlz.i33(i33, i1 immarg)
 declare i64 @llvm.ctlz.i64(i64, i1 immarg)
 declare <4 x i32> @llvm.ctlz.v4i32(<4 x i32>, i1)

>From 2883bdae45149c1edc14c0345f8edbbd2874d35f Mon Sep 17 00:00:00 2001
From: cardigan1008 <ybni at cse.cuhk.edu.hk>
Date: Mon, 29 Dec 2025 17:15:13 +0800
Subject: [PATCH 2/2] [InstCombine] Disable non-power-of-two integer widths in
 `foldBitCeil`.

---
 llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp | 3 +++
 llvm/test/Transforms/InstCombine/bit_ceil.ll          | 8 +++++---
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 67d1845832725..b7ab642cde6ea 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -3871,6 +3871,9 @@ static Instruction *foldBitCeil(SelectInst &SI, IRBuilderBase &Builder,
                                    ShouldDropNoWrap))
     return nullptr;
 
+  if (!isPowerOf2_32(BitWidth))
+    return nullptr;
+
   if (ShouldDropNoWrap) {
     cast<Instruction>(CtlzOp)->setHasNoUnsignedWrap(false);
     cast<Instruction>(CtlzOp)->setHasNoSignedWrap(false);
diff --git a/llvm/test/Transforms/InstCombine/bit_ceil.ll b/llvm/test/Transforms/InstCombine/bit_ceil.ll
index caf1944a05d36..edf1c176ff12c 100644
--- a/llvm/test/Transforms/InstCombine/bit_ceil.ll
+++ b/llvm/test/Transforms/InstCombine/bit_ceil.ll
@@ -340,10 +340,12 @@ define i32 @test_drop_range_attr(i32 %x) {
 define i33 @test_bit_ceil_i33_non_pow2(i33 %x) {
 ; CHECK-LABEL: @test_bit_ceil_i33_non_pow2(
 ; CHECK-NEXT:    [[CTLZ:%.*]] = call range(i33 0, 34) i33 @llvm.ctlz.i33(i33 [[X:%.*]], i1 false)
-; CHECK-NEXT:    [[TMP1:%.*]] = sub nsw i33 0, [[CTLZ]]
-; CHECK-NEXT:    [[TMP2:%.*]] = and i33 [[TMP1]], 32
+; CHECK-NEXT:    [[TMP2:%.*]] = sub nuw nsw i33 33, [[CTLZ]]
 ; CHECK-NEXT:    [[SEL:%.*]] = shl nuw i33 1, [[TMP2]]
-; CHECK-NEXT:    ret i33 [[SEL]]
+; CHECK-NEXT:    [[DEC:%.*]] = add i33 [[X]], -1
+; CHECK-NEXT:    [[ULT:%.*]] = icmp ult i33 [[DEC]], -2
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[ULT]], i33 [[SEL]], i33 1
+; CHECK-NEXT:    ret i33 [[SEL1]]
 ;
   %ctlz = call i33 @llvm.ctlz.i33(i33 %x, i1 false)
   %sub = sub i33 33, %ctlz



More information about the llvm-commits mailing list