[llvm] [InstCombine] Generalize `(A + 1) + ~B` fold to any constant (PR #188271)

Piotr Fusik via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 9 07:05:31 PDT 2026


https://github.com/pfusik updated https://github.com/llvm/llvm-project/pull/188271

>From 3fac46f51306f7ced4c59d5e1fdedf48e673b286 Mon Sep 17 00:00:00 2001
From: Piotr Fusik <p.fusik at samsung.com>
Date: Tue, 24 Mar 2026 14:32:47 +0100
Subject: [PATCH 01/15] [InstCombine][test] Combine `A + C + ~B --> A - B +
 (C-1)`

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

diff --git a/llvm/test/Transforms/InstCombine/add.ll b/llvm/test/Transforms/InstCombine/add.ll
index aa68dfb540064..8bb50ae0fc381 100644
--- a/llvm/test/Transforms/InstCombine/add.ll
+++ b/llvm/test/Transforms/InstCombine/add.ll
@@ -1194,6 +1194,71 @@ define i32 @add_to_sub2(i32 %A, i32 %M) {
   ret i32 %E
 }
 
+define i32 @add_not_add_m1(i32 %a, i32 %b) {
+; CHECK-LABEL: @add_not_add_m1(
+; CHECK-NEXT:    [[NOT:%.*]] = xor i32 [[B:%.*]], -1
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[A:%.*]], -1
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[ADD]], [[NOT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %not = xor i32 %b, -1
+  %add = add i32 %a, -1
+  %r = add i32 %add, %not
+  ret i32 %r
+}
+
+define <2 x i32> @add_not_add_m1_vec(<2 x i32> %a, <2 x i32> %b) {
+; CHECK-LABEL: @add_not_add_m1_vec(
+; CHECK-NEXT:    [[NOT:%.*]] = xor <2 x i32> [[B:%.*]], splat (i32 -1)
+; CHECK-NEXT:    [[ADD:%.*]] = add <2 x i32> [[A:%.*]], splat (i32 -1)
+; CHECK-NEXT:    [[R:%.*]] = add <2 x i32> [[ADD]], [[NOT]]
+; CHECK-NEXT:    ret <2 x i32> [[R]]
+;
+  %not = xor <2 x i32> %b, <i32 -1, i32 -1>
+  %add = add <2 x i32> %a, <i32 -1, i32 -1>
+  %r = add <2 x i32> %add, %not
+  ret <2 x i32> %r
+}
+
+define i32 @add_not_add_m1_commuted(i32 %a, i32 %b) {
+; CHECK-LABEL: @add_not_add_m1_commuted(
+; CHECK-NEXT:    [[NOT:%.*]] = xor i32 [[B:%.*]], -1
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[A:%.*]], -1
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[ADD]], [[NOT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %not = xor i32 %b, -1
+  %add = add i32 %a, -1
+  %r = add i32 %not, %add
+  ret i32 %r
+}
+
+define i32 @add_not_add_m1_assoc(i32 %a, i32 %b) {
+; CHECK-LABEL: @add_not_add_m1_assoc(
+; CHECK-NEXT:    [[NOT:%.*]] = xor i32 [[B:%.*]], -1
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[A:%.*]], [[NOT]]
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[ADD]], -1
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %not = xor i32 %b, -1
+  %add = add i32 %not, %a
+  %r = add i32 %add, -1
+  ret i32 %r
+}
+
+define i32 @add_not_add_intmin(i32 %a, i32 %b) {
+; CHECK-LABEL: @add_not_add_intmin(
+; CHECK-NEXT:    [[NOT:%.*]] = xor i32 [[B:%.*]], -1
+; CHECK-NEXT:    [[ADD:%.*]] = xor i32 [[A:%.*]], -2147483648
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[ADD]], [[NOT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %not = xor i32 %b, -1
+  %add = add i32 %a, -2147483648
+  %r = add i32 %add, %not
+  ret i32 %r
+}
+
 ; (X | C1) + C2 --> (X | C1) ^ C1 iff (C1 == -C2)
 define i32 @test44(i32 %A) {
 ; CHECK-LABEL: @test44(

>From 9655d9a82173d2f050e22b7d806eec029fc15ba8 Mon Sep 17 00:00:00 2001
From: Piotr Fusik <p.fusik at samsung.com>
Date: Tue, 24 Mar 2026 14:33:06 +0100
Subject: [PATCH 02/15] [InstCombine] Combine `A + C + ~B --> A - B + (C-1)`

Example:

    int foo(int a, int b) { return a - 1 + ~b; }

Before, on AArch64:

    mvn w8, w1
    add w8, w0, w8
    sub w0, w8, #1

After (matches gcc):

    sub w0, w0, w1
    sub w0, w0, #2
---
 .../InstCombine/InstCombineAddSub.cpp         | 21 ++++++++++++-------
 llvm/test/Transforms/InstCombine/add.ll       | 20 +++++++-----------
 2 files changed, 22 insertions(+), 19 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index c781c6978b275..ae9b66dd5a6f8 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1604,13 +1604,20 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
   if (Value *V = checkForNegativeOperand(I, Builder))
     return replaceInstUsesWith(I, V);
 
-  // (A + 1) + ~B --> A - B
-  // ~B + (A + 1) --> A - B
-  // (~B + A) + 1 --> A - B
-  // (A + ~B) + 1 --> A - B
-  if (match(&I, m_c_BinOp(m_Add(m_Value(A), m_One()), m_Not(m_Value(B)))) ||
-      match(&I, m_BinOp(m_c_Add(m_Not(m_Value(B)), m_Value(A)), m_One())))
-    return BinaryOperator::CreateSub(A, B);
+  {
+    // (A + C) + ~B --> A - B + (C-1)
+    // ~B + (A + C) --> A - B + (C-1)
+    // (~B + A) + C --> A - B + (C-1)
+    // (A + ~B) + C --> A - B + (C-1)
+    const APInt *C;
+    if (match(&I,
+              m_c_BinOp(m_Add(m_Value(A), m_APInt(C)), m_Not(m_Value(B)))) ||
+        match(&I,
+              m_BinOp(m_c_Add(m_Not(m_Value(B)), m_Value(A)), m_APInt(C)))) {
+      Value *Sub = Builder.CreateSub(A, B);
+      return BinaryOperator::CreateAdd(Sub, ConstantInt::get(Ty, *C - 1));
+    }
+  }
 
   // (A + RHS) + RHS --> A + (RHS << 1)
   if (match(LHS, m_OneUse(m_c_Add(m_Value(A), m_Specific(RHS)))))
diff --git a/llvm/test/Transforms/InstCombine/add.ll b/llvm/test/Transforms/InstCombine/add.ll
index 8bb50ae0fc381..1006831fc1e0f 100644
--- a/llvm/test/Transforms/InstCombine/add.ll
+++ b/llvm/test/Transforms/InstCombine/add.ll
@@ -1196,9 +1196,8 @@ define i32 @add_to_sub2(i32 %A, i32 %M) {
 
 define i32 @add_not_add_m1(i32 %a, i32 %b) {
 ; CHECK-LABEL: @add_not_add_m1(
-; CHECK-NEXT:    [[NOT:%.*]] = xor i32 [[B:%.*]], -1
-; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[A:%.*]], -1
-; CHECK-NEXT:    [[R:%.*]] = add i32 [[ADD]], [[NOT]]
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[TMP1]], -2
 ; CHECK-NEXT:    ret i32 [[R]]
 ;
   %not = xor i32 %b, -1
@@ -1209,9 +1208,8 @@ define i32 @add_not_add_m1(i32 %a, i32 %b) {
 
 define <2 x i32> @add_not_add_m1_vec(<2 x i32> %a, <2 x i32> %b) {
 ; CHECK-LABEL: @add_not_add_m1_vec(
-; CHECK-NEXT:    [[NOT:%.*]] = xor <2 x i32> [[B:%.*]], splat (i32 -1)
-; CHECK-NEXT:    [[ADD:%.*]] = add <2 x i32> [[A:%.*]], splat (i32 -1)
-; CHECK-NEXT:    [[R:%.*]] = add <2 x i32> [[ADD]], [[NOT]]
+; CHECK-NEXT:    [[TMP1:%.*]] = sub <2 x i32> [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = add <2 x i32> [[TMP1]], splat (i32 -2)
 ; CHECK-NEXT:    ret <2 x i32> [[R]]
 ;
   %not = xor <2 x i32> %b, <i32 -1, i32 -1>
@@ -1222,9 +1220,8 @@ define <2 x i32> @add_not_add_m1_vec(<2 x i32> %a, <2 x i32> %b) {
 
 define i32 @add_not_add_m1_commuted(i32 %a, i32 %b) {
 ; CHECK-LABEL: @add_not_add_m1_commuted(
-; CHECK-NEXT:    [[NOT:%.*]] = xor i32 [[B:%.*]], -1
-; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[A:%.*]], -1
-; CHECK-NEXT:    [[R:%.*]] = add i32 [[ADD]], [[NOT]]
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[TMP1]], -2
 ; CHECK-NEXT:    ret i32 [[R]]
 ;
   %not = xor i32 %b, -1
@@ -1235,9 +1232,8 @@ define i32 @add_not_add_m1_commuted(i32 %a, i32 %b) {
 
 define i32 @add_not_add_m1_assoc(i32 %a, i32 %b) {
 ; CHECK-LABEL: @add_not_add_m1_assoc(
-; CHECK-NEXT:    [[NOT:%.*]] = xor i32 [[B:%.*]], -1
-; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[A:%.*]], [[NOT]]
-; CHECK-NEXT:    [[R:%.*]] = add i32 [[ADD]], -1
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[TMP1]], -2
 ; CHECK-NEXT:    ret i32 [[R]]
 ;
   %not = xor i32 %b, -1

>From 4ac15736af7ece889ec7069e538e5cf2b6c9242c Mon Sep 17 00:00:00 2001
From: Piotr Fusik <p.fusik at samsung.com>
Date: Tue, 24 Mar 2026 17:24:23 +0100
Subject: [PATCH 03/15] [InstCombine][test] Update
 fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll

---
 ...old-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
index d16f36927d71a..ded85cde7590a 100644
--- a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
+++ b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
@@ -49,7 +49,9 @@ define <4 x i32> @t2_vec_poison0(<4 x i32> %x, <4 x i32> %y) {
 
 define <4 x i32> @t3_vec_poison1(<4 x i32> %x, <4 x i32> %y) {
 ; CHECK-LABEL: @t3_vec_poison1(
-; CHECK-NEXT:    [[T2:%.*]] = sub <4 x i32> [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[T0:%.*]] = xor <4 x i32> [[X:%.*]], splat (i32 -1)
+; CHECK-NEXT:    [[T1:%.*]] = add <4 x i32> [[Y:%.*]], [[T0]]
+; CHECK-NEXT:    [[T2:%.*]] = add <4 x i32> [[T1]], <i32 1, i32 1, i32 poison, i32 1>
 ; CHECK-NEXT:    ret <4 x i32> [[T2]]
 ;
   %t0 = xor <4 x i32> %x, <i32 -1, i32 -1, i32 -1, i32 -1>
@@ -60,7 +62,9 @@ define <4 x i32> @t3_vec_poison1(<4 x i32> %x, <4 x i32> %y) {
 
 define <4 x i32> @t4_vec_poison2(<4 x i32> %x, <4 x i32> %y) {
 ; CHECK-LABEL: @t4_vec_poison2(
-; CHECK-NEXT:    [[T2:%.*]] = sub <4 x i32> [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[T0:%.*]] = xor <4 x i32> [[X:%.*]], <i32 -1, i32 -1, i32 poison, i32 -1>
+; CHECK-NEXT:    [[T1:%.*]] = add <4 x i32> [[Y:%.*]], [[T0]]
+; CHECK-NEXT:    [[T2:%.*]] = add <4 x i32> [[T1]], <i32 1, i32 1, i32 poison, i32 1>
 ; CHECK-NEXT:    ret <4 x i32> [[T2]]
 ;
   %t0 = xor <4 x i32> %x, <i32 -1, i32 -1, i32 poison, i32 -1>
@@ -201,9 +205,8 @@ define i32 @n11(i32 %x, i32 %y) {
 
 define i32 @n12(i32 %x, i32 %y) {
 ; CHECK-LABEL: @n12(
-; CHECK-NEXT:    [[T0:%.*]] = xor i32 [[X:%.*]], -1
-; CHECK-NEXT:    [[T1:%.*]] = add i32 [[Y:%.*]], [[T0]]
-; CHECK-NEXT:    [[T2:%.*]] = add i32 [[T1]], 2
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[T2:%.*]] = add i32 [[TMP1]], 1
 ; CHECK-NEXT:    ret i32 [[T2]]
 ;
   %t0 = xor i32 %x, -1

>From 820c990b3d457f120edf320acbe1d5158eb65876 Mon Sep 17 00:00:00 2001
From: Piotr Fusik <p.fusik at samsung.com>
Date: Wed, 25 Mar 2026 07:48:28 +0100
Subject: [PATCH 04/15] [InstCombine] Allow poison `C` vector elements

---
 llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp     | 8 ++++----
 .../fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll     | 8 ++------
 2 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index ae9b66dd5a6f8..8140c5aca879c 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1610,10 +1610,10 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
     // (~B + A) + C --> A - B + (C-1)
     // (A + ~B) + C --> A - B + (C-1)
     const APInt *C;
-    if (match(&I,
-              m_c_BinOp(m_Add(m_Value(A), m_APInt(C)), m_Not(m_Value(B)))) ||
-        match(&I,
-              m_BinOp(m_c_Add(m_Not(m_Value(B)), m_Value(A)), m_APInt(C)))) {
+    if (match(&I, m_c_BinOp(m_Add(m_Value(A), m_APIntAllowPoison(C)),
+                            m_Not(m_Value(B)))) ||
+        match(&I, m_BinOp(m_c_Add(m_Not(m_Value(B)), m_Value(A)),
+                          m_APIntAllowPoison(C)))) {
       Value *Sub = Builder.CreateSub(A, B);
       return BinaryOperator::CreateAdd(Sub, ConstantInt::get(Ty, *C - 1));
     }
diff --git a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
index ded85cde7590a..a4401b2827e34 100644
--- a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
+++ b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
@@ -49,9 +49,7 @@ define <4 x i32> @t2_vec_poison0(<4 x i32> %x, <4 x i32> %y) {
 
 define <4 x i32> @t3_vec_poison1(<4 x i32> %x, <4 x i32> %y) {
 ; CHECK-LABEL: @t3_vec_poison1(
-; CHECK-NEXT:    [[T0:%.*]] = xor <4 x i32> [[X:%.*]], splat (i32 -1)
-; CHECK-NEXT:    [[T1:%.*]] = add <4 x i32> [[Y:%.*]], [[T0]]
-; CHECK-NEXT:    [[T2:%.*]] = add <4 x i32> [[T1]], <i32 1, i32 1, i32 poison, i32 1>
+; CHECK-NEXT:    [[T2:%.*]] = sub <4 x i32> [[Y:%.*]], [[X:%.*]]
 ; CHECK-NEXT:    ret <4 x i32> [[T2]]
 ;
   %t0 = xor <4 x i32> %x, <i32 -1, i32 -1, i32 -1, i32 -1>
@@ -62,9 +60,7 @@ define <4 x i32> @t3_vec_poison1(<4 x i32> %x, <4 x i32> %y) {
 
 define <4 x i32> @t4_vec_poison2(<4 x i32> %x, <4 x i32> %y) {
 ; CHECK-LABEL: @t4_vec_poison2(
-; CHECK-NEXT:    [[T0:%.*]] = xor <4 x i32> [[X:%.*]], <i32 -1, i32 -1, i32 poison, i32 -1>
-; CHECK-NEXT:    [[T1:%.*]] = add <4 x i32> [[Y:%.*]], [[T0]]
-; CHECK-NEXT:    [[T2:%.*]] = add <4 x i32> [[T1]], <i32 1, i32 1, i32 poison, i32 1>
+; CHECK-NEXT:    [[T2:%.*]] = sub <4 x i32> [[Y:%.*]], [[X:%.*]]
 ; CHECK-NEXT:    ret <4 x i32> [[T2]]
 ;
   %t0 = xor <4 x i32> %x, <i32 -1, i32 -1, i32 poison, i32 -1>

>From 4a93f2517ee562702c32e63c0c0f378f4c0b03e5 Mon Sep 17 00:00:00 2001
From: Piotr Fusik <p.fusik at samsung.com>
Date: Wed, 25 Mar 2026 07:53:39 +0100
Subject: [PATCH 05/15] [InstCombine][test] Move tests to
 fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll

---
 llvm/test/Transforms/InstCombine/add.ll       | 61 -------------------
 ...c-of-add-of-not-x-and-y-to-sub-x-from-y.ll | 61 +++++++++++++++++++
 2 files changed, 61 insertions(+), 61 deletions(-)

diff --git a/llvm/test/Transforms/InstCombine/add.ll b/llvm/test/Transforms/InstCombine/add.ll
index 1006831fc1e0f..aa68dfb540064 100644
--- a/llvm/test/Transforms/InstCombine/add.ll
+++ b/llvm/test/Transforms/InstCombine/add.ll
@@ -1194,67 +1194,6 @@ define i32 @add_to_sub2(i32 %A, i32 %M) {
   ret i32 %E
 }
 
-define i32 @add_not_add_m1(i32 %a, i32 %b) {
-; CHECK-LABEL: @add_not_add_m1(
-; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT:    [[R:%.*]] = add i32 [[TMP1]], -2
-; CHECK-NEXT:    ret i32 [[R]]
-;
-  %not = xor i32 %b, -1
-  %add = add i32 %a, -1
-  %r = add i32 %add, %not
-  ret i32 %r
-}
-
-define <2 x i32> @add_not_add_m1_vec(<2 x i32> %a, <2 x i32> %b) {
-; CHECK-LABEL: @add_not_add_m1_vec(
-; CHECK-NEXT:    [[TMP1:%.*]] = sub <2 x i32> [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT:    [[R:%.*]] = add <2 x i32> [[TMP1]], splat (i32 -2)
-; CHECK-NEXT:    ret <2 x i32> [[R]]
-;
-  %not = xor <2 x i32> %b, <i32 -1, i32 -1>
-  %add = add <2 x i32> %a, <i32 -1, i32 -1>
-  %r = add <2 x i32> %add, %not
-  ret <2 x i32> %r
-}
-
-define i32 @add_not_add_m1_commuted(i32 %a, i32 %b) {
-; CHECK-LABEL: @add_not_add_m1_commuted(
-; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT:    [[R:%.*]] = add i32 [[TMP1]], -2
-; CHECK-NEXT:    ret i32 [[R]]
-;
-  %not = xor i32 %b, -1
-  %add = add i32 %a, -1
-  %r = add i32 %not, %add
-  ret i32 %r
-}
-
-define i32 @add_not_add_m1_assoc(i32 %a, i32 %b) {
-; CHECK-LABEL: @add_not_add_m1_assoc(
-; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT:    [[R:%.*]] = add i32 [[TMP1]], -2
-; CHECK-NEXT:    ret i32 [[R]]
-;
-  %not = xor i32 %b, -1
-  %add = add i32 %not, %a
-  %r = add i32 %add, -1
-  ret i32 %r
-}
-
-define i32 @add_not_add_intmin(i32 %a, i32 %b) {
-; CHECK-LABEL: @add_not_add_intmin(
-; CHECK-NEXT:    [[NOT:%.*]] = xor i32 [[B:%.*]], -1
-; CHECK-NEXT:    [[ADD:%.*]] = xor i32 [[A:%.*]], -2147483648
-; CHECK-NEXT:    [[R:%.*]] = add i32 [[ADD]], [[NOT]]
-; CHECK-NEXT:    ret i32 [[R]]
-;
-  %not = xor i32 %b, -1
-  %add = add i32 %a, -2147483648
-  %r = add i32 %add, %not
-  ret i32 %r
-}
-
 ; (X | C1) + C2 --> (X | C1) ^ C1 iff (C1 == -C2)
 define i32 @test44(i32 %A) {
 ; CHECK-LABEL: @test44(
diff --git a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
index a4401b2827e34..9d499c467978a 100644
--- a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
+++ b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
@@ -21,6 +21,31 @@ define i32 @t0(i32 %x, i32 %y) {
   ret i32 %t2
 }
 
+define i32 @add_not_add_m1(i32 %a, i32 %b) {
+; CHECK-LABEL: @add_not_add_m1(
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[TMP1]], -2
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %not = xor i32 %b, -1
+  %add = add i32 %a, -1
+  %r = add i32 %add, %not
+  ret i32 %r
+}
+
+define i32 @add_not_add_intmin(i32 %a, i32 %b) {
+; CHECK-LABEL: @add_not_add_intmin(
+; CHECK-NEXT:    [[NOT:%.*]] = xor i32 [[B:%.*]], -1
+; CHECK-NEXT:    [[ADD:%.*]] = xor i32 [[A:%.*]], -2147483648
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[ADD]], [[NOT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %not = xor i32 %b, -1
+  %add = add i32 %a, -2147483648
+  %r = add i32 %add, %not
+  ret i32 %r
+}
+
 ;------------------------------------------------------------------------------;
 ; Vector tests
 ;------------------------------------------------------------------------------;
@@ -69,6 +94,18 @@ define <4 x i32> @t4_vec_poison2(<4 x i32> %x, <4 x i32> %y) {
   ret <4 x i32> %t2
 }
 
+define <2 x i32> @add_not_add_m1_vec(<2 x i32> %a, <2 x i32> %b) {
+; CHECK-LABEL: @add_not_add_m1_vec(
+; CHECK-NEXT:    [[TMP1:%.*]] = sub <2 x i32> [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = add <2 x i32> [[TMP1]], splat (i32 -2)
+; CHECK-NEXT:    ret <2 x i32> [[R]]
+;
+  %not = xor <2 x i32> %b, splat (i32 -1)
+  %add = add <2 x i32> %a, splat (i32 -1)
+  %r = add <2 x i32> %add, %not
+  ret <2 x i32> %r
+}
+
 ;------------------------------------------------------------------------------;
 ; One-use tests
 ;------------------------------------------------------------------------------;
@@ -182,6 +219,30 @@ define i32 @t10_commutative2(i32 %x) {
   ret i32 %t2
 }
 
+define i32 @add_not_add_m1_commuted(i32 %a, i32 %b) {
+; CHECK-LABEL: @add_not_add_m1_commuted(
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[TMP1]], -2
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %not = xor i32 %b, -1
+  %add = add i32 %a, -1
+  %r = add i32 %not, %add
+  ret i32 %r
+}
+
+define i32 @add_not_add_m1_assoc(i32 %a, i32 %b) {
+; CHECK-LABEL: @add_not_add_m1_assoc(
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[TMP1]], -2
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %not = xor i32 %b, -1
+  %add = add i32 %not, %a
+  %r = add i32 %add, -1
+  ret i32 %r
+}
+
 ;------------------------------------------------------------------------------;
 ; Basic negative tests
 ;------------------------------------------------------------------------------;

>From 16887639afd69be8b128b114dfc975ccdd22855c Mon Sep 17 00:00:00 2001
From: Piotr Fusik <p.fusik at samsung.com>
Date: Wed, 25 Mar 2026 07:57:08 +0100
Subject: [PATCH 06/15] [InstCombine][test] Test `C!=1` with poison vector
 element

---
 ...fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
index 9d499c467978a..c8fd46473f50d 100644
--- a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
+++ b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
@@ -106,6 +106,18 @@ define <2 x i32> @add_not_add_m1_vec(<2 x i32> %a, <2 x i32> %b) {
   ret <2 x i32> %r
 }
 
+define <2 x i32> @add_not_add_m1_vec_poison(<2 x i32> %a, <2 x i32> %b) {
+; CHECK-LABEL: @add_not_add_m1_vec_poison(
+; CHECK-NEXT:    [[TMP1:%.*]] = sub <2 x i32> [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = add <2 x i32> [[TMP1]], splat (i32 -2)
+; CHECK-NEXT:    ret <2 x i32> [[R]]
+;
+  %not = xor <2 x i32> %b, splat (i32 -1)
+  %add = add <2 x i32> %a, <i32 -1, i32 poison>
+  %r = add <2 x i32> %add, %not
+  ret <2 x i32> %r
+}
+
 ;------------------------------------------------------------------------------;
 ; One-use tests
 ;------------------------------------------------------------------------------;

>From fb63b3289e075b616b81986e6de4aa6c0d0dc2c1 Mon Sep 17 00:00:00 2001
From: Piotr Fusik <p.fusik at samsung.com>
Date: Wed, 25 Mar 2026 08:01:27 +0100
Subject: [PATCH 07/15] [InstCombine][test] Test is no longer negative

---
 ...c-of-add-of-not-x-and-y-to-sub-x-from-y.ll | 24 +++++++++----------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
index c8fd46473f50d..ded9b3f2a7139 100644
--- a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
+++ b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
@@ -21,6 +21,18 @@ define i32 @t0(i32 %x, i32 %y) {
   ret i32 %t2
 }
 
+define i32 @t12(i32 %x, i32 %y) {
+; CHECK-LABEL: @t12(
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[T2:%.*]] = add i32 [[TMP1]], 1
+; CHECK-NEXT:    ret i32 [[T2]]
+;
+  %t0 = xor i32 %x, -1
+  %t1 = add i32 %t0, %y
+  %t2 = add i32 %t1, 2
+  ret i32 %t2
+}
+
 define i32 @add_not_add_m1(i32 %a, i32 %b) {
 ; CHECK-LABEL: @add_not_add_m1(
 ; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[A:%.*]], [[B:%.*]]
@@ -271,15 +283,3 @@ define i32 @n11(i32 %x, i32 %y) {
   %t2 = add i32 %t1, 1
   ret i32 %t2
 }
-
-define i32 @n12(i32 %x, i32 %y) {
-; CHECK-LABEL: @n12(
-; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[Y:%.*]], [[X:%.*]]
-; CHECK-NEXT:    [[T2:%.*]] = add i32 [[TMP1]], 1
-; CHECK-NEXT:    ret i32 [[T2]]
-;
-  %t0 = xor i32 %x, -1
-  %t1 = add i32 %t0, %y
-  %t2 = add i32 %t1, 2 ; not +1
-  ret i32 %t2
-}

>From 0d1e3d2e9bc2f80f0ba563dc912a9f9e30f79826 Mon Sep 17 00:00:00 2001
From: Piotr Fusik <p.fusik at samsung.com>
Date: Wed, 8 Apr 2026 17:15:58 +0200
Subject: [PATCH 08/15] [InstCombine][NFC] Comment the transform

---
 llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 8140c5aca879c..cfcf6fc42af70 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1609,6 +1609,10 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
     // ~B + (A + C) --> A - B + (C-1)
     // (~B + A) + C --> A - B + (C-1)
     // (A + ~B) + C --> A - B + (C-1)
+    // This relies on the ~B == -1-B identity.
+    // With constant C, subtraction of one is free, so we replace three ops
+    // (two adds and a bitwise-not) with two (sub and add)
+    // or even one (just sub for the C==1 special case).
     const APInt *C;
     if (match(&I, m_c_BinOp(m_Add(m_Value(A), m_APIntAllowPoison(C)),
                             m_Not(m_Value(B)))) ||

>From 44848f9119c380c5d10822aa19cefc814c6ea1f0 Mon Sep 17 00:00:00 2001
From: Piotr Fusik <p.fusik at samsung.com>
Date: Wed, 8 Apr 2026 17:45:36 +0200
Subject: [PATCH 09/15] [InstCombine][test] Update file summary

---
 .../fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll         | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
index ded9b3f2a7139..134e4c442b5de 100644
--- a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
+++ b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
@@ -2,9 +2,9 @@
 ; RUN: opt -passes=instcombine -S < %s | FileCheck %s
 
 ; Given:
-;   add (add (xor %x, -1), %y), 1
+;   add (add (xor %x, -1), %y), C
 ; Transform it to:
-;   sub %y, %x
+;   add (sub %y, %x), C-1
 
 ;------------------------------------------------------------------------------;
 ; Scalar tests

>From 4a0ad1d0be7d230c09c49f8478d2a72f41e4ff2a Mon Sep 17 00:00:00 2001
From: Piotr Fusik <p.fusik at samsung.com>
Date: Wed, 8 Apr 2026 17:45:48 +0200
Subject: [PATCH 10/15] [InstCombine][test] Add multi-use tests

---
 ...c-of-add-of-not-x-and-y-to-sub-x-from-y.ll | 49 +++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
index 134e4c442b5de..a4834899fb327 100644
--- a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
+++ b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
@@ -182,6 +182,55 @@ define i32 @t7(i32 %x, i32 %y) {
   ret i32 %t2
 }
 
+define i32 @t5_5(i32 %x, i32 %y) {
+; CHECK-LABEL: @t5_5(
+; CHECK-NEXT:    [[T0:%.*]] = xor i32 [[X:%.*]], -1
+; CHECK-NEXT:    call void @use32(i32 [[T0]])
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[Y:%.*]], [[X]]
+; CHECK-NEXT:    [[T2:%.*]] = add i32 [[TMP1]], 4
+; CHECK-NEXT:    ret i32 [[T2]]
+;
+  %t0 = xor i32 %x, -1
+  call void @use32(i32 %t0)
+  %t1 = add i32 %t0, %y
+  %t2 = add i32 %t1, 5
+  ret i32 %t2
+}
+
+define i32 @t6_5(i32 %x, i32 %y) {
+; CHECK-LABEL: @t6_5(
+; CHECK-NEXT:    [[T0:%.*]] = xor i32 [[X:%.*]], -1
+; CHECK-NEXT:    [[T1:%.*]] = add i32 [[Y:%.*]], [[T0]]
+; CHECK-NEXT:    call void @use32(i32 [[T1]])
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[Y]], [[X]]
+; CHECK-NEXT:    [[T2:%.*]] = add i32 [[TMP1]], 4
+; CHECK-NEXT:    ret i32 [[T2]]
+;
+  %t0 = xor i32 %x, -1
+  %t1 = add i32 %t0, %y
+  call void @use32(i32 %t1)
+  %t2 = add i32 %t1, 5
+  ret i32 %t2
+}
+
+define i32 @t7_5(i32 %x, i32 %y) {
+; CHECK-LABEL: @t7_5(
+; CHECK-NEXT:    [[T0:%.*]] = xor i32 [[X:%.*]], -1
+; CHECK-NEXT:    call void @use32(i32 [[T0]])
+; CHECK-NEXT:    [[T1:%.*]] = add i32 [[Y:%.*]], [[T0]]
+; CHECK-NEXT:    call void @use32(i32 [[T1]])
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[Y]], [[X]]
+; CHECK-NEXT:    [[T2:%.*]] = add i32 [[TMP1]], 4
+; CHECK-NEXT:    ret i32 [[T2]]
+;
+  %t0 = xor i32 %x, -1
+  call void @use32(i32 %t0)
+  %t1 = add i32 %t0, %y
+  call void @use32(i32 %t1)
+  %t2 = add i32 %t1, 5
+  ret i32 %t2
+}
+
 ;------------------------------------------------------------------------------;
 ; Commutativity
 ;------------------------------------------------------------------------------;

>From 073520c648ad522a0df741f69a8098fca0b8b896 Mon Sep 17 00:00:00 2001
From: Piotr Fusik <p.fusik at samsung.com>
Date: Wed, 8 Apr 2026 17:52:11 +0200
Subject: [PATCH 11/15] [InstCombine] Fix multi-use pessimization

---
 .../lib/Transforms/InstCombine/InstCombineAddSub.cpp |  2 +-
 ...fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll | 12 +++++-------
 2 files changed, 6 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index cfcf6fc42af70..23e33330b5103 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1616,7 +1616,7 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
     const APInt *C;
     if (match(&I, m_c_BinOp(m_Add(m_Value(A), m_APIntAllowPoison(C)),
                             m_Not(m_Value(B)))) ||
-        match(&I, m_BinOp(m_c_Add(m_Not(m_Value(B)), m_Value(A)),
+        match(&I, m_BinOp(m_OneUse(m_c_Add(m_Not(m_Value(B)), m_Value(A))),
                           m_APIntAllowPoison(C)))) {
       Value *Sub = Builder.CreateSub(A, B);
       return BinaryOperator::CreateAdd(Sub, ConstantInt::get(Ty, *C - 1));
diff --git a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
index a4834899fb327..93f60332191a7 100644
--- a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
+++ b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
@@ -155,7 +155,7 @@ define i32 @t6(i32 %x, i32 %y) {
 ; CHECK-NEXT:    [[T0:%.*]] = xor i32 [[X:%.*]], -1
 ; CHECK-NEXT:    [[T1:%.*]] = add i32 [[Y:%.*]], [[T0]]
 ; CHECK-NEXT:    call void @use32(i32 [[T1]])
-; CHECK-NEXT:    [[T2:%.*]] = sub i32 [[Y]], [[X]]
+; CHECK-NEXT:    [[T2:%.*]] = add i32 [[T1]], 1
 ; CHECK-NEXT:    ret i32 [[T2]]
 ;
   %t0 = xor i32 %x, -1
@@ -171,7 +171,7 @@ define i32 @t7(i32 %x, i32 %y) {
 ; CHECK-NEXT:    call void @use32(i32 [[T0]])
 ; CHECK-NEXT:    [[T1:%.*]] = add i32 [[Y:%.*]], [[T0]]
 ; CHECK-NEXT:    call void @use32(i32 [[T1]])
-; CHECK-NEXT:    [[T2:%.*]] = sub i32 [[Y]], [[X]]
+; CHECK-NEXT:    [[T2:%.*]] = add i32 [[T1]], 1
 ; CHECK-NEXT:    ret i32 [[T2]]
 ;
   %t0 = xor i32 %x, -1
@@ -202,8 +202,7 @@ define i32 @t6_5(i32 %x, i32 %y) {
 ; CHECK-NEXT:    [[T0:%.*]] = xor i32 [[X:%.*]], -1
 ; CHECK-NEXT:    [[T1:%.*]] = add i32 [[Y:%.*]], [[T0]]
 ; CHECK-NEXT:    call void @use32(i32 [[T1]])
-; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[Y]], [[X]]
-; CHECK-NEXT:    [[T2:%.*]] = add i32 [[TMP1]], 4
+; CHECK-NEXT:    [[T2:%.*]] = add i32 [[T1]], 5
 ; CHECK-NEXT:    ret i32 [[T2]]
 ;
   %t0 = xor i32 %x, -1
@@ -219,8 +218,7 @@ define i32 @t7_5(i32 %x, i32 %y) {
 ; CHECK-NEXT:    call void @use32(i32 [[T0]])
 ; CHECK-NEXT:    [[T1:%.*]] = add i32 [[Y:%.*]], [[T0]]
 ; CHECK-NEXT:    call void @use32(i32 [[T1]])
-; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[Y]], [[X]]
-; CHECK-NEXT:    [[T2:%.*]] = add i32 [[TMP1]], 4
+; CHECK-NEXT:    [[T2:%.*]] = add i32 [[T1]], 5
 ; CHECK-NEXT:    ret i32 [[T2]]
 ;
   %t0 = xor i32 %x, -1
@@ -244,7 +242,7 @@ define i32 @t8_commutative0(i32 %x) {
 ; CHECK-NEXT:    call void @use32(i32 [[T0]])
 ; CHECK-NEXT:    [[T1:%.*]] = add i32 [[Y]], [[T0]]
 ; CHECK-NEXT:    call void @use32(i32 [[T1]])
-; CHECK-NEXT:    [[T2:%.*]] = sub i32 [[Y]], [[X]]
+; CHECK-NEXT:    [[T2:%.*]] = add i32 [[T1]], 1
 ; CHECK-NEXT:    ret i32 [[T2]]
 ;
   %y = call i32 @gen32()

>From af334493994dfebfd54538fbdc7a65624840a575 Mon Sep 17 00:00:00 2001
From: Piotr Fusik <p.fusik at samsung.com>
Date: Thu, 9 Apr 2026 14:45:38 +0200
Subject: [PATCH 12/15] [InstCombine][test] Test multi-use of `A + C`

---
 ...c-of-add-of-not-x-and-y-to-sub-x-from-y.ll | 29 +++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
index 93f60332191a7..32aee2ab946fb 100644
--- a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
+++ b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
@@ -229,6 +229,35 @@ define i32 @t7_5(i32 %x, i32 %y) {
   ret i32 %t2
 }
 
+define i32 @t13(i32 %x, i32 %y) {
+; CHECK-LABEL: @t13(
+; CHECK-NEXT:    [[T0:%.*]] = add i32 [[Y:%.*]], 1
+; CHECK-NEXT:    call void @use32(i32 [[T0]])
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[Y]], [[X:%.*]]
+; CHECK-NEXT:    ret i32 [[TMP1]]
+;
+  %t0 = add i32 %y, 1
+  call void @use32(i32 %t0)
+  %t1 = xor i32 %x, -1
+  %t2 = add i32 %t0, %t1
+  ret i32 %t2
+}
+
+define i32 @t13_5(i32 %x, i32 %y) {
+; CHECK-LABEL: @t13_5(
+; CHECK-NEXT:    [[T0:%.*]] = add i32 [[Y:%.*]], 5
+; CHECK-NEXT:    call void @use32(i32 [[T0]])
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[Y]], [[X:%.*]]
+; CHECK-NEXT:    [[T2:%.*]] = add i32 [[TMP1]], 4
+; CHECK-NEXT:    ret i32 [[T2]]
+;
+  %t0 = add i32 %y, 5
+  call void @use32(i32 %t0)
+  %t1 = xor i32 %x, -1
+  %t2 = add i32 %t0, %t1
+  ret i32 %t2
+}
+
 ;------------------------------------------------------------------------------;
 ; Commutativity
 ;------------------------------------------------------------------------------;

>From c4eddfd422fe7621390eddc1d5fbb83d8db1982c Mon Sep 17 00:00:00 2001
From: Piotr Fusik <p.fusik at samsung.com>
Date: Thu, 9 Apr 2026 15:35:45 +0200
Subject: [PATCH 13/15] [InstCombine][test] Test multi-use of `~B`

---
 ...c-of-add-of-not-x-and-y-to-sub-x-from-y.ll | 64 +++++++++++++++++++
 1 file changed, 64 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
index 32aee2ab946fb..d22760bcbd4cc 100644
--- a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
+++ b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
@@ -258,6 +258,70 @@ define i32 @t13_5(i32 %x, i32 %y) {
   ret i32 %t2
 }
 
+define i32 @t14(i32 %x, i32 %y) {
+; CHECK-LABEL: @t14(
+; CHECK-NEXT:    [[T1:%.*]] = xor i32 [[X:%.*]], -1
+; CHECK-NEXT:    call void @use32(i32 [[T1]])
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[Y:%.*]], [[X]]
+; CHECK-NEXT:    ret i32 [[TMP1]]
+;
+  %t0 = add i32 %y, 1
+  %t1 = xor i32 %x, -1
+  call void @use32(i32 %t1)
+  %t2 = add i32 %t0, %t1
+  ret i32 %t2
+}
+
+define i32 @t14_5(i32 %x, i32 %y) {
+; CHECK-LABEL: @t14_5(
+; CHECK-NEXT:    [[T1:%.*]] = xor i32 [[X:%.*]], -1
+; CHECK-NEXT:    call void @use32(i32 [[T1]])
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[Y:%.*]], [[X]]
+; CHECK-NEXT:    [[T2:%.*]] = add i32 [[TMP1]], 4
+; CHECK-NEXT:    ret i32 [[T2]]
+;
+  %t0 = add i32 %y, 5
+  %t1 = xor i32 %x, -1
+  call void @use32(i32 %t1)
+  %t2 = add i32 %t0, %t1
+  ret i32 %t2
+}
+
+define i32 @t15(i32 %x, i32 %y) {
+; CHECK-LABEL: @t15(
+; CHECK-NEXT:    [[T0:%.*]] = add i32 [[Y:%.*]], 1
+; CHECK-NEXT:    call void @use32(i32 [[T0]])
+; CHECK-NEXT:    [[T1:%.*]] = xor i32 [[X:%.*]], -1
+; CHECK-NEXT:    call void @use32(i32 [[T1]])
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[Y]], [[X]]
+; CHECK-NEXT:    ret i32 [[TMP1]]
+;
+  %t0 = add i32 %y, 1
+  call void @use32(i32 %t0)
+  %t1 = xor i32 %x, -1
+  call void @use32(i32 %t1)
+  %t2 = add i32 %t0, %t1
+  ret i32 %t2
+}
+
+define i32 @t15_5(i32 %x, i32 %y) {
+; CHECK-LABEL: @t15_5(
+; CHECK-NEXT:    [[T0:%.*]] = add i32 [[Y:%.*]], 5
+; CHECK-NEXT:    call void @use32(i32 [[T0]])
+; CHECK-NEXT:    [[T1:%.*]] = xor i32 [[X:%.*]], -1
+; CHECK-NEXT:    call void @use32(i32 [[T1]])
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[Y]], [[X]]
+; CHECK-NEXT:    [[T2:%.*]] = add i32 [[TMP1]], 4
+; CHECK-NEXT:    ret i32 [[T2]]
+;
+  %t0 = add i32 %y, 5
+  call void @use32(i32 %t0)
+  %t1 = xor i32 %x, -1
+  call void @use32(i32 %t1)
+  %t2 = add i32 %t0, %t1
+  ret i32 %t2
+}
+
 ;------------------------------------------------------------------------------;
 ; Commutativity
 ;------------------------------------------------------------------------------;

>From d9d3e6247ad5b43803c43bb7802ace8baaf55b6c Mon Sep 17 00:00:00 2001
From: Piotr Fusik <p.fusik at samsung.com>
Date: Thu, 9 Apr 2026 15:47:56 +0200
Subject: [PATCH 14/15] [InstCombine] Fix pessimization for multi-use of both
 `A + C` and `~B`

---
 llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp        | 4 +++-
 .../fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll        | 5 ++---
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 23e33330b5103..f22a2a7ea487b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1614,8 +1614,10 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
     // (two adds and a bitwise-not) with two (sub and add)
     // or even one (just sub for the C==1 special case).
     const APInt *C;
-    if (match(&I, m_c_BinOp(m_Add(m_Value(A), m_APIntAllowPoison(C)),
+    if (match(&I, m_c_BinOp(m_OneUse(m_Add(m_Value(A), m_APIntAllowPoison(C))),
                             m_Not(m_Value(B)))) ||
+        match(&I, m_c_BinOp(m_Add(m_Value(A), m_APIntAllowPoison(C)),
+                            m_OneUse(m_Not(m_Value(B))))) ||
         match(&I, m_BinOp(m_OneUse(m_c_Add(m_Not(m_Value(B)), m_Value(A))),
                           m_APIntAllowPoison(C)))) {
       Value *Sub = Builder.CreateSub(A, B);
diff --git a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
index d22760bcbd4cc..d85272858e4e2 100644
--- a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
+++ b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
@@ -293,7 +293,7 @@ define i32 @t15(i32 %x, i32 %y) {
 ; CHECK-NEXT:    call void @use32(i32 [[T0]])
 ; CHECK-NEXT:    [[T1:%.*]] = xor i32 [[X:%.*]], -1
 ; CHECK-NEXT:    call void @use32(i32 [[T1]])
-; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[Y]], [[X]]
+; CHECK-NEXT:    [[TMP1:%.*]] = add i32 [[T0]], [[T1]]
 ; CHECK-NEXT:    ret i32 [[TMP1]]
 ;
   %t0 = add i32 %y, 1
@@ -310,8 +310,7 @@ define i32 @t15_5(i32 %x, i32 %y) {
 ; CHECK-NEXT:    call void @use32(i32 [[T0]])
 ; CHECK-NEXT:    [[T1:%.*]] = xor i32 [[X:%.*]], -1
 ; CHECK-NEXT:    call void @use32(i32 [[T1]])
-; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[Y]], [[X]]
-; CHECK-NEXT:    [[T2:%.*]] = add i32 [[TMP1]], 4
+; CHECK-NEXT:    [[T2:%.*]] = add i32 [[T0]], [[T1]]
 ; CHECK-NEXT:    ret i32 [[T2]]
 ;
   %t0 = add i32 %y, 5

>From b3d47556ffe67ca238c6b1f6b84bc832cbdce01b Mon Sep 17 00:00:00 2001
From: Piotr Fusik <p.fusik at samsung.com>
Date: Thu, 9 Apr 2026 15:56:27 +0200
Subject: [PATCH 15/15] [InstCombine] For multi-use `A + C` the transform is
 only profitable with `C == 1`

For `C != 1` it would produce two adds with different constants
which is possibly a pessimization.
---
 llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp        | 5 +++--
 .../fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll        | 4 ++--
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index f22a2a7ea487b..1918c9143f0f7 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1616,8 +1616,9 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
     const APInt *C;
     if (match(&I, m_c_BinOp(m_OneUse(m_Add(m_Value(A), m_APIntAllowPoison(C))),
                             m_Not(m_Value(B)))) ||
-        match(&I, m_c_BinOp(m_Add(m_Value(A), m_APIntAllowPoison(C)),
-                            m_OneUse(m_Not(m_Value(B))))) ||
+        (match(&I, m_c_BinOp(m_Add(m_Value(A), m_APIntAllowPoison(C)),
+                             m_OneUse(m_Not(m_Value(B))))) &&
+         *C == 1) ||
         match(&I, m_BinOp(m_OneUse(m_c_Add(m_Not(m_Value(B)), m_Value(A))),
                           m_APIntAllowPoison(C)))) {
       Value *Sub = Builder.CreateSub(A, B);
diff --git a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
index d85272858e4e2..b0085e1e330a1 100644
--- a/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
+++ b/llvm/test/Transforms/InstCombine/fold-inc-of-add-of-not-x-and-y-to-sub-x-from-y.ll
@@ -247,8 +247,8 @@ define i32 @t13_5(i32 %x, i32 %y) {
 ; CHECK-LABEL: @t13_5(
 ; CHECK-NEXT:    [[T0:%.*]] = add i32 [[Y:%.*]], 5
 ; CHECK-NEXT:    call void @use32(i32 [[T0]])
-; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 [[Y]], [[X:%.*]]
-; CHECK-NEXT:    [[T2:%.*]] = add i32 [[TMP1]], 4
+; CHECK-NEXT:    [[T1:%.*]] = xor i32 [[X:%.*]], -1
+; CHECK-NEXT:    [[T2:%.*]] = add i32 [[T0]], [[T1]]
 ; CHECK-NEXT:    ret i32 [[T2]]
 ;
   %t0 = add i32 %y, 5



More information about the llvm-commits mailing list