[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