[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