[llvm] [InstCombine] fold ((x - 1) | (2^k - 1)) + 1 to (x + (2^k - 1)) & -(2^k) (PR #216436)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 14 17:18:36 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Pranav Alavandi (pranavalavandi)
<details>
<summary>Changes</summary>
Fold `((x - 1) | (C - 1)) + 1 to (x + (C - 1)) & -(C)` for a power of 2 `C`
Fixes issue: https://github.com/llvm/llvm-project/issues/215945
Generalized proof: https://alive2.llvm.org/ce/z/tQubkT
Similar to https://github.com/llvm/llvm-project/pull/215122 and corresponding issue https://github.com/llvm/llvm-project/issues/214675
---
Full diff: https://github.com/llvm/llvm-project/pull/216436.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp (+10-3)
- (modified) llvm/test/Transforms/InstCombine/add-mask-neg.ll (+115)
``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 30812cafd2f80..6c919d59446dd 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1822,13 +1822,20 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
}
// Align-up idiom:
- // X + ((-X) & (C - 1)) --> (X + C - 1) & -C, for a power-of-two C.
- // Note -C == ~(C - 1), so the mask is simply the inverted low-bit mask.
+ // X + ((-X) & (C - 1)) --> (X + C - 1) & -C
+ // ((X - 1) | (C - 1)) + 1 -> (X + C - 1) & -C
+ // For a power-of-two C. Note -C == ~(C - 1), so the mask is simply the
+ // inverted low-bit mask.
{
const APInt *LowMask;
if (match(&I,
m_c_Add(m_OneUse(m_And(m_Neg(m_Value(A)), m_LowBitMask(LowMask))),
- m_Deferred(A)))) {
+ m_Deferred(A))) ||
+ match(&I,
+ m_c_Add(m_One(), m_OneUse(m_c_Or(m_Add(m_Value(A), m_AllOnes()),
+ m_LowBitMask(LowMask)))))
+
+ ) {
Value *NewAdd = Builder.CreateAdd(A, ConstantInt::get(Ty, *LowMask));
return BinaryOperator::CreateAnd(NewAdd, ConstantInt::get(Ty, ~*LowMask));
}
diff --git a/llvm/test/Transforms/InstCombine/add-mask-neg.ll b/llvm/test/Transforms/InstCombine/add-mask-neg.ll
index a83d86da58b82..3cf250dc5d03c 100644
--- a/llvm/test/Transforms/InstCombine/add-mask-neg.ll
+++ b/llvm/test/Transforms/InstCombine/add-mask-neg.ll
@@ -287,5 +287,120 @@ define <2 x i32> @align_up_vec_poison_elt(<2 x i32> %x) {
ret <2 x i32> %r
}
+;
+; ((X - 1) | C) + 1 -> (X + C) & ~C, for power-of-two C
+;
+
+define i32 @align_up_via_or(i32 %x) {
+; CHECK-LABEL: @align_up_via_or(
+; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], 4095
+; CHECK-NEXT: [[ADD:%.*]] = and i32 [[TMP1]], -4096
+; CHECK-NEXT: ret i32 [[ADD]]
+;
+ %sub = add i32 %x, -1
+ %or = or i32 %sub, 4095
+ %add = add i32 %or, 1
+ ret i32 %add
+}
+
+define i32 @align_up_via_or_commuted(i32 %x) {
+; CHECK-LABEL: @align_up_via_or_commuted(
+; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], 4095
+; CHECK-NEXT: [[ADD:%.*]] = and i32 [[TMP1]], -4096
+; CHECK-NEXT: ret i32 [[ADD]]
+;
+ %sub = add i32 %x, -1
+ %or = or i32 %sub, 4095
+ %add = add i32 1, %or
+ ret i32 %add
+}
+
+define i32 @align_up_via_or_commuted_or(i32 %x) {
+; CHECK-LABEL: @align_up_via_or_commuted_or(
+; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], 4095
+; CHECK-NEXT: [[ADD:%.*]] = and i32 [[TMP1]], -4096
+; CHECK-NEXT: ret i32 [[ADD]]
+;
+ %sub = add i32 %x, -1
+ %or = or i32 4095, %sub
+ %add = add i32 %or, 1
+ ret i32 %add
+}
+
+define <2 x i32> @align_up_via_or_vec(<2 x i32> %x) {
+; CHECK-LABEL: @align_up_via_or_vec(
+; CHECK-NEXT: [[TMP1:%.*]] = add <2 x i32> [[X:%.*]], splat (i32 4095)
+; CHECK-NEXT: [[ADD:%.*]] = and <2 x i32> [[TMP1]], splat (i32 -4096)
+; CHECK-NEXT: ret <2 x i32> [[ADD]]
+;
+ %sub = add <2 x i32> %x, splat (i32 -1)
+ %or = or <2 x i32> %sub, splat (i32 4095)
+ %add = add <2 x i32> %or, splat (i32 1)
+ ret <2 x i32> %add
+}
+
+; negative test - the mask is not a low bit mask
+
+
+define i32 @align_up_via_or_not_lowbitmask(i32 %x) {
+; CHECK-LABEL: @align_up_via_or_not_lowbitmask(
+; CHECK-NEXT: [[SUB:%.*]] = add i32 [[X:%.*]], -1
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[SUB]], 13
+; CHECK-NEXT: [[ADD:%.*]] = add i32 [[OR]], 1
+; CHECK-NEXT: ret i32 [[ADD]]
+;
+ %sub = add i32 %x, -1
+ %or = or i32 %sub, 13
+ %add = add i32 %or, 1
+ ret i32 %add
+}
+
+; negative test - extra use of the or
+
+define i32 @align_up_via_or_multiuse_or(i32 %x) {
+; CHECK-LABEL: @align_up_via_or_multiuse_or(
+; CHECK-NEXT: [[SUB:%.*]] = add i32 [[X:%.*]], -1
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[SUB]], 4095
+; CHECK-NEXT: [[ADD:%.*]] = add i32 [[OR]], 1
+; CHECK-NEXT: call void @use(i32 [[OR]])
+; CHECK-NEXT: ret i32 [[ADD]]
+;
+ %sub = add i32 %x, -1
+ %or = or i32 %sub, 4095
+ %add = add i32 %or, 1
+ call void @use(i32 %or)
+ ret i32 %add
+}
+
+; extra use of the sub is not affecting
+
+define i32 @align_up_via_or_multiuse_sub(i32 %x) {
+; CHECK-LABEL: @align_up_via_or_multiuse_sub(
+; CHECK-NEXT: [[SUB:%.*]] = add i32 [[X:%.*]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X]], 4095
+; CHECK-NEXT: [[ADD:%.*]] = and i32 [[TMP1]], -4096
+; CHECK-NEXT: call void @use(i32 [[SUB]])
+; CHECK-NEXT: ret i32 [[ADD]]
+;
+ %sub = add i32 %x, -1
+ %or = or i32 %sub, 4095
+ %add = add i32 %or, 1
+ call void @use(i32 %sub)
+ ret i32 %add
+}
+
+; nsw/nuw on the source add are dropped
+
+define i32 @align_up_via_or_nsw_nuw(i32 %x){
+; CHECK-LABEL: @align_up_via_or_nsw_nuw(
+; CHECK-NEXT: ret i32 0
+;
+ %sub = add nuw nsw i32 %x, -1
+ %or = or i32 %sub, 4095
+ %add = add nuw nsw i32 %or, 1
+ ret i32 %add
+}
+
+
declare void @use(i32)
declare void @usev(<2 x i32>)
``````````
</details>
https://github.com/llvm/llvm-project/pull/216436
More information about the llvm-commits
mailing list