[llvm] 579fe10 - [X86] Add DAG combiner for X86ISD::XOR to eliminate redundant XORs (#216134)

via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 14 02:17:17 PDT 2026


Author: AZero13
Date: 2026-08-14T09:17:11Z
New Revision: 579fe105ba8233c373256189c94e53bfa059132c

URL: https://github.com/llvm/llvm-project/commit/579fe105ba8233c373256189c94e53bfa059132c
DIFF: https://github.com/llvm/llvm-project/commit/579fe105ba8233c373256189c94e53bfa059132c.diff

LOG: [X86] Add DAG combiner for X86ISD::XOR to eliminate redundant XORs (#216134)

Replace matching generic `ISD::XOR(LHS, RHS)` and `ISD::XOR(RHS, LHS)` nodes to share matching `X86ISD::XOR` operation.

Added: 
    

Modified: 
    llvm/lib/Target/X86/X86ISelLowering.cpp
    llvm/test/CodeGen/X86/cmp-xor.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 86e67626cdadb..0dc3340a13c73 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -59573,6 +59573,28 @@ static SDValue combineCMP(SDNode *N, SelectionDAG &DAG,
   return Op.getValue(1);
 }
 
+/// If a matching generic opcode node exists, replace its uses with the value
+/// of the target-specific node N.
+static void matchGenericOp(unsigned Opc, SDNode *N, SDValue N0, SDValue N1,
+                           TargetLowering::DAGCombinerInfo &DCI,
+                           bool Negate = false) {
+  SDValue Ops[] = {N0, N1};
+  SelectionDAG &DAG = DCI.DAG;
+  SDVTList VTs = DAG.getVTList(N->getValueType(0));
+  if (SDNode *Generic = DAG.getNodeIfExists(Opc, VTs, Ops)) {
+    SDValue Op(N, 0);
+    if (Negate) {
+      // If the generic node is only used by a node that also uses the
+      // target-specific node N, bailing out prevents potential DAG cycles and
+      // unprofitable replacements.
+      if (Generic->hasOneUse() && Generic->user_begin()->isOnlyUserOf(N))
+        return;
+      Op = DAG.getNegative(Op, SDLoc(N), N->getValueType(0));
+    }
+    DCI.CombineTo(Generic, Op);
+  }
+}
+
 static SDValue combineX86AddSub(SDNode *N, SelectionDAG &DAG,
                                 TargetLowering::DAGCombinerInfo &DCI,
                                 const X86Subtarget &ST) {
@@ -59597,38 +59619,23 @@ static SDValue combineX86AddSub(SDNode *N, SelectionDAG &DAG,
   }
 
   // Fold any similar generic ADD/SUB opcodes to reuse this node.
-  auto MatchGeneric = [&](unsigned Opc, SDValue N0, SDValue N1, bool Negate) {
-    SDValue Ops[] = {N0, N1};
-    SDVTList VTs = DAG.getVTList(N->getValueType(0));
-    if (SDNode *GenericAddSub = DAG.getNodeIfExists(Opc, VTs, Ops)) {
-      SDValue Op(N, 0);
-      if (Negate) {
-        // Bail if this is only used by a user of the x86 add/sub.
-        if (GenericAddSub->hasOneUse() &&
-            GenericAddSub->user_begin()->isOnlyUserOf(N))
-          return;
-        Op = DAG.getNegative(Op, DL, VT);
-      }
-      DCI.CombineTo(GenericAddSub, Op);
-    }
-  };
-  MatchGeneric(GenericOpc, LHS, RHS, false);
-  MatchGeneric(GenericOpc, RHS, LHS, X86ISD::SUB == N->getOpcode());
+  matchGenericOp(GenericOpc, N, LHS, RHS, DCI, false);
+  matchGenericOp(GenericOpc, N, RHS, LHS, DCI, IsSub);
 
   if (auto *Const = dyn_cast<ConstantSDNode>(RHS)) {
     SDValue NegC = DAG.getConstant(-Const->getAPIntValue(), DL, VT);
-    if (X86ISD::SUB == N->getOpcode()) {
+    if (IsSub) {
       // Fold generic add(LHS, -C) to X86ISD::SUB(LHS, C).
-      MatchGeneric(ISD::ADD, LHS, NegC, false);
+      matchGenericOp(ISD::ADD, N, LHS, NegC, DCI, false);
     } else {
       // Negate X86ISD::ADD(LHS, C) and replace generic sub(-C, LHS).
-      MatchGeneric(ISD::SUB, NegC, LHS, true);
+      matchGenericOp(ISD::SUB, N, NegC, LHS, DCI, true);
     }
   } else if (auto *Const = dyn_cast<ConstantSDNode>(LHS)) {
-    if (X86ISD::SUB == N->getOpcode()) {
+    if (IsSub) {
       SDValue NegC = DAG.getConstant(-Const->getAPIntValue(), DL, VT);
       // Negate X86ISD::SUB(C, RHS) and replace generic add(RHS, -C).
-      MatchGeneric(ISD::ADD, RHS, NegC, true);
+      matchGenericOp(ISD::ADD, N, RHS, NegC, DCI, true);
     }
   }
 
@@ -59660,6 +59667,28 @@ static SDValue combineX86AddSub(SDNode *N, SelectionDAG &DAG,
                                    /*ZeroSecondOpOnly*/ true);
 }
 
+static SDValue combineX86XOR(SDNode *N, SelectionDAG &DAG,
+                             TargetLowering::DAGCombinerInfo &DCI) {
+  assert(N->getOpcode() == X86ISD::XOR && "Expected X86ISD::XOR");
+
+  SDLoc DL(N);
+  SDValue LHS = N->getOperand(0);
+  SDValue RHS = N->getOperand(1);
+  MVT VT = LHS.getSimpleValueType();
+
+  // If we don't use the flag result, simplify back to a generic op.
+  if (!N->hasAnyUseOfValue(1)) {
+    SDValue Res = DAG.getNode(ISD::XOR, DL, VT, LHS, RHS);
+    return DAG.getMergeValues({Res, DAG.getConstant(0, DL, MVT::i32)}, DL);
+  }
+
+  // Fold any matching generic nodes to reuse this node.
+  matchGenericOp(ISD::XOR, N, LHS, RHS, DCI);
+  matchGenericOp(ISD::XOR, N, RHS, LHS, DCI);
+
+  return SDValue();
+}
+
 static SDValue combineSBB(SDNode *N, SelectionDAG &DAG) {
   SDValue LHS = N->getOperand(0);
   SDValue RHS = N->getOperand(1);
@@ -63419,6 +63448,7 @@ SDValue X86TargetLowering::PerformDAGCombine(SDNode *N,
   case ISD::SUB:            return combineSub(N, DAG, DCI, Subtarget);
   case X86ISD::ADD:
   case X86ISD::SUB:         return combineX86AddSub(N, DAG, DCI, Subtarget);
+  case X86ISD::XOR:         return combineX86XOR(N, DAG, DCI);
   case ISD::SADDSAT:
   case ISD::SSUBSAT:        return combineToHorizontalAddSub(N, DAG, Subtarget);
   case X86ISD::CLOAD:

diff  --git a/llvm/test/CodeGen/X86/cmp-xor.ll b/llvm/test/CodeGen/X86/cmp-xor.ll
index a8a7da4871ba9..1d15b0b0e3b73 100644
--- a/llvm/test/CodeGen/X86/cmp-xor.ll
+++ b/llvm/test/CodeGen/X86/cmp-xor.ll
@@ -54,3 +54,126 @@ define i32 @cmp_xor_i32_commute(i32 %a, i32 %b, i32 %c)
   ret i32 %sel
 }
 
+define zeroext i1 @xor_cmp_rmw_i32(ptr %x) {
+; X86-LABEL: xor_cmp_rmw_i32:
+; X86:       # %bb.0:
+; X86-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT:    xorl $7, (%eax)
+; X86-NEXT:    setne %al
+; X86-NEXT:    retl
+;
+; X64-LABEL: xor_cmp_rmw_i32:
+; X64:       # %bb.0:
+; X64-NEXT:    xorl $7, (%rdi)
+; X64-NEXT:    setne %al
+; X64-NEXT:    retq
+  %load = load i32, ptr %x
+  %xor = xor i32 %load, 7
+  store i32 %xor, ptr %x
+  %cmp = icmp ne i32 %load, 7
+  ret i1 %cmp
+}
+
+define i32 @xor_cmp_multiuse(i32 %a, i32 %b, ptr %p) {
+; X86-LABEL: xor_cmp_multiuse:
+; X86:       # %bb.0:
+; X86-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT:    xorl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT:    movl %ecx, (%eax)
+; X86-NEXT:    movl $42, %eax
+; X86-NEXT:    je .LBB3_2
+; X86-NEXT:  # %bb.1:
+; X86-NEXT:    movl %ecx, %eax
+; X86-NEXT:  .LBB3_2:
+; X86-NEXT:    retl
+;
+; X64-LABEL: xor_cmp_multiuse:
+; X64:       # %bb.0:
+; X64-NEXT:    xorl %esi, %edi
+; X64-NEXT:    movl %edi, (%rdx)
+; X64-NEXT:    movl $42, %eax
+; X64-NEXT:    cmovnel %edi, %eax
+; X64-NEXT:    retq
+  %xor = xor i32 %a, %b
+  store i32 %xor, ptr %p
+  %cmp = icmp eq i32 %a, %b
+  %sel = select i1 %cmp, i32 42, i32 %xor
+  ret i32 %sel
+}
+
+define zeroext i1 @xor_cmp_rmw_eq_i32(ptr %x) {
+; X86-LABEL: xor_cmp_rmw_eq_i32:
+; X86:       # %bb.0:
+; X86-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT:    xorl $7, (%eax)
+; X86-NEXT:    sete %al
+; X86-NEXT:    retl
+;
+; X64-LABEL: xor_cmp_rmw_eq_i32:
+; X64:       # %bb.0:
+; X64-NEXT:    xorl $7, (%rdi)
+; X64-NEXT:    sete %al
+; X64-NEXT:    retq
+  %load = load i32, ptr %x
+  %xor = xor i32 %load, 7
+  store i32 %xor, ptr %x
+  %cmp = icmp eq i32 %load, 7
+  ret i1 %cmp
+}
+
+define zeroext i1 @xor_cmp_rmw_sgt_i32(ptr %x) {
+; X86-LABEL: xor_cmp_rmw_sgt_i32:
+; X86:       # %bb.0:
+; X86-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT:    movl (%eax), %ecx
+; X86-NEXT:    movl %ecx, %edx
+; X86-NEXT:    xorl $7, %edx
+; X86-NEXT:    movl %edx, (%eax)
+; X86-NEXT:    cmpl $8, %ecx
+; X86-NEXT:    setge %al
+; X86-NEXT:    retl
+;
+; X64-LABEL: xor_cmp_rmw_sgt_i32:
+; X64:       # %bb.0:
+; X64-NEXT:    movl (%rdi), %eax
+; X64-NEXT:    movl %eax, %ecx
+; X64-NEXT:    xorl $7, %ecx
+; X64-NEXT:    movl %ecx, (%rdi)
+; X64-NEXT:    cmpl $8, %eax
+; X64-NEXT:    setge %al
+; X64-NEXT:    retq
+  %load = load i32, ptr %x
+  %xor = xor i32 %load, 7
+  store i32 %xor, ptr %x
+  %cmp = icmp sgt i32 %load, 7
+  ret i1 %cmp
+}
+
+define zeroext i1 @xor_cmp_rmw_ult_i32(ptr %x) {
+; X86-LABEL: xor_cmp_rmw_ult_i32:
+; X86:       # %bb.0:
+; X86-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT:    movl (%eax), %ecx
+; X86-NEXT:    movl %ecx, %edx
+; X86-NEXT:    xorl $7, %edx
+; X86-NEXT:    movl %edx, (%eax)
+; X86-NEXT:    cmpl $7, %ecx
+; X86-NEXT:    setb %al
+; X86-NEXT:    retl
+;
+; X64-LABEL: xor_cmp_rmw_ult_i32:
+; X64:       # %bb.0:
+; X64-NEXT:    movl (%rdi), %eax
+; X64-NEXT:    movl %eax, %ecx
+; X64-NEXT:    xorl $7, %ecx
+; X64-NEXT:    movl %ecx, (%rdi)
+; X64-NEXT:    cmpl $7, %eax
+; X64-NEXT:    setb %al
+; X64-NEXT:    retq
+  %load = load i32, ptr %x
+  %xor = xor i32 %load, 7
+  store i32 %xor, ptr %x
+  %cmp = icmp ult i32 %load, 7
+  ret i1 %cmp
+}


        


More information about the llvm-commits mailing list