Skip to content

Conversation

@rj-jesus
Copy link
Contributor

@rj-jesus rj-jesus commented Jul 7, 2025

A disjoint OR can be transformed to an EOR.

We already lower EOR+NOT to EON, but not DisjointOR+NOT.

This shows up in scalar NBSL patterns: https://godbolt.org/z/154s8vMMG

https://godbolt.org/z/Efz7jKd6P

@llvmbot
Copy link
Member

llvmbot commented Jul 7, 2025

@llvm/pr-subscribers-llvm-selectiondag

@llvm/pr-subscribers-backend-aarch64

Author: Ricardo Jesus (rj-jesus)

Changes

A disjoint OR can be transformed to an EOR.

We already lower EOR+NOT to EON, but not DisjointOR+NOT.

This shows up in scalar NBSL patterns: https://godbolt.org/z/154s8vMMG

https://godbolt.org/z/Efz7jKd6P


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

2 Files Affected:

  • (modified) llvm/lib/Target/AArch64/AArch64InstrInfo.td (+13-1)
  • (modified) llvm/test/CodeGen/AArch64/eon.ll (+21)
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index 0cb7b02d84a6e..9e289d3826cd0 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -1254,6 +1254,16 @@ def fminnum_nnan : PatFrag<(ops node:$Rn, node:$Rm),
   return N->getFlags().hasNoNaNs();
   }]>;
 
+// Match disjoint or nodes.
+def or_disjoint : PatFrag<(ops node:$lhs, node:$rhs),
+                          (or node:$lhs, node:$rhs), [{
+  return N->getFlags().hasDisjoint();
+}]> {
+  let GISelPredicateCode = [{
+    return mi_match(MI, MRI, m_GDisjointOr(m_Reg(), m_Reg()));
+  }];
+}
+
 //===----------------------------------------------------------------------===//
 
 //===----------------------------------------------------------------------===//
@@ -3118,7 +3128,9 @@ defm AND  : LogicalReg<0b00, 0, "and", and>;
 defm BIC  : LogicalReg<0b00, 1, "bic",
                        BinOpFrag<(and node:$LHS, (not node:$RHS))>, 3>;
 defm EON  : LogicalReg<0b10, 1, "eon",
-                       BinOpFrag<(not (xor node:$LHS, node:$RHS))>>;
+                       PatFrags<(ops node:$LHS, node:$RHS),
+                                [(not (xor node:$LHS, node:$RHS)),
+                                 (not (or_disjoint node:$LHS, node:$RHS))]>>;
 defm EOR  : LogicalReg<0b10, 0, "eor", xor>;
 defm ORN  : LogicalReg<0b01, 1, "orn",
                        BinOpFrag<(or node:$LHS, (not node:$RHS))>>;
diff --git a/llvm/test/CodeGen/AArch64/eon.ll b/llvm/test/CodeGen/AArch64/eon.ll
index 3468a0f63436e..8b31cbfe16b1a 100644
--- a/llvm/test/CodeGen/AArch64/eon.ll
+++ b/llvm/test/CodeGen/AArch64/eon.ll
@@ -30,3 +30,24 @@ entry:
   %shl2 = shl i64 %xor, %xor1
   ret i64 %shl2
 }
+
+; Check that eon is generated if the xor is a disjoint or.
+define i64 @disjoint_or(i64 %a, i64 %b) {
+; CHECK-LABEL: disjoint_or:
+; CHECK: eon
+; CHECK: ret
+  %or = or disjoint i64 %a, %b
+  %eon = xor i64 %or, -1
+  ret i64 %eon
+}
+
+; Check that eon is *not* generated if the or is not disjoint.
+define i64 @normal_or(i64 %a, i64 %b) {
+; CHECK-LABEL: normal_or:
+; CHECK: orr
+; CHECK: mvn
+; CHECK: ret
+  %or = or i64 %a, %b
+  %not = xor i64 %or, -1
+  ret i64 %not
+}

@AZero13
Copy link
Contributor

AZero13 commented Jul 7, 2025

question: does ORN not exist on AArch64? was curious as to why the non-disjoint ors don't fold to that

@rj-jesus
Copy link
Contributor Author

rj-jesus commented Jul 7, 2025

question: does ORN not exist on AArch64? was curious as to why the non-disjoint ors don't fold to that

ORN does exit, but it doesn't implement NOT (OR (a, b)); it implements OR (a, NOT (b)).
(Technically EON it's the same, but it just so happens that NOT (EOR (a, b)) = EOR (a, NOT (b)).)

Copy link
Contributor

@david-arm david-arm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@llvmbot llvmbot added the llvm:SelectionDAG SelectionDAGISel as well label Jul 8, 2025
@AZero13
Copy link
Contributor

AZero13 commented Jul 9, 2025

(x | y) + (x & y) = x + y
(x | y) - (x & y) = x ^ y

Disjoint or is an or where (x & y) is 0, so I wonder if there's anything else we can use these properties for to optimize

@AZero13
Copy link
Contributor

AZero13 commented Jul 9, 2025

Me when I discover the concept of adders in CPUs

@topperc
Copy link
Collaborator

topperc commented Jul 9, 2025

I'm going to copy this to RISCV too. Thanks!

@AZero13
Copy link
Contributor

AZero13 commented Jul 9, 2025

I'm going to copy this to RISCV too. Thanks!

I am also doing to copy this for selectiondag

topperc added a commit that referenced this pull request Jul 9, 2025
A disjoint OR can be converted to XOR. And a XOR+NOT is XNOR. Idea
taken from #147279.
    
I changed the existing xnor pattern to have the not on the outside
instead of the inside. These are equivalent for xor since xor is
associative. Tablegen was already generating multiple variants
of the isel pattern using associativity.
    
There are some issues here. The disjoint flag isn't preserved
through type legalization. I was hoping we could recover it
manually for the masked merge cases, but that doesn't work either.
@rj-jesus rj-jesus merged commit 2d424e6 into llvm:main Jul 9, 2025
9 checks passed
@rj-jesus rj-jesus deleted the rjj/lower-disjoint-or-not-to-eon branch July 9, 2025 07:38
@rj-jesus
Copy link
Contributor Author

rj-jesus commented Jul 9, 2025

Thanks for porting this @topperc and @AZero13!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend:AArch64 llvm:SelectionDAG SelectionDAGISel as well

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants