Skip to content

[InstCombine] Restrict foldBitCeil to power-of-two integer widths#173849

Merged
nikic merged 3 commits intollvm:mainfrom
cardigan1008:fix-173787
Dec 29, 2025
Merged

[InstCombine] Restrict foldBitCeil to power-of-two integer widths#173849
nikic merged 3 commits intollvm:mainfrom
cardigan1008:fix-173787

Conversation

@cardigan1008
Copy link
Contributor

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

@cardigan1008 cardigan1008 requested a review from nikic as a code owner December 29, 2025 09:20
@llvmbot llvmbot added llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms labels Dec 29, 2025
@llvmbot
Copy link
Member

llvmbot commented Dec 29, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Yunbo Ni (cardigan1008)

Changes

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


Full diff: https://github.com/llvm/llvm-project/pull/173849.diff

2 Files Affected:

  • (modified) llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp (+3)
  • (modified) llvm/test/Transforms/InstCombine/bit_ceil.ll (+20)
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 09f90ee05735d..edf1c176ff12c 100644
--- a/llvm/test/Transforms/InstCombine/bit_ceil.ll
+++ b/llvm/test/Transforms/InstCombine/bit_ceil.ll
@@ -337,6 +337,25 @@ 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:    [[TMP2:%.*]] = sub nuw nsw i33 33, [[CTLZ]]
+; CHECK-NEXT:    [[SEL:%.*]] = shl nuw i33 1, [[TMP2]]
+; 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
+  %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 +397,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)

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@nikic nikic enabled auto-merge (squash) December 29, 2025 12:13
@nikic nikic merged commit 57927e9 into llvm:main Dec 29, 2025
9 of 10 checks passed
mahesh-attarde pushed a commit to mahesh-attarde/llvm-project that referenced this pull request Jan 6, 2026
…lvm#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 llvm#173787
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[InstCombine] foldBitCeil miscompiles non-power-of-two integer widths

3 participants