[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