[llvm] [InstCombine] fold ((x - 1) | (2^k - 1)) + 1 to (x + (2^k - 1)) & -(2^k) (PR #216436)

Pranav Alavandi via llvm-commits llvm-commits at lists.llvm.org
Sat Aug 15 02:42:11 PDT 2026


https://github.com/pranavalavandi updated https://github.com/llvm/llvm-project/pull/216436

>From 12fbb9a4af5a7440e45c376f75400c5e1e6f6217 Mon Sep 17 00:00:00 2001
From: pranavalavandi <pranav.alavandi at gmail.com>
Date: Sat, 15 Aug 2026 09:51:29 +1000
Subject: [PATCH 1/3] [InstCombine] Add tests

---
 .../Transforms/InstCombine/add-mask-neg.ll    | 119 ++++++++++++++++++
 1 file changed, 119 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/add-mask-neg.ll b/llvm/test/Transforms/InstCombine/add-mask-neg.ll
index a83d86da58b82..2d775f8e74890 100644
--- a/llvm/test/Transforms/InstCombine/add-mask-neg.ll
+++ b/llvm/test/Transforms/InstCombine/add-mask-neg.ll
@@ -287,5 +287,124 @@ 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:    [[SUB:%.*]] = add i32 [[X:%.*]], -1
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[SUB]], 4095
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[OR]], 1
+; 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:    [[SUB:%.*]] = add i32 [[X:%.*]], -1
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[SUB]], 4095
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[OR]], 1
+; 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:    [[SUB:%.*]] = add i32 [[X:%.*]], -1
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[SUB]], 4095
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[OR]], 1
+; 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:    [[SUB:%.*]] = add <2 x i32> [[X:%.*]], splat (i32 -1)
+; CHECK-NEXT:    [[OR:%.*]] = or <2 x i32> [[SUB]], splat (i32 4095)
+; CHECK-NEXT:    [[ADD:%.*]] = add <2 x i32> [[OR]], splat (i32 1)
+; 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:    [[OR:%.*]] = or i32 [[SUB]], 4095
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[OR]], 1
+; 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>)

>From cbda7617d36d05d5176f287f9236ad299f9a374a Mon Sep 17 00:00:00 2001
From: pranavalavandi <pranav.alavandi at gmail.com>
Date: Sat, 15 Aug 2026 09:54:37 +1000
Subject: [PATCH 2/3] [InstCombine] fold ((x - 1) | (2^k - 1)) + 1 to (x + (2^k
 - 1)) & -(2^k)

---
 .../InstCombine/InstCombineAddSub.cpp         | 13 +++++++---
 .../Transforms/InstCombine/add-mask-neg.ll    | 24 ++++++++-----------
 2 files changed, 20 insertions(+), 17 deletions(-)

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 2d775f8e74890..3cf250dc5d03c 100644
--- a/llvm/test/Transforms/InstCombine/add-mask-neg.ll
+++ b/llvm/test/Transforms/InstCombine/add-mask-neg.ll
@@ -293,9 +293,8 @@ define <2 x i32> @align_up_vec_poison_elt(<2 x i32> %x) {
 
 define i32 @align_up_via_or(i32 %x) {
 ; CHECK-LABEL: @align_up_via_or(
-; CHECK-NEXT:    [[SUB:%.*]] = add i32 [[X:%.*]], -1
-; CHECK-NEXT:    [[OR:%.*]] = or i32 [[SUB]], 4095
-; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[OR]], 1
+; CHECK-NEXT:    [[TMP1:%.*]] = add i32 [[X:%.*]], 4095
+; CHECK-NEXT:    [[ADD:%.*]] = and i32 [[TMP1]], -4096
 ; CHECK-NEXT:    ret i32 [[ADD]]
 ;
   %sub = add i32 %x, -1
@@ -306,9 +305,8 @@ define i32 @align_up_via_or(i32 %x) {
 
 define i32 @align_up_via_or_commuted(i32 %x) {
 ; CHECK-LABEL: @align_up_via_or_commuted(
-; CHECK-NEXT:    [[SUB:%.*]] = add i32 [[X:%.*]], -1
-; CHECK-NEXT:    [[OR:%.*]] = or i32 [[SUB]], 4095
-; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[OR]], 1
+; CHECK-NEXT:    [[TMP1:%.*]] = add i32 [[X:%.*]], 4095
+; CHECK-NEXT:    [[ADD:%.*]] = and i32 [[TMP1]], -4096
 ; CHECK-NEXT:    ret i32 [[ADD]]
 ;
   %sub = add i32 %x, -1
@@ -319,9 +317,8 @@ define i32 @align_up_via_or_commuted(i32 %x) {
 
 define i32 @align_up_via_or_commuted_or(i32 %x) {
 ; CHECK-LABEL: @align_up_via_or_commuted_or(
-; CHECK-NEXT:    [[SUB:%.*]] = add i32 [[X:%.*]], -1
-; CHECK-NEXT:    [[OR:%.*]] = or i32 [[SUB]], 4095
-; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[OR]], 1
+; CHECK-NEXT:    [[TMP1:%.*]] = add i32 [[X:%.*]], 4095
+; CHECK-NEXT:    [[ADD:%.*]] = and i32 [[TMP1]], -4096
 ; CHECK-NEXT:    ret i32 [[ADD]]
 ;
   %sub = add i32 %x, -1
@@ -332,9 +329,8 @@ define i32 @align_up_via_or_commuted_or(i32 %x) {
 
 define <2 x i32> @align_up_via_or_vec(<2 x i32> %x) {
 ; CHECK-LABEL: @align_up_via_or_vec(
-; CHECK-NEXT:    [[SUB:%.*]] = add <2 x i32> [[X:%.*]], splat (i32 -1)
-; CHECK-NEXT:    [[OR:%.*]] = or <2 x i32> [[SUB]], splat (i32 4095)
-; CHECK-NEXT:    [[ADD:%.*]] = add <2 x i32> [[OR]], splat (i32 1)
+; 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)
@@ -381,8 +377,8 @@ define i32 @align_up_via_or_multiuse_or(i32 %x) {
 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:    [[OR:%.*]] = or i32 [[SUB]], 4095
-; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[OR]], 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]]
 ;

>From 3ab0b031afe0d904bbd885caffc2f6d8824f18cb Mon Sep 17 00:00:00 2001
From: pranavalavandi <pranav.alavandi at gmail.com>
Date: Sat, 15 Aug 2026 19:14:51 +1000
Subject: [PATCH 3/3] Address PR comments

---
 .../InstCombine/InstCombineAddSub.cpp         |  4 +--
 .../Transforms/InstCombine/add-mask-neg.ll    | 28 ++-----------------
 2 files changed, 4 insertions(+), 28 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 6c919d59446dd..bc43a870537bb 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1832,8 +1832,8 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
               m_c_Add(m_OneUse(m_And(m_Neg(m_Value(A)), m_LowBitMask(LowMask))),
                       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)))))
+              m_Add(m_OneUse(m_Or(m_Add(m_Value(A), m_AllOnes()),
+                                               m_LowBitMask(LowMask))), m_One()))
 
     ) {
       Value *NewAdd = Builder.CreateAdd(A, 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 3cf250dc5d03c..ddad9701f5b2e 100644
--- a/llvm/test/Transforms/InstCombine/add-mask-neg.ll
+++ b/llvm/test/Transforms/InstCombine/add-mask-neg.ll
@@ -303,30 +303,6 @@ define i32 @align_up_via_or(i32 %x) {
   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)
@@ -389,7 +365,7 @@ define i32 @align_up_via_or_multiuse_sub(i32 %x) {
   ret i32 %add
 }
 
-; nsw/nuw on the source add are dropped
+; nsw on the source add is dropped
 
 define i32 @align_up_via_or_nsw_nuw(i32 %x){
 ; CHECK-LABEL: @align_up_via_or_nsw_nuw(
@@ -397,7 +373,7 @@ define i32 @align_up_via_or_nsw_nuw(i32 %x){
 ;
   %sub = add nuw nsw i32 %x, -1
   %or = or i32 %sub, 4095
-  %add = add nuw nsw i32 %or, 1
+  %add = add nsw i32 %or, 1
   ret i32 %add
 }
 



More information about the llvm-commits mailing list