[llvm] [AArch64] Fold (a | b) == a -> (a & ~b) == 0 when and-not exists (PR #145368)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 23 10:25:18 PDT 2025


https://github.com/AZero13 created https://github.com/llvm/llvm-project/pull/145368

This is especially helpful for AArch64, which simplifies ands cmp to tst.

>From 87b35483cd4a1e539e5991ccee6b2b86a39fdd69 Mon Sep 17 00:00:00 2001
From: Rose <gfunni234 at gmail.com>
Date: Mon, 23 Jun 2025 12:38:38 -0400
Subject: [PATCH 1/4] Pre-commit test (NFC)

---
 .../test/CodeGen/AArch64/aarch64-bitwisenot-fold.ll | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/llvm/test/CodeGen/AArch64/aarch64-bitwisenot-fold.ll b/llvm/test/CodeGen/AArch64/aarch64-bitwisenot-fold.ll
index 5fbf38b2560d4..522ff0f30036d 100644
--- a/llvm/test/CodeGen/AArch64/aarch64-bitwisenot-fold.ll
+++ b/llvm/test/CodeGen/AArch64/aarch64-bitwisenot-fold.ll
@@ -96,3 +96,16 @@ define i64 @andnot_sub_with_neg_i64(i64 %a0, i64 %a1) {
   %and = and i64 %diff, %a0
   ret i64 %and
 }
+
+define i32 @and_not_select(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: and_not_select:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    orr w8, w1, w0
+; CHECK-NEXT:    cmp w8, w0
+; CHECK-NEXT:    csel w0, w0, w2, eq
+; CHECK-NEXT:    ret
+  %or = or i32 %b, %a
+  %cmp = icmp eq i32 %or, %a
+  %a.c = select i1 %cmp, i32 %a, i32 %c
+  ret i32 %a.c
+}

>From 9a435accd5c0677e37c1498490d795a0fd525b1b Mon Sep 17 00:00:00 2001
From: Rose <gfunni234 at gmail.com>
Date: Mon, 23 Jun 2025 12:41:15 -0400
Subject: [PATCH 2/4] Update aarch64-bitwisenot-fold.ll

---
 .../CodeGen/AArch64/aarch64-bitwisenot-fold.ll | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/llvm/test/CodeGen/AArch64/aarch64-bitwisenot-fold.ll b/llvm/test/CodeGen/AArch64/aarch64-bitwisenot-fold.ll
index 522ff0f30036d..bf59b2a53e50f 100644
--- a/llvm/test/CodeGen/AArch64/aarch64-bitwisenot-fold.ll
+++ b/llvm/test/CodeGen/AArch64/aarch64-bitwisenot-fold.ll
@@ -97,8 +97,8 @@ define i64 @andnot_sub_with_neg_i64(i64 %a0, i64 %a1) {
   ret i64 %and
 }
 
-define i32 @and_not_select(i32 %a, i32 %b, i32 %c) {
-; CHECK-LABEL: and_not_select:
+define i32 @and_not_select_eq(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: and_not_select_eq:
 ; CHECK:       // %bb.0:
 ; CHECK-NEXT:    orr w8, w1, w0
 ; CHECK-NEXT:    cmp w8, w0
@@ -109,3 +109,17 @@ define i32 @and_not_select(i32 %a, i32 %b, i32 %c) {
   %a.c = select i1 %cmp, i32 %a, i32 %c
   ret i32 %a.c
 }
+
+define i32 @and_not_select_ne(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: and_not_select_ne:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    orr w8, w1, w0
+; CHECK-NEXT:    cmp w8, w0
+; CHECK-NEXT:    csel w0, w0, w2, ne
+; CHECK-NEXT:    ret
+  %or = or i32 %b, %a
+  %cmp = icmp ne i32 %or, %a
+  %a.c = select i1 %cmp, i32 %a, i32 %c
+  ret i32 %a.c
+}
+

>From bace672f1be743b90e16ffd45752dea1d704f00e Mon Sep 17 00:00:00 2001
From: Rose <gfunni234 at gmail.com>
Date: Mon, 23 Jun 2025 12:58:49 -0400
Subject: [PATCH 3/4] Update aarch64-bitwisenot-fold.ll

---
 llvm/test/CodeGen/AArch64/aarch64-bitwisenot-fold.ll | 1 -
 1 file changed, 1 deletion(-)

diff --git a/llvm/test/CodeGen/AArch64/aarch64-bitwisenot-fold.ll b/llvm/test/CodeGen/AArch64/aarch64-bitwisenot-fold.ll
index bf59b2a53e50f..28099a76fa34b 100644
--- a/llvm/test/CodeGen/AArch64/aarch64-bitwisenot-fold.ll
+++ b/llvm/test/CodeGen/AArch64/aarch64-bitwisenot-fold.ll
@@ -122,4 +122,3 @@ define i32 @and_not_select_ne(i32 %a, i32 %b, i32 %c) {
   %a.c = select i1 %cmp, i32 %a, i32 %c
   ret i32 %a.c
 }
-

>From 32975f478f6f3301a70853082c853273c4c33511 Mon Sep 17 00:00:00 2001
From: Rose <gfunni234 at gmail.com>
Date: Mon, 23 Jun 2025 13:22:51 -0400
Subject: [PATCH 4/4] Fold (a | b) == a -> (a & ~b) == 0 when and-not exists

This is especially helpful for AArch64, which simplifies ands cmp to tst.
---
 llvm/include/llvm/CodeGen/TargetLowering.h    |  2 +
 .../CodeGen/SelectionDAG/TargetLowering.cpp   | 53 +++++++++++++++++++
 2 files changed, 55 insertions(+)

diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index 727526055e592..ff2523b8a2517 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -5800,6 +5800,8 @@ class LLVM_ABI TargetLowering : public TargetLoweringBase {
 private:
   SDValue foldSetCCWithAnd(EVT VT, SDValue N0, SDValue N1, ISD::CondCode Cond,
                            const SDLoc &DL, DAGCombinerInfo &DCI) const;
+  SDValue foldSetCCWithOr(EVT VT, SDValue N0, SDValue N1, ISD::CondCode Cond,
+                          const SDLoc &DL, DAGCombinerInfo &DCI) const;
   SDValue foldSetCCWithBinOp(EVT VT, SDValue N0, SDValue N1, ISD::CondCode Cond,
                              const SDLoc &DL, DAGCombinerInfo &DCI) const;
 
diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
index 66717135c9adf..72f860521bb04 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -4212,6 +4212,56 @@ SDValue TargetLowering::foldSetCCWithAnd(EVT VT, SDValue N0, SDValue N1,
   return SDValue();
 }
 
+/// This helper function of SimplifySetCC tries to optimize the comparison when
+/// either operand of the SetCC node is a bitwise-or instruction.
+SDValue TargetLowering::foldSetCCWithOr(EVT VT, SDValue N0, SDValue N1,
+                                         ISD::CondCode Cond, const SDLoc &DL,
+                                         DAGCombinerInfo &DCI) const {
+  if (N1.getOpcode() == ISD::OR && N0.getOpcode() != ISD::OR)
+    std::swap(N0, N1);
+
+  SelectionDAG &DAG = DCI.DAG;
+  EVT OpVT = N0.getValueType();
+  if (N0.getOpcode() != ISD::OR || !OpVT.isInteger() ||
+      (Cond != ISD::SETEQ && Cond != ISD::SETNE))
+    return SDValue();
+
+  // Match these patterns in any of their permutations:
+  // (X | Y) == Y
+  // (X | Y) != Y
+  SDValue X, Y;
+  if (N0.getOperand(0) == N1) {
+    X = N0.getOperand(1);
+    Y = N0.getOperand(0);
+  } else if (N0.getOperand(1) == N1) {
+    X = N0.getOperand(0);
+    Y = N0.getOperand(1);
+  } else {
+    return SDValue();
+  }
+
+  SDValue Zero = DAG.getConstant(0, DL, OpVT);
+  if (N0.hasOneUse() && hasAndNotCompare(Y)) {
+    // If the target supports an 'and-not' or 'and-complement' logic operation,
+    // try to use that to make a comparison operation more efficient.
+    // But don't do this transform if the mask is a single bit because there are
+    // more efficient ways to deal with that case (for example, 'bt' on x86 or
+    // 'rlwinm' on PPC).
+
+    // Bail out if the compare operand that we want to turn into a zero is
+    // already a zero (otherwise, infinite loop).
+    if (isNullConstant(Y))
+      return SDValue();
+
+    // Transform this into: X & ~Y == 0.
+    SDValue NotY = DAG.getNOT(SDLoc(Y), Y, OpVT);
+    SDValue NewAnd = DAG.getNode(ISD::AND, SDLoc(N0), OpVT, X, NotY);
+    return DAG.getSetCC(DL, VT, NewAnd, Zero, Cond);
+  }
+
+  return SDValue();
+}
+
 /// There are multiple IR patterns that could be checking whether certain
 /// truncation of a signed number would be lossy or not. The pattern which is
 /// best at IR level, may not lower optimally. Thus, we want to unfold it.
@@ -5507,6 +5557,9 @@ SDValue TargetLowering::SimplifySetCC(EVT VT, SDValue N0, SDValue N1,
 
     if (SDValue V = foldSetCCWithAnd(VT, N0, N1, Cond, dl, DCI))
       return V;
+
+    if (SDValue V = foldSetCCWithOr(VT, N0, N1, Cond, dl, DCI))
+      return V;
   }
 
   // Fold remainder of division by a constant.



More information about the llvm-commits mailing list