[llvm] [InstSimplify] Fold (X & Y) + ((X & Y) ^ Y) to Y (PR #221920)
Phong Do via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 8 01:43:28 PDT 2026
https://github.com/phongndo updated https://github.com/llvm/llvm-project/pull/221920
>From 18ab3b1a168386f0a3b20ab4b099574cc7ff12ca Mon Sep 17 00:00:00 2001
From: phongndo <me at phongndo.com>
Date: Tue, 8 Sep 2026 01:06:12 -0700
Subject: [PATCH 1/2] [InstSimplify] Add tests for addition of complementary
masked bits
---
.../Transforms/InstSimplify/add-xor-and.ll | 129 ++++++++++++++++++
1 file changed, 129 insertions(+)
create mode 100644 llvm/test/Transforms/InstSimplify/add-xor-and.ll
diff --git a/llvm/test/Transforms/InstSimplify/add-xor-and.ll b/llvm/test/Transforms/InstSimplify/add-xor-and.ll
new file mode 100644
index 0000000000000..6e247f469f3dd
--- /dev/null
+++ b/llvm/test/Transforms/InstSimplify/add-xor-and.ll
@@ -0,0 +1,129 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -S -passes=instsimplify < %s | FileCheck %s
+
+define i8 @add_xor_and(i8 %x, i8 %mask) {
+; CHECK-LABEL: @add_xor_and(
+; CHECK-NEXT: [[A:%.*]] = and i8 [[X:%.*]], [[MASK:%.*]]
+; CHECK-NEXT: [[B:%.*]] = xor i8 [[A]], [[MASK]]
+; CHECK-NEXT: [[SUM:%.*]] = add i8 [[A]], [[B]]
+; CHECK-NEXT: ret i8 [[SUM]]
+;
+ %a = and i8 %x, %mask
+ %b = xor i8 %a, %mask
+ %sum = add i8 %a, %b
+ ret i8 %sum
+}
+
+define i8 @commute_add(i8 %x, i8 %mask) {
+; CHECK-LABEL: @commute_add(
+; CHECK-NEXT: [[A:%.*]] = and i8 [[X:%.*]], [[MASK:%.*]]
+; CHECK-NEXT: [[B:%.*]] = xor i8 [[A]], [[MASK]]
+; CHECK-NEXT: [[SUM:%.*]] = add i8 [[B]], [[A]]
+; CHECK-NEXT: ret i8 [[SUM]]
+;
+ %a = and i8 %x, %mask
+ %b = xor i8 %a, %mask
+ %sum = add i8 %b, %a
+ ret i8 %sum
+}
+
+define i8 @commute_xor(i8 %x, i8 %mask) {
+; CHECK-LABEL: @commute_xor(
+; CHECK-NEXT: [[A:%.*]] = and i8 [[X:%.*]], [[MASK:%.*]]
+; CHECK-NEXT: [[B:%.*]] = xor i8 [[MASK]], [[A]]
+; CHECK-NEXT: [[SUM:%.*]] = add i8 [[A]], [[B]]
+; CHECK-NEXT: ret i8 [[SUM]]
+;
+ %a = and i8 %x, %mask
+ %b = xor i8 %mask, %a
+ %sum = add i8 %a, %b
+ ret i8 %sum
+}
+
+define i8 @commute_and(i8 %x, i8 %mask) {
+; CHECK-LABEL: @commute_and(
+; CHECK-NEXT: [[A:%.*]] = and i8 [[MASK:%.*]], [[X:%.*]]
+; CHECK-NEXT: [[B:%.*]] = xor i8 [[A]], [[MASK]]
+; CHECK-NEXT: [[SUM:%.*]] = add i8 [[A]], [[B]]
+; CHECK-NEXT: ret i8 [[SUM]]
+;
+ %a = and i8 %mask, %x
+ %b = xor i8 %a, %mask
+ %sum = add i8 %a, %b
+ ret i8 %sum
+}
+
+define i8 @nowrap(i8 %x, i8 %mask) {
+; CHECK-LABEL: @nowrap(
+; CHECK-NEXT: [[A:%.*]] = and i8 [[X:%.*]], [[MASK:%.*]]
+; CHECK-NEXT: [[B:%.*]] = xor i8 [[A]], [[MASK]]
+; CHECK-NEXT: [[SUM:%.*]] = add nuw nsw i8 [[A]], [[B]]
+; CHECK-NEXT: ret i8 [[SUM]]
+;
+ %a = and i8 %x, %mask
+ %b = xor i8 %a, %mask
+ %sum = add nuw nsw i8 %a, %b
+ ret i8 %sum
+}
+
+define <2 x i8> @vector(<2 x i8> %x, <2 x i8> %mask) {
+; CHECK-LABEL: @vector(
+; CHECK-NEXT: [[A:%.*]] = and <2 x i8> [[X:%.*]], [[MASK:%.*]]
+; CHECK-NEXT: [[B:%.*]] = xor <2 x i8> [[A]], [[MASK]]
+; CHECK-NEXT: [[SUM:%.*]] = add <2 x i8> [[A]], [[B]]
+; CHECK-NEXT: ret <2 x i8> [[SUM]]
+;
+ %a = and <2 x i8> %x, %mask
+ %b = xor <2 x i8> %a, %mask
+ %sum = add <2 x i8> %a, %b
+ ret <2 x i8> %sum
+}
+
+define i1 @boolean(i1 %x, i1 %mask) {
+; CHECK-LABEL: @boolean(
+; CHECK-NEXT: ret i1 [[MASK:%.*]]
+;
+ %a = and i1 %x, %mask
+ %b = xor i1 %a, %mask
+ %sum = add i1 %a, %b
+ ret i1 %sum
+}
+
+define i8 @different_mask(i8 %x, i8 %mask, i8 %other) {
+; CHECK-LABEL: @different_mask(
+; CHECK-NEXT: [[A:%.*]] = and i8 [[X:%.*]], [[MASK:%.*]]
+; CHECK-NEXT: [[B:%.*]] = xor i8 [[A]], [[OTHER:%.*]]
+; CHECK-NEXT: [[SUM:%.*]] = add i8 [[A]], [[B]]
+; CHECK-NEXT: ret i8 [[SUM]]
+;
+ %a = and i8 %x, %mask
+ %b = xor i8 %a, %other
+ %sum = add i8 %a, %b
+ ret i8 %sum
+}
+
+define i8 @different_addend(i8 %x, i8 %mask, i8 %other) {
+; CHECK-LABEL: @different_addend(
+; CHECK-NEXT: [[A:%.*]] = and i8 [[X:%.*]], [[MASK:%.*]]
+; CHECK-NEXT: [[B:%.*]] = xor i8 [[A]], [[MASK]]
+; CHECK-NEXT: [[SUM:%.*]] = add i8 [[OTHER:%.*]], [[B]]
+; CHECK-NEXT: ret i8 [[SUM]]
+;
+ %a = and i8 %x, %mask
+ %b = xor i8 %a, %mask
+ %sum = add i8 %other, %b
+ ret i8 %sum
+}
+
+define i8 @or_instead_of_and(i8 %x, i8 %mask) {
+; CHECK-LABEL: @or_instead_of_and(
+; CHECK-NEXT: [[A:%.*]] = or i8 [[X:%.*]], [[MASK:%.*]]
+; CHECK-NEXT: [[B:%.*]] = xor i8 [[A]], [[MASK]]
+; CHECK-NEXT: [[SUM:%.*]] = add i8 [[A]], [[B]]
+; CHECK-NEXT: ret i8 [[SUM]]
+;
+ %a = or i8 %x, %mask
+ %b = xor i8 %a, %mask
+ %sum = add i8 %a, %b
+ ret i8 %sum
+}
>From 90f7bc8fae2a8b9af3bce107632197299820e8aa Mon Sep 17 00:00:00 2001
From: phongndo <me at phongndo.com>
Date: Tue, 8 Sep 2026 01:10:31 -0700
Subject: [PATCH 2/2] [InstSimplify] Fold (X & Y) + ((X & Y) ^ Y) to Y
---
llvm/lib/Analysis/InstructionSimplify.cpp | 7 ++
.../Transforms/InstSimplify/add-xor-and.ll | 74 +++++++++----------
2 files changed, 40 insertions(+), 41 deletions(-)
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index c76f4d2e9f327..9dbf1c58fbbdb 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -620,6 +620,13 @@ static Value *simplifyAddInst(Value *Op0, Value *Op1, bool IsNSW, bool IsNUW,
match(Op0, m_Sub(m_Value(Y), m_Specific(Op1))))
return Y;
+ // (X & Y) + ((X & Y) ^ Y) -> Y: the addends partition the set bits of Y.
+ if ((match(Op1, m_c_Xor(m_Specific(Op0), m_Value(Y))) &&
+ match(Op0, m_c_And(m_Value(), m_Specific(Y)))) ||
+ (match(Op0, m_c_Xor(m_Specific(Op1), m_Value(Y))) &&
+ match(Op1, m_c_And(m_Value(), m_Specific(Y)))))
+ return Y;
+
// X + ~X -> -1 since ~X = -X-1
Type *Ty = Op0->getType();
if (match(Op0, m_Not(m_Specific(Op1))) || match(Op1, m_Not(m_Specific(Op0))))
diff --git a/llvm/test/Transforms/InstSimplify/add-xor-and.ll b/llvm/test/Transforms/InstSimplify/add-xor-and.ll
index 6e247f469f3dd..c474453d39065 100644
--- a/llvm/test/Transforms/InstSimplify/add-xor-and.ll
+++ b/llvm/test/Transforms/InstSimplify/add-xor-and.ll
@@ -1,12 +1,10 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature
; RUN: opt -S -passes=instsimplify < %s | FileCheck %s
define i8 @add_xor_and(i8 %x, i8 %mask) {
-; CHECK-LABEL: @add_xor_and(
-; CHECK-NEXT: [[A:%.*]] = and i8 [[X:%.*]], [[MASK:%.*]]
-; CHECK-NEXT: [[B:%.*]] = xor i8 [[A]], [[MASK]]
-; CHECK-NEXT: [[SUM:%.*]] = add i8 [[A]], [[B]]
-; CHECK-NEXT: ret i8 [[SUM]]
+; CHECK-LABEL: define {{[^@]+}}@add_xor_and
+; CHECK-SAME: (i8 [[X:%.*]], i8 [[MASK:%.*]]) {
+; CHECK-NEXT: ret i8 [[MASK]]
;
%a = and i8 %x, %mask
%b = xor i8 %a, %mask
@@ -15,11 +13,9 @@ define i8 @add_xor_and(i8 %x, i8 %mask) {
}
define i8 @commute_add(i8 %x, i8 %mask) {
-; CHECK-LABEL: @commute_add(
-; CHECK-NEXT: [[A:%.*]] = and i8 [[X:%.*]], [[MASK:%.*]]
-; CHECK-NEXT: [[B:%.*]] = xor i8 [[A]], [[MASK]]
-; CHECK-NEXT: [[SUM:%.*]] = add i8 [[B]], [[A]]
-; CHECK-NEXT: ret i8 [[SUM]]
+; CHECK-LABEL: define {{[^@]+}}@commute_add
+; CHECK-SAME: (i8 [[X:%.*]], i8 [[MASK:%.*]]) {
+; CHECK-NEXT: ret i8 [[MASK]]
;
%a = and i8 %x, %mask
%b = xor i8 %a, %mask
@@ -28,11 +24,9 @@ define i8 @commute_add(i8 %x, i8 %mask) {
}
define i8 @commute_xor(i8 %x, i8 %mask) {
-; CHECK-LABEL: @commute_xor(
-; CHECK-NEXT: [[A:%.*]] = and i8 [[X:%.*]], [[MASK:%.*]]
-; CHECK-NEXT: [[B:%.*]] = xor i8 [[MASK]], [[A]]
-; CHECK-NEXT: [[SUM:%.*]] = add i8 [[A]], [[B]]
-; CHECK-NEXT: ret i8 [[SUM]]
+; CHECK-LABEL: define {{[^@]+}}@commute_xor
+; CHECK-SAME: (i8 [[X:%.*]], i8 [[MASK:%.*]]) {
+; CHECK-NEXT: ret i8 [[MASK]]
;
%a = and i8 %x, %mask
%b = xor i8 %mask, %a
@@ -41,11 +35,9 @@ define i8 @commute_xor(i8 %x, i8 %mask) {
}
define i8 @commute_and(i8 %x, i8 %mask) {
-; CHECK-LABEL: @commute_and(
-; CHECK-NEXT: [[A:%.*]] = and i8 [[MASK:%.*]], [[X:%.*]]
-; CHECK-NEXT: [[B:%.*]] = xor i8 [[A]], [[MASK]]
-; CHECK-NEXT: [[SUM:%.*]] = add i8 [[A]], [[B]]
-; CHECK-NEXT: ret i8 [[SUM]]
+; CHECK-LABEL: define {{[^@]+}}@commute_and
+; CHECK-SAME: (i8 [[X:%.*]], i8 [[MASK:%.*]]) {
+; CHECK-NEXT: ret i8 [[MASK]]
;
%a = and i8 %mask, %x
%b = xor i8 %a, %mask
@@ -54,11 +46,9 @@ define i8 @commute_and(i8 %x, i8 %mask) {
}
define i8 @nowrap(i8 %x, i8 %mask) {
-; CHECK-LABEL: @nowrap(
-; CHECK-NEXT: [[A:%.*]] = and i8 [[X:%.*]], [[MASK:%.*]]
-; CHECK-NEXT: [[B:%.*]] = xor i8 [[A]], [[MASK]]
-; CHECK-NEXT: [[SUM:%.*]] = add nuw nsw i8 [[A]], [[B]]
-; CHECK-NEXT: ret i8 [[SUM]]
+; CHECK-LABEL: define {{[^@]+}}@nowrap
+; CHECK-SAME: (i8 [[X:%.*]], i8 [[MASK:%.*]]) {
+; CHECK-NEXT: ret i8 [[MASK]]
;
%a = and i8 %x, %mask
%b = xor i8 %a, %mask
@@ -67,11 +57,9 @@ define i8 @nowrap(i8 %x, i8 %mask) {
}
define <2 x i8> @vector(<2 x i8> %x, <2 x i8> %mask) {
-; CHECK-LABEL: @vector(
-; CHECK-NEXT: [[A:%.*]] = and <2 x i8> [[X:%.*]], [[MASK:%.*]]
-; CHECK-NEXT: [[B:%.*]] = xor <2 x i8> [[A]], [[MASK]]
-; CHECK-NEXT: [[SUM:%.*]] = add <2 x i8> [[A]], [[B]]
-; CHECK-NEXT: ret <2 x i8> [[SUM]]
+; CHECK-LABEL: define {{[^@]+}}@vector
+; CHECK-SAME: (<2 x i8> [[X:%.*]], <2 x i8> [[MASK:%.*]]) {
+; CHECK-NEXT: ret <2 x i8> [[MASK]]
;
%a = and <2 x i8> %x, %mask
%b = xor <2 x i8> %a, %mask
@@ -80,8 +68,9 @@ define <2 x i8> @vector(<2 x i8> %x, <2 x i8> %mask) {
}
define i1 @boolean(i1 %x, i1 %mask) {
-; CHECK-LABEL: @boolean(
-; CHECK-NEXT: ret i1 [[MASK:%.*]]
+; CHECK-LABEL: define {{[^@]+}}@boolean
+; CHECK-SAME: (i1 [[X:%.*]], i1 [[MASK:%.*]]) {
+; CHECK-NEXT: ret i1 [[MASK]]
;
%a = and i1 %x, %mask
%b = xor i1 %a, %mask
@@ -90,9 +79,10 @@ define i1 @boolean(i1 %x, i1 %mask) {
}
define i8 @different_mask(i8 %x, i8 %mask, i8 %other) {
-; CHECK-LABEL: @different_mask(
-; CHECK-NEXT: [[A:%.*]] = and i8 [[X:%.*]], [[MASK:%.*]]
-; CHECK-NEXT: [[B:%.*]] = xor i8 [[A]], [[OTHER:%.*]]
+; CHECK-LABEL: define {{[^@]+}}@different_mask
+; CHECK-SAME: (i8 [[X:%.*]], i8 [[MASK:%.*]], i8 [[OTHER:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = and i8 [[X]], [[MASK]]
+; CHECK-NEXT: [[B:%.*]] = xor i8 [[A]], [[OTHER]]
; CHECK-NEXT: [[SUM:%.*]] = add i8 [[A]], [[B]]
; CHECK-NEXT: ret i8 [[SUM]]
;
@@ -103,10 +93,11 @@ define i8 @different_mask(i8 %x, i8 %mask, i8 %other) {
}
define i8 @different_addend(i8 %x, i8 %mask, i8 %other) {
-; CHECK-LABEL: @different_addend(
-; CHECK-NEXT: [[A:%.*]] = and i8 [[X:%.*]], [[MASK:%.*]]
+; CHECK-LABEL: define {{[^@]+}}@different_addend
+; CHECK-SAME: (i8 [[X:%.*]], i8 [[MASK:%.*]], i8 [[OTHER:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = and i8 [[X]], [[MASK]]
; CHECK-NEXT: [[B:%.*]] = xor i8 [[A]], [[MASK]]
-; CHECK-NEXT: [[SUM:%.*]] = add i8 [[OTHER:%.*]], [[B]]
+; CHECK-NEXT: [[SUM:%.*]] = add i8 [[OTHER]], [[B]]
; CHECK-NEXT: ret i8 [[SUM]]
;
%a = and i8 %x, %mask
@@ -116,8 +107,9 @@ define i8 @different_addend(i8 %x, i8 %mask, i8 %other) {
}
define i8 @or_instead_of_and(i8 %x, i8 %mask) {
-; CHECK-LABEL: @or_instead_of_and(
-; CHECK-NEXT: [[A:%.*]] = or i8 [[X:%.*]], [[MASK:%.*]]
+; CHECK-LABEL: define {{[^@]+}}@or_instead_of_and
+; CHECK-SAME: (i8 [[X:%.*]], i8 [[MASK:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = or i8 [[X]], [[MASK]]
; CHECK-NEXT: [[B:%.*]] = xor i8 [[A]], [[MASK]]
; CHECK-NEXT: [[SUM:%.*]] = add i8 [[A]], [[B]]
; CHECK-NEXT: ret i8 [[SUM]]
More information about the llvm-commits
mailing list