[llvm] [InstSimplify] Fold (X & Y) + ((X & Y) ^ Y) to Y (PR #221920)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 8 01:44:03 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Phong Do (phongndo)
<details>
<summary>Changes</summary>
Issue #<!-- -->221351.
Fold (X & Y) + ((X & Y) ^ Y) to Y: the addends partition the set bits of Y, so their
sum is Y.
Implemented in InstSimplify because the fold returns an existing value without
creating instructions. The shift/ctz variant is out of scope.
## Validation
- Tests cover commuted operands, vectors, flags, and negative cases.
- InstSimplify and InstCombine suites: 2,018 passed, 144 unsupported in my build.
- Alive2 proofs passed for i8, flagged i8, <2 x i4>, and i1, with undef checking
enabled.
- The <2 x i8> proof timed out; reducing the vector element width to i4 succeeded.
## Motivation
The fold partially improves the supplied example, but my bounded real-code scan found
no benefit. This draft is intended to gather broader benchmark evidence before
requesting final review.
---
Full diff: https://github.com/llvm/llvm-project/pull/221920.diff
2 Files Affected:
- (modified) llvm/lib/Analysis/InstructionSimplify.cpp (+7)
- (added) llvm/test/Transforms/InstSimplify/add-xor-and.ll (+121)
``````````diff
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index b8cdd16266948..f68dfd237de99 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
new file mode 100644
index 0000000000000..c474453d39065
--- /dev/null
+++ b/llvm/test/Transforms/InstSimplify/add-xor-and.ll
@@ -0,0 +1,121 @@
+; 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: 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
+ %sum = add i8 %a, %b
+ ret i8 %sum
+}
+
+define i8 @commute_add(i8 %x, i8 %mask) {
+; 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
+ %sum = add i8 %b, %a
+ ret i8 %sum
+}
+
+define i8 @commute_xor(i8 %x, i8 %mask) {
+; 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
+ %sum = add i8 %a, %b
+ ret i8 %sum
+}
+
+define i8 @commute_and(i8 %x, i8 %mask) {
+; 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
+ %sum = add i8 %a, %b
+ ret i8 %sum
+}
+
+define i8 @nowrap(i8 %x, i8 %mask) {
+; 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
+ %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: 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
+ %sum = add <2 x i8> %a, %b
+ ret <2 x i8> %sum
+}
+
+define i1 @boolean(i1 %x, 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
+ %sum = add i1 %a, %b
+ ret i1 %sum
+}
+
+define i8 @different_mask(i8 %x, i8 %mask, i8 %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]]
+;
+ %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: 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: 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: 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]]
+;
+ %a = or i8 %x, %mask
+ %b = xor i8 %a, %mask
+ %sum = add i8 %a, %b
+ ret i8 %sum
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/221920
More information about the llvm-commits
mailing list