[llvm] [X86]: Reassoc demorgan rule for ANDN (PR #163789)

via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 16 07:01:13 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-x86

Author: None (kper)

<details>
<summary>Changes</summary>

The pattern `~(a | b)` should use the `ANDN` instruction when `BMI` is available. We do that by reassociating to `~a & ~b`. The `b` is then wrapped with a `NOT` and `ANDN` already negates the first operand.


Fixes: https://github.com/llvm/llvm-project/issues/163516
Proof: https://alive2.llvm.org/ce/z/vwoZo6

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


2 Files Affected:

- (modified) llvm/lib/Target/X86/X86ISelLowering.cpp (+28) 
- (added) llvm/test/CodeGen/X86/bmi-reassoc-demorgan.ll (+98) 


``````````diff
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index a0b64ff370b10..e2632d114ce0b 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -51651,6 +51651,31 @@ static SDValue combineAndXorSubWithBMI(SDNode *And, const SDLoc &DL,
   return AndN;
 }
 
+// fold (not (or A, B)) -> nand(A, not(B)) if BMI
+static SDValue
+combineReassocDemorganWithNANDWithBMI(SDNode *Xor, const SDLoc &DL,
+                                      SelectionDAG &DAG,
+                                      const X86Subtarget &Subtarget) {
+  using namespace llvm::SDPatternMatch;
+
+  EVT VT = Xor->getValueType(0);
+  // Make sure this node is a candidate for BMI instructions.
+  if (!Subtarget.hasBMI() || (VT != MVT::i32 && VT != MVT::i64))
+    return SDValue();
+
+  SDValue A;
+  SDValue B;
+  APInt Cst;
+  if (!(sd_match(Xor, m_Xor(m_Or(m_Value(A), m_Value(B)), m_ConstInt(Cst))) &&
+        Cst.isAllOnes()))
+    return SDValue();
+
+  auto Opcode =
+      Subtarget.is64Bit() && VT == MVT::i64 ? X86::ANDN64rr : X86::ANDN32rr;
+  auto AndN = DAG.getMachineNode(Opcode, DL, VT, A, DAG.getNOT(DL, B, VT));
+  return SDValue(AndN, 0);
+}
+
 static SDValue combineX86SubCmpForFlags(SDNode *N, SDValue Flag,
                                         SelectionDAG &DAG,
                                         TargetLowering::DAGCombinerInfo &DCI,
@@ -55150,6 +55175,9 @@ static SDValue combineXor(SDNode *N, SelectionDAG &DAG,
   if (SDValue R = combineBMILogicOp(N, DAG, Subtarget))
     return R;
 
+  if (SDValue R = combineReassocDemorganWithNANDWithBMI(N, DL, DAG, Subtarget))
+    return R;
+
   return combineFneg(N, DAG, DCI, Subtarget);
 }
 
diff --git a/llvm/test/CodeGen/X86/bmi-reassoc-demorgan.ll b/llvm/test/CodeGen/X86/bmi-reassoc-demorgan.ll
new file mode 100644
index 0000000000000..ea81d08cd2e6d
--- /dev/null
+++ b/llvm/test/CodeGen/X86/bmi-reassoc-demorgan.ll
@@ -0,0 +1,98 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc < %s -mtriple=i686-unknown-unknown -mattr=+bmi | FileCheck %s --check-prefix=X86
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mattr=+bmi | FileCheck %s --check-prefix=X64
+
+define i32 @reassoc_demorgan_i32(i32 %a, i32 %b) nounwind {
+; X86-LABEL: reassoc_demorgan_i32:
+; X86:       # %bb.0:
+; X86-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT:    notl %ecx
+; X86-NEXT:    andnl %ecx, %eax, %eax
+; X86-NEXT:    retl
+;
+; X64-LABEL: reassoc_demorgan_i32:
+; X64:       # %bb.0:
+; X64-NEXT:    notl %edi
+; X64-NEXT:    andnl %edi, %esi, %eax
+; X64-NEXT:    retq
+  %temp = or i32 %b, %a
+  %res = xor i32 %temp, -1
+  ret i32 %res
+}
+
+define i32 @reassoc_demorgan_three_arguments_i32(i32 %a, i32 %b, i32 %c) nounwind {
+; X86-LABEL: reassoc_demorgan_three_arguments_i32:
+; X86:       # %bb.0:
+; X86-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT:    orl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT:    notl %eax
+; X86-NEXT:    andnl %eax, %ecx, %eax
+; X86-NEXT:    retl
+;
+; X64-LABEL: reassoc_demorgan_three_arguments_i32:
+; X64:       # %bb.0:
+; X64-NEXT:    orl %esi, %edi
+; X64-NEXT:    notl %edx
+; X64-NEXT:    andnl %edx, %edi, %eax
+; X64-NEXT:    retq
+  %and.demorgan = or i32 %b, %a
+  %and3.demorgan = or i32 %and.demorgan, %c
+  %and3 = xor i32 %and3.demorgan, -1
+  ret i32 %and3
+}
+
+define i64 @reassoc_demorgan_i64(i64 %a, i64 %b) nounwind {
+; X86-LABEL: reassoc_demorgan_i64:
+; X86:       # %bb.0:
+; X86-NEXT:    pushl %esi
+; X86-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT:    movl {{[0-9]+}}(%esp), %edx
+; X86-NEXT:    movl {{[0-9]+}}(%esp), %esi
+; X86-NEXT:    notl %edx
+; X86-NEXT:    andnl %edx, %eax, %eax
+; X86-NEXT:    notl %esi
+; X86-NEXT:    andnl %esi, %ecx, %edx
+; X86-NEXT:    popl %esi
+; X86-NEXT:    retl
+;
+; X64-LABEL: reassoc_demorgan_i64:
+; X64:       # %bb.0:
+; X64-NEXT:    notq %rdi
+; X64-NEXT:    andnq %rdi, %rsi, %rax
+; X64-NEXT:    retq
+  %temp = or i64 %b, %a
+  %res = xor i64 %temp, -1
+  ret i64 %res
+}
+
+define i64 @reassoc_demorgan_three_arguments_i64(i64 %a, i64 %b, i64 %c) nounwind {
+; X86-LABEL: reassoc_demorgan_three_arguments_i64:
+; X86:       # %bb.0:
+; X86-NEXT:    pushl %esi
+; X86-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT:    movl {{[0-9]+}}(%esp), %edx
+; X86-NEXT:    movl {{[0-9]+}}(%esp), %esi
+; X86-NEXT:    orl {{[0-9]+}}(%esp), %esi
+; X86-NEXT:    orl {{[0-9]+}}(%esp), %edx
+; X86-NEXT:    notl %eax
+; X86-NEXT:    andnl %eax, %edx, %eax
+; X86-NEXT:    notl %ecx
+; X86-NEXT:    andnl %ecx, %esi, %edx
+; X86-NEXT:    popl %esi
+; X86-NEXT:    retl
+;
+; X64-LABEL: reassoc_demorgan_three_arguments_i64:
+; X64:       # %bb.0:
+; X64-NEXT:    orq %rsi, %rdi
+; X64-NEXT:    notq %rdx
+; X64-NEXT:    andnq %rdx, %rdi, %rax
+; X64-NEXT:    retq
+  %and.demorgan = or i64 %b, %a
+  %and3.demorgan = or i64 %and.demorgan, %c
+  %and3 = xor i64 %and3.demorgan, -1
+  ret i64 %and3
+}

``````````

</details>


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


More information about the llvm-commits mailing list