[llvm] [InstCombine] Reassociate XOR to expose complemented-mask fold (PR #195430)

via llvm-commits llvm-commits at lists.llvm.org
Sat May 2 01:15:21 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: yiiiz (ZY546)

<details>
<summary>Changes</summary>

Reassociate XOR expressions of the form:

(A ^ (X & ~C)) ^ C

into:

((X & ~C) ^ C) ^ A

This exposes the existing demanded-bits fold that simplifies:

(X & ~C) ^ C -> X | C

Fixed: #<!-- -->195019

---
Full diff: https://github.com/llvm/llvm-project/pull/195430.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp (+23) 
- (added) llvm/test/Transforms/InstCombine/xor-reassociate-and-mask.ll (+115) 


``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index d81ed56290476..fdbfa14c39213 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -5631,6 +5631,29 @@ Instruction *InstCombinerImpl::visitXor(BinaryOperator &I) {
   if (Instruction *Abs = canonicalizeAbs(I, Builder))
     return Abs;
 
+  const APInt *AndC, *XorC;
+  // Reassociate to expose the existing demanded-bits fold:
+  //   (A ^ (X & ~C)) ^ C --> ((X & ~C) ^ C) ^ A
+  //   ((X & ~C) ^ C) --> (X | C)
+  if (match(&I,
+            m_c_Xor(m_OneUse(m_c_Xor(m_Value(A, m_Unless(m_ConstantExpr())),
+                                     m_Value(B, m_Unless(m_ConstantExpr())))),
+                    m_APInt(XorC)))) {
+    Value *Masked = B, *Other = A;
+    if (!match(Masked, m_OneUse(m_And(m_Value(X), m_APInt(AndC)))) ||
+        *AndC != ~*XorC) {
+      Masked = A;
+      Other = B;
+      if (!match(Masked, m_OneUse(m_And(m_Value(X), m_APInt(AndC)))) ||
+          *AndC != ~*XorC)
+        Masked = nullptr;
+    }
+    if (Masked)
+      return BinaryOperator::CreateXor(
+          Builder.CreateXor(Masked, ConstantInt::get(I.getType(), *XorC)),
+          Other);
+  }
+
   // Otherwise, if all else failed, try to hoist the xor-by-constant:
   //   (X ^ C) ^ Y --> (X ^ Y) ^ C
   // Just like we do in other places, we completely avoid the fold
diff --git a/llvm/test/Transforms/InstCombine/xor-reassociate-and-mask.ll b/llvm/test/Transforms/InstCombine/xor-reassociate-and-mask.ll
new file mode 100644
index 0000000000000..9ac80f07eb6bd
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/xor-reassociate-and-mask.ll
@@ -0,0 +1,115 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define i32 @basic(i32 %a, i32 %x) {
+; CHECK-LABEL: @basic(
+; CHECK-NEXT:    [[TMP1:%.*]] = or i32 [[X:%.*]], 1
+; CHECK-NEXT:    [[R:%.*]] = xor i32 [[TMP1]], [[A:%.*]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %masked = and i32 %x, -2
+  %xor0 = xor i32 %a, %masked
+  %r = xor i32 %xor0, 1
+  ret i32 %r
+}
+
+define i32 @outer_xor_commuted(i32 %a, i32 %x) {
+; CHECK-LABEL: @outer_xor_commuted(
+; CHECK-NEXT:    [[TMP1:%.*]] = or i32 [[X:%.*]], 1
+; CHECK-NEXT:    [[R:%.*]] = xor i32 [[TMP1]], [[A:%.*]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %masked = and i32 %x, -2
+  %xor0 = xor i32 %a, %masked
+  %r = xor i32 1, %xor0
+  ret i32 %r
+}
+
+define i32 @multi_bit_mask(i32 %a, i32 %x) {
+; CHECK-LABEL: @multi_bit_mask(
+; CHECK-NEXT:    [[TMP1:%.*]] = or i32 [[X:%.*]], 42
+; CHECK-NEXT:    [[R:%.*]] = xor i32 [[TMP1]], [[A:%.*]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %masked = and i32 %x, -43
+  %xor0 = xor i32 %a, %masked
+  %r = xor i32 %xor0, 42
+  ret i32 %r
+}
+
+define i64 @wide_constant(i64 %a, i64 %x) {
+; CHECK-LABEL: @wide_constant(
+; CHECK-NEXT:    [[TMP1:%.*]] = or i64 [[X:%.*]], 4294967296
+; CHECK-NEXT:    [[R:%.*]] = xor i64 [[TMP1]], [[A:%.*]]
+; CHECK-NEXT:    ret i64 [[R]]
+;
+  %masked = and i64 %x, -4294967297
+  %xor0 = xor i64 %a, %masked
+  %r = xor i64 %xor0, 4294967296
+  ret i64 %r
+}
+
+define <2 x i32> @splat_vector(<2 x i32> %a, <2 x i32> %x) {
+; CHECK-LABEL: @splat_vector(
+; CHECK-NEXT:    [[TMP1:%.*]] = or <2 x i32> [[X:%.*]], splat (i32 1)
+; CHECK-NEXT:    [[R:%.*]] = xor <2 x i32> [[TMP1]], [[A:%.*]]
+; CHECK-NEXT:    ret <2 x i32> [[R]]
+;
+  %masked = and <2 x i32> %x, <i32 -2, i32 -2>
+  %xor0 = xor <2 x i32> %a, %masked
+  %r = xor <2 x i32> %xor0, <i32 1, i32 1>
+  ret <2 x i32> %r
+}
+
+define i32 @inner_xor_commuted(i32 %a, i32 %x) {
+; CHECK-LABEL: @inner_xor_commuted(
+; CHECK-NEXT:    [[TMP1:%.*]] = or i32 [[X:%.*]], 1
+; CHECK-NEXT:    [[R:%.*]] = xor i32 [[TMP1]], [[A:%.*]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %masked = and i32 %x, -2
+  %xor0 = xor i32 %masked, %a
+  %r = xor i32 %xor0, 1
+  ret i32 %r
+}
+
+define i32 @extra_use_of_inner_xor_negative(i32 %a, i32 %x, ptr %p) {
+; CHECK-LABEL: @extra_use_of_inner_xor_negative(
+; CHECK-NEXT:    [[MASKED:%.*]] = and i32 [[X:%.*]], -2
+; CHECK-NEXT:    [[XOR0:%.*]] = xor i32 [[A:%.*]], [[MASKED]]
+; CHECK-NEXT:    store i32 [[XOR0]], ptr [[P:%.*]], align 4
+; CHECK-NEXT:    [[R:%.*]] = xor i32 [[XOR0]], 1
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %masked = and i32 %x, -2
+  %xor0 = xor i32 %a, %masked
+  store i32 %xor0, ptr %p, align 4
+  %r = xor i32 %xor0, 1
+  ret i32 %r
+}
+
+define i32 @non_complement_mask_negative(i32 %a, i32 %x) {
+; CHECK-LABEL: @non_complement_mask_negative(
+; CHECK-NEXT:    [[MASKED:%.*]] = and i32 [[X:%.*]], -4
+; CHECK-NEXT:    [[XOR0:%.*]] = xor i32 [[A:%.*]], [[MASKED]]
+; CHECK-NEXT:    [[R:%.*]] = xor i32 [[XOR0]], 1
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %masked = and i32 %x, -4
+  %xor0 = xor i32 %a, %masked
+  %r = xor i32 %xor0, 1
+  ret i32 %r
+}
+
+define i32 @wrong_xor_constant_negative(i32 %a, i32 %x) {
+; CHECK-LABEL: @wrong_xor_constant_negative(
+; CHECK-NEXT:    [[MASKED:%.*]] = and i32 [[X:%.*]], -2
+; CHECK-NEXT:    [[XOR0:%.*]] = xor i32 [[A:%.*]], [[MASKED]]
+; CHECK-NEXT:    [[R:%.*]] = xor i32 [[XOR0]], 2
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %masked = and i32 %x, -2
+  %xor0 = xor i32 %a, %masked
+  %r = xor i32 %xor0, 2
+  ret i32 %r
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/195430


More information about the llvm-commits mailing list