[llvm] ca8082f - [InstCombine] Fold ((X + AddC) & Mask) ^ Mask to (~AddC - X) & Mask (#174278)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Feb 13 01:17:04 PST 2026
Author: hanbeom
Date: 2026-02-13T18:17:00+09:00
New Revision: ca8082f3564a3274094aa9eb4c3997142f8df2f9
URL: https://github.com/llvm/llvm-project/commit/ca8082f3564a3274094aa9eb4c3997142f8df2f9
DIFF: https://github.com/llvm/llvm-project/commit/ca8082f3564a3274094aa9eb4c3997142f8df2f9.diff
LOG: [InstCombine] Fold ((X + AddC) & Mask) ^ Mask to (~AddC - X) & Mask (#174278)
This patch optimizes specific pattern.
((X + AddC) & Mask) ^ Mask
-> (~AddC - X) & Mask
Proof: https://alive2.llvm.org/ce/z/XFHfnD
Fixed: https://github.com/llvm/llvm-project/issues/128475
Added:
Modified:
llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
llvm/test/Transforms/InstCombine/and-xor-merge.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index b23519fd9f77f..13118b189dab0 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -5201,6 +5201,27 @@ Instruction *InstCombinerImpl::foldNot(BinaryOperator &I) {
return nullptr;
}
+// ((X + C) & M) ^ M --> (~C − X) & M
+static Instruction *foldMaskedAddXorPattern(BinaryOperator &I,
+ InstCombiner::BuilderTy &Builder) {
+ Value *X, *Mask;
+ Constant *AddC;
+ BinaryOperator *AddInst;
+ if (match(&I,
+ m_Xor(m_OneUse(m_And(m_OneUse(m_CombineAnd(
+ m_BinOp(AddInst),
+ m_Add(m_Value(X), m_ImmConstant(AddC)))),
+ m_Value(Mask))),
+ m_Deferred(Mask)))) {
+ Value *NotC = Builder.CreateNot(AddC);
+ Value *NewSub = Builder.CreateSub(NotC, X, "", AddInst->hasNoUnsignedWrap(),
+ AddInst->hasNoSignedWrap());
+ return BinaryOperator::CreateAnd(NewSub, Mask);
+ }
+
+ return nullptr;
+}
+
// FIXME: We use commutative matchers (m_c_*) for some, but not all, matches
// here. We should standardize that construct where it is needed or choose some
// other way to ensure that commutated variants of patterns are not missed.
@@ -5546,5 +5567,8 @@ Instruction *InstCombinerImpl::visitXor(BinaryOperator &I) {
if (Instruction *Res = foldBitwiseLogicWithIntrinsics(I, Builder))
return Res;
+ if (Instruction *Res = foldMaskedAddXorPattern(I, Builder))
+ return Res;
+
return nullptr;
}
diff --git a/llvm/test/Transforms/InstCombine/and-xor-merge.ll b/llvm/test/Transforms/InstCombine/and-xor-merge.ll
index cf1285cbc11a4..a306fdb4698c6 100644
--- a/llvm/test/Transforms/InstCombine/and-xor-merge.ll
+++ b/llvm/test/Transforms/InstCombine/and-xor-merge.ll
@@ -79,3 +79,145 @@ define i32 @PR75692_3(i32 %x, i32 %y) {
%t4 = and i32 %t2, %t3
ret i32 %t4
}
+
+; ((X + C) & M) ^ M --> (~C − X) & M
+define i8 @add_and_xor_basic(i8 %x) {
+; CHECK-LABEL: @add_and_xor_basic(
+; CHECK-NEXT: [[ADD:%.*]] = sub i8 10, [[X:%.*]]
+; CHECK-NEXT: [[AND:%.*]] = and i8 [[ADD]], 15
+; CHECK-NEXT: ret i8 [[AND]]
+;
+ %add = add i8 %x, 5
+ %and = and i8 %add, 15
+ %xor = xor i8 %and, 15
+ ret i8 %xor
+}
+
+define <4 x i32> @add_and_xor_vector_splat(<4 x i32> %x) {
+; CHECK-LABEL: @add_and_xor_vector_splat(
+; CHECK-NEXT: [[ADD:%.*]] = sub <4 x i32> splat (i32 53), [[X:%.*]]
+; CHECK-NEXT: [[AND:%.*]] = and <4 x i32> [[ADD]], splat (i32 63)
+; CHECK-NEXT: ret <4 x i32> [[AND]]
+;
+ %add = add <4 x i32> %x, <i32 10, i32 10, i32 10, i32 10>
+ %and = and <4 x i32> %add, <i32 63, i32 63, i32 63, i32 63>
+ %xor = xor <4 x i32> %and, <i32 63, i32 63, i32 63, i32 63>
+ ret <4 x i32> %xor
+}
+
+define i32 @add_and_xor_overflow_addc(i32 %x) {
+; CHECK-LABEL: @add_and_xor_overflow_addc(
+; CHECK-NEXT: [[ADD:%.*]] = sub i32 27, [[X:%.*]]
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[ADD]], 31
+; CHECK-NEXT: ret i32 [[AND]]
+;
+ %add = add i32 %x, 100
+ %and = and i32 %add, 31
+ %xor = xor i32 %and, 31
+ ret i32 %xor
+}
+
+define i32 @add_and_xor_negative_addc(i32 %x) {
+; CHECK-LABEL: @add_and_xor_negative_addc(
+; CHECK-NEXT: [[ADD:%.*]] = sub i32 1, [[X:%.*]]
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[ADD]], 255
+; CHECK-NEXT: ret i32 [[AND]]
+;
+ %add = add i32 %x, -2
+ %and = and i32 %add, 255
+ %xor = xor i32 %and, 255
+ ret i32 %xor
+}
+
+define i8 @add_and_xor_not_low_mask(i8 %x) {
+; CHECK-LABEL: @add_and_xor_not_low_mask(
+; CHECK-NEXT: [[TMP1:%.*]] = sub i8 1, [[X:%.*]]
+; CHECK-NEXT: [[XOR:%.*]] = and i8 [[TMP1]], -18
+; CHECK-NEXT: ret i8 [[XOR]]
+;
+ %add = add i8 %x, -2
+ %and = and i8 %add, 238
+ %xor = xor i8 %and, 238
+ ret i8 %xor
+}
+
+define i8 @add_and_xor_not_low_mask2(i8 %x) {
+; CHECK-LABEL: @add_and_xor_not_low_mask2(
+; CHECK-NEXT: [[TMP1:%.*]] = sub i8 1, [[X:%.*]]
+; CHECK-NEXT: [[XOR:%.*]] = and i8 [[TMP1]], 119
+; CHECK-NEXT: ret i8 [[XOR]]
+;
+ %add = add i8 %x, -2
+ %and = and i8 %add, 119
+ %xor = xor i8 %and, 119
+ ret i8 %xor
+}
+
+define i8 @add_and_xor_not_low_mask3(i8 %x) {
+; CHECK-LABEL: @add_and_xor_not_low_mask3(
+; CHECK-NEXT: [[TMP1:%.*]] = sub i8 1, [[X:%.*]]
+; CHECK-NEXT: [[XOR:%.*]] = and i8 [[TMP1]], -91
+; CHECK-NEXT: ret i8 [[XOR]]
+;
+ %add = add i8 %x, -2
+ %and = and i8 %add, 165
+ %xor = xor i8 %and, 165
+ ret i8 %xor
+}
+
+define i8 @add_and_xor_not_low_mask4(i8 %x) {
+; CHECK-LABEL: @add_and_xor_not_low_mask4(
+; CHECK-NEXT: [[TMP1:%.*]] = sub i8 1, [[X:%.*]]
+; CHECK-NEXT: [[XOR:%.*]] = and i8 [[TMP1]], -86
+; CHECK-NEXT: ret i8 [[XOR]]
+;
+ %add = add i8 %x, -2
+ %and = and i8 %add, 170
+ %xor = xor i8 %and, 170
+ ret i8 %xor
+}
+
+; This test is trasformed to 'xor(and(add x, 11), 15), 15)' and being applied.
+define i8 @add_and_xor_sub_op(i8 %x) {
+; CHECK-LABEL: @add_and_xor_sub_op(
+; CHECK-NEXT: [[SUB:%.*]] = sub i8 4, [[X:%.*]]
+; CHECK-NEXT: [[AND:%.*]] = and i8 [[SUB]], 15
+; CHECK-NEXT: ret i8 [[AND]]
+;
+ %sub = sub i8 %x, 5
+ %and = and i8 %sub, 15
+ %xor = xor i8 %and, 15
+ ret i8 %xor
+}
+
+; and_xor_mask negative tests
+
+define i8 @neg_add_and_xor_mask_mismatch(i8 %x) {
+; CHECK-LABEL: @neg_add_and_xor_mask_mismatch(
+; CHECK-NEXT: [[ADD:%.*]] = add i8 [[X:%.*]], 5
+; CHECK-NEXT: [[AND:%.*]] = and i8 [[ADD]], 15
+; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[AND]], 7
+; CHECK-NEXT: ret i8 [[XOR]]
+;
+ %add = add i8 %x, 5
+ %and = and i8 %add, 15
+ %xor = xor i8 %and, 7
+ ret i8 %xor
+}
+
+define i8 @neg_add_and_xor_multi_use(i8 %x) {
+; CHECK-LABEL: @neg_add_and_xor_multi_use(
+; CHECK-NEXT: [[ADD:%.*]] = add i8 [[X:%.*]], 5
+; CHECK-NEXT: call void @use(i8 [[ADD]])
+; CHECK-NEXT: [[AND:%.*]] = and i8 [[ADD]], 15
+; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[AND]], 15
+; CHECK-NEXT: ret i8 [[XOR]]
+;
+ %add = add i8 %x, 5
+ call void @use(i8 %add)
+ %and = and i8 %add, 15
+ %xor = xor i8 %and, 15
+ ret i8 %xor
+}
+
+declare void @use(i8)
More information about the llvm-commits
mailing list