[llvm] [RISCV] Optimize (and (icmp x, 0, neq), (icmp y, 0, neq)) utilizing zicond extension (PR #166469)

Ryan Buchner via llvm-commits llvm-commits at lists.llvm.org
Thu Nov 6 16:49:36 PST 2025


https://github.com/bababuck updated https://github.com/llvm/llvm-project/pull/166469

>From a5bc43edf36373213c3697014ecefacf8df58668 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Mon, 3 Nov 2025 10:25:08 -0800
Subject: [PATCH 01/10] [RISCV] Add new test for
 shouldNormalizeToSelectSequence variant

---
 llvm/test/CodeGen/RISCV/zicond-opts.ll | 29 ++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/llvm/test/CodeGen/RISCV/zicond-opts.ll b/llvm/test/CodeGen/RISCV/zicond-opts.ll
index d8e2b2c2bf58d..c74469fc0e8ce 100644
--- a/llvm/test/CodeGen/RISCV/zicond-opts.ll
+++ b/llvm/test/CodeGen/RISCV/zicond-opts.ll
@@ -26,6 +26,35 @@ define i32 @icmp_and(i64 %x, i64 %y) {
   ret i32 %6
 }
 
+; (and (icmp x. 0, ne), (icmp y, 0, ne)) -> (czero.eqz (icmp x, 0, ne), y)
+define i32 @icmp_and_select(i64 %x, i64 %y, i32 %z) {
+; RV32ZICOND-LABEL: icmp_and_select:
+; RV32ZICOND:       # %bb.0:
+; RV32ZICOND-NEXT:    sgtz a5, a3
+; RV32ZICOND-NEXT:    snez a2, a2
+; RV32ZICOND-NEXT:    or a0, a0, a1
+; RV32ZICOND-NEXT:    czero.eqz a1, a5, a3
+; RV32ZICOND-NEXT:    czero.nez a2, a2, a3
+; RV32ZICOND-NEXT:    or a1, a2, a1
+; RV32ZICOND-NEXT:    snez a0, a0
+; RV32ZICOND-NEXT:    and a0, a0, a1
+; RV32ZICOND-NEXT:    czero.eqz a0, a4, a0
+; RV32ZICOND-NEXT:    ret
+;
+; RV64ZICOND-LABEL: icmp_and_select:
+; RV64ZICOND:       # %bb.0:
+; RV64ZICOND-NEXT:    sgtz a1, a1
+; RV64ZICOND-NEXT:    snez a0, a0
+; RV64ZICOND-NEXT:    and a0, a0, a1
+; RV64ZICOND-NEXT:    czero.eqz a0, a2, a0
+; RV64ZICOND-NEXT:    ret
+  %3 = icmp sgt i64 %y, 0
+  %4 = icmp ne i64 %x, 0
+  %5 = and i1 %4, %3
+  %6 = select i1 %5, i32 %z, i32 0
+  ret i32 %6
+}
+
 ; (and (and (icmp x, 0, ne), (icmp y, 0, ne)), (icmp z, 0, ne)) -> (czero.eqz (czero.eqz (icmp x, 0, ne), y), z)
 define i32 @icmp_and_and(i64 %x, i64 %y, i64 %z) {
 ; RV32ZICOND-LABEL: icmp_and_and:

>From a10ddf7097b1b9dc70ed7ffe5f7133c8054dbe63 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Mon, 3 Nov 2025 08:02:00 -0800
Subject: [PATCH 02/10] [RISCV] Plumb in a SDNode* to
 TargetLowering::shouldNormalizeToSelectSequence

Will need the extra information for RISCV lowering.
---
 llvm/include/llvm/CodeGen/TargetLowering.h      | 4 ++--
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp   | 2 +-
 llvm/lib/Target/AArch64/AArch64ISelLowering.cpp | 4 ++--
 llvm/lib/Target/AArch64/AArch64ISelLowering.h   | 3 ++-
 llvm/lib/Target/RISCV/RISCVISelLowering.h       | 3 ++-
 5 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index 78f63b4406eb0..1a9469d484bc5 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -2489,8 +2489,8 @@ class LLVM_ABI TargetLoweringBase {
   /// select(N0|N1, X, Y) => select(N0, select(N1, X, Y, Y)) if it is likely
   /// that it saves us from materializing N0 and N1 in an integer register.
   /// Targets that are able to perform and/or on flags should return false here.
-  virtual bool shouldNormalizeToSelectSequence(LLVMContext &Context,
-                                               EVT VT) const {
+  virtual bool shouldNormalizeToSelectSequence(LLVMContext &Context, EVT VT,
+                                               SDNode *) const {
     // If a target has multiple condition registers, then it likely has logical
     // operations on those registers.
     if (hasMultipleConditionRegisters(VT))
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 46c4bb85a7420..a3632190292fa 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -12387,7 +12387,7 @@ SDValue DAGCombiner::visitSELECT(SDNode *N) {
     // and we always transform to the left side if we know that we can further
     // optimize the combination of the conditions.
     bool normalizeToSequence =
-        TLI.shouldNormalizeToSelectSequence(*DAG.getContext(), VT);
+        TLI.shouldNormalizeToSelectSequence(*DAG.getContext(), VT, N);
     // select (and Cond0, Cond1), X, Y
     //   -> select Cond0, (select Cond1, X, Y), Y
     if (N0->getOpcode() == ISD::AND && N0->hasOneUse()) {
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 60aa61e993b26..e1e9b37d6e5bf 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -29457,8 +29457,8 @@ bool AArch64TargetLowering::functionArgumentNeedsConsecutiveRegisters(
   return all_equal(ValueVTs);
 }
 
-bool AArch64TargetLowering::shouldNormalizeToSelectSequence(LLVMContext &,
-                                                            EVT) const {
+bool AArch64TargetLowering::shouldNormalizeToSelectSequence(LLVMContext &, EVT,
+                                                            SDNode *) const {
   return false;
 }
 
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h
index 2cb8ed29f252a..52b1a6ab4c90d 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h
@@ -867,7 +867,8 @@ class AArch64TargetLowering : public TargetLowering {
                                        SmallVectorImpl<SDValue> &Results,
                                        SelectionDAG &DAG) const;
 
-  bool shouldNormalizeToSelectSequence(LLVMContext &, EVT) const override;
+  bool shouldNormalizeToSelectSequence(LLVMContext &, EVT,
+                                       SDNode *) const override;
 
   void finalizeLowering(MachineFunction &MF) const override;
 
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.h b/llvm/lib/Target/RISCV/RISCVISelLowering.h
index 9e3e2a9443625..e01aca05f3ff7 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.h
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.h
@@ -607,7 +607,8 @@ class RISCVTargetLowering : public TargetLowering {
   /// select(N0&N1, X, Y) => select(N0, select(N1, X, Y), Y) and
   /// select(N0|N1, X, Y) => select(N0, select(N1, X, Y, Y))
   /// RISC-V doesn't have flags so it's better to perform the and/or in a GPR.
-  bool shouldNormalizeToSelectSequence(LLVMContext &, EVT) const override {
+  bool shouldNormalizeToSelectSequence(LLVMContext &, EVT,
+                                       SDNode *N) const override {
     return false;
   }
 

>From b67a9bac6347c260b81ea206987527ee240ae4d7 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Mon, 3 Nov 2025 08:05:57 -0800
Subject: [PATCH 03/10] [RISCV] Add TargetLowering::hasConditionalZero()

Will guide lowering chioces in DAGCombine.
---
 llvm/include/llvm/CodeGen/TargetLowering.h  | 4 ++++
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 4 ++++
 llvm/lib/Target/RISCV/RISCVISelLowering.h   | 2 ++
 3 files changed, 10 insertions(+)

diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index 1a9469d484bc5..dac4dee97fbe5 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -2502,6 +2502,10 @@ class LLVM_ABI TargetLoweringBase {
       Action != TypeSplitVector;
   }
 
+  // Return true is targets has a conditional zero-ing instruction
+  // i.e. select cond, x, 0
+  virtual bool hasConditionalZero() const { return false; }
+
   virtual bool isProfitableToCombineMinNumMaxNum(EVT VT) const { return true; }
 
   /// Return true if a select of constants (select Cond, C1, C2) should be
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index e0cf739f67d9b..2ca6adabab091 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -2763,6 +2763,10 @@ bool RISCVTargetLowering::mergeStoresAfterLegalization(EVT VT) const {
          (VT.isFixedLengthVector() && VT.getVectorElementType() == MVT::i1);
 }
 
+bool RISCVTargetLowering::hasConditionalZero() const {
+  return Subtarget.hasStdExtZicond() || Subtarget.hasVendorXVentanaCondOps();
+}
+
 bool RISCVTargetLowering::isLegalElementTypeForRVV(EVT ScalarTy) const {
   if (!ScalarTy.isSimple())
     return false;
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.h b/llvm/lib/Target/RISCV/RISCVISelLowering.h
index e01aca05f3ff7..c7931730e10b1 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.h
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.h
@@ -612,6 +612,8 @@ class RISCVTargetLowering : public TargetLowering {
     return false;
   }
 
+  bool hasConditionalZero() const override;
+
   /// Disables storing and loading vectors by default when there are function
   /// calls between the load and store, since these are more expensive than just
   /// using scalars

>From 821a64f576eef7f179df5ac7d35f0b248a28501d Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Mon, 3 Nov 2025 08:06:35 -0800
Subject: [PATCH 04/10] [RISCV] Optimize (and (icmp x, 0, eq), (icmp y, 0, eq))
 utilizing zicond extension

%1 = icmp x, 0, eq
%2 = icmp y, 0, eq
%3 = and %1, %2

Origionally lowered to:
%1 = seqz x
%2 = seqz y
%3 = and %1, %2

With optimiztion:
%1 = seqz x
%3 = czero.eqz %1, y
---
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp |  8 ++-
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp   | 63 ++++++++++++++++++-
 llvm/lib/Target/RISCV/RISCVISelLowering.h     | 10 +--
 llvm/test/CodeGen/RISCV/zicond-opts.ll        | 32 ++++------
 4 files changed, 82 insertions(+), 31 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index a3632190292fa..8a9ed7a7eac46 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -12202,7 +12202,9 @@ static SDValue foldBoolSelectToLogic(SDNode *N, const SDLoc &DL,
 
   // select Cond, T, Cond --> and Cond, freeze(T)
   // select Cond, T, 0    --> and Cond, freeze(T)
-  if (Cond == F || isNullOrNullSplat(F, /* AllowUndefs */ true))
+  // select Cond, T, 0 is a conditional zero
+  if (Cond == F || (!TLI.hasConditionalZero() &&
+                    isNullOrNullSplat(F, /* AllowUndefs */ true)))
     return matcher.getNode(ISD::AND, DL, VT, Cond, DAG.getFreeze(T));
 
   // select Cond, T, 1 --> or (not Cond), freeze(T)
@@ -12213,7 +12215,9 @@ static SDValue foldBoolSelectToLogic(SDNode *N, const SDLoc &DL,
   }
 
   // select Cond, 0, F --> and (not Cond), freeze(F)
-  if (isNullOrNullSplat(T, /* AllowUndefs */ true)) {
+  // select Cond, 0, F is a conditional zero
+  if (!TLI.hasConditionalZero() &&
+      isNullOrNullSplat(T, /* AllowUndefs */ true)) {
     SDValue NotCond =
         matcher.getNode(ISD::XOR, DL, VT, Cond, DAG.getAllOnesConstant(DL, VT));
     return matcher.getNode(ISD::AND, DL, VT, NotCond, DAG.getFreeze(F));
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 2ca6adabab091..2d178586a0827 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -2763,6 +2763,47 @@ bool RISCVTargetLowering::mergeStoresAfterLegalization(EVT VT) const {
          (VT.isFixedLengthVector() && VT.getVectorElementType() == MVT::i1);
 }
 
+// Can the given operation be interchanged with a Zicond::CZERO operation
+// Must be:
+// - a SETCC instruction
+// - Must compare a value for [in]equality against 0
+static bool isCzeroCompatible(const SDValue Op) {
+  if (Op.getValueType() == MVT::i1 && Op.getOpcode() == ISD::SETCC &&
+      isNullConstant(Op.getOperand(1))) {
+    ISD::CondCode CondCode = cast<CondCodeSDNode>(Op.getOperand(2))->get();
+    return CondCode == ISD::SETNE || CondCode == ISD::SETEQ;
+  }
+  return false;
+}
+
+// Disable normalizing for most cases
+// select(N0&N1, X, Y) => select(N0, select(N1, X, Y), Y) and
+// select(N0|N1, X, Y) => select(N0, Y, select(N1, X, Y))
+// For select(N0, select(N1, X, Y), Y), if Y=0 and N0=setcc(eqz || nez):
+// %N1 = setcc [any_cond] %A, %B
+// %CZ = czero.eqz %N1, X
+// %Res = czero.eqz %N0, %CZ
+// ...
+// But for select(N0&N1, X, Y):
+// %N0 = setcc [eq/ne] %C, 0
+// %N1 = setcc [any_cond] %A, %B
+// %And = and %N0, %N1
+// %Res = czero.eqz %And, %X
+bool RISCVTargetLowering::shouldNormalizeToSelectSequence(LLVMContext &, EVT VT,
+                                                          SDNode *N) const {
+  if (Subtarget.hasStdExtZicond() || Subtarget.hasVendorXVentanaCondOps()) {
+    assert(
+        N->getOpcode() == ISD::SELECT &&
+        "shouldNormalizeToSelectSequence() called with non-SELECT operation");
+    const SDValue &CondV = N->getOperand(0);
+    const SDValue &TrueV = N->getOperand(1);
+    const SDValue &FalseV = N->getOperand(2);
+    if (CondV.hasOneUse() && isCzeroCompatible(CondV) && isNullConstant(FalseV))
+      return true;
+  }
+  return false;
+}
+
 bool RISCVTargetLowering::hasConditionalZero() const {
   return Subtarget.hasStdExtZicond() || Subtarget.hasVendorXVentanaCondOps();
 }
@@ -16121,6 +16162,25 @@ static SDValue reverseZExtICmpCombine(SDNode *N, SelectionDAG &DAG,
   return DAG.getNode(ISD::ZERO_EXTEND, DL, VT, Res);
 }
 
+static SDValue reduceANDOfSetCC(SDNode *N, SelectionDAG &DAG,
+                                const RISCVSubtarget &Subtarget) {
+  if (Subtarget.hasStdExtZicond() || Subtarget.hasVendorXVentanaCondOps()) {
+    // (and (i1) f, (setcc c, 0, ne)) -> (select c, f, 0) -> (czero.nez f, c)
+    // (and (i1) f, (setcc c, 0, eq)) -> (select c, 0, f) -> (czero.eqz f, c)
+    // (and (setcc c, 0, ne), (i1) g) -> (select c, g, 0) -> (czero.nez g, c)
+    // (and (setcc c, 0, eq), (i1) g) -> (select c, 0, g) -> (czero.eqz g, c)
+    const bool CzeroOp1 = isCzeroCompatible(N->getOperand(1));
+    if (CzeroOp1 || isCzeroCompatible(N->getOperand(0))) {
+      const SDValue I1Op = CzeroOp1 ? N->getOperand(0) : N->getOperand(1);
+      const SDValue SetCCOp = CzeroOp1 ? N->getOperand(1) : N->getOperand(0);
+      SDLoc DL(N);
+      return DAG.getNode(ISD::SELECT, DL, MVT::i1, SetCCOp, I1Op,
+                         DAG.getConstant(0, DL, MVT::i1));
+    }
+  }
+  return SDValue();
+}
+
 static SDValue reduceANDOfAtomicLoad(SDNode *N,
                                      TargetLowering::DAGCombinerInfo &DCI) {
   SelectionDAG &DAG = DCI.DAG;
@@ -16184,7 +16244,8 @@ static SDValue performANDCombine(SDNode *N,
 
   if (SDValue V = reverseZExtICmpCombine(N, DAG, Subtarget))
     return V;
-
+  if (SDValue V = reduceANDOfSetCC(N, DAG, Subtarget))
+    return V;
   if (SDValue V = combineBinOpToReduce(N, DAG, Subtarget))
     return V;
   if (SDValue V = combineBinOpOfExtractToReduceTree(N, DAG, Subtarget))
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.h b/llvm/lib/Target/RISCV/RISCVISelLowering.h
index c7931730e10b1..7a2eca41b4955 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.h
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.h
@@ -603,14 +603,8 @@ class RISCVTargetLowering : public TargetLowering {
   /// this override can be removed.
   bool mergeStoresAfterLegalization(EVT VT) const override;
 
-  /// Disable normalizing
-  /// select(N0&N1, X, Y) => select(N0, select(N1, X, Y), Y) and
-  /// select(N0|N1, X, Y) => select(N0, select(N1, X, Y, Y))
-  /// RISC-V doesn't have flags so it's better to perform the and/or in a GPR.
-  bool shouldNormalizeToSelectSequence(LLVMContext &, EVT,
-                                       SDNode *N) const override {
-    return false;
-  }
+  bool shouldNormalizeToSelectSequence(LLVMContext &, EVT VT,
+                                       SDNode *N) const override;
 
   bool hasConditionalZero() const override;
 
diff --git a/llvm/test/CodeGen/RISCV/zicond-opts.ll b/llvm/test/CodeGen/RISCV/zicond-opts.ll
index c74469fc0e8ce..baa6bcb284098 100644
--- a/llvm/test/CodeGen/RISCV/zicond-opts.ll
+++ b/llvm/test/CodeGen/RISCV/zicond-opts.ll
@@ -8,16 +8,14 @@ define i32 @icmp_and(i64 %x, i64 %y) {
 ; RV32ZICOND:       # %bb.0:
 ; RV32ZICOND-NEXT:    or a2, a2, a3
 ; RV32ZICOND-NEXT:    or a0, a0, a1
-; RV32ZICOND-NEXT:    snez a1, a2
 ; RV32ZICOND-NEXT:    snez a0, a0
-; RV32ZICOND-NEXT:    and a0, a0, a1
+; RV32ZICOND-NEXT:    czero.eqz a0, a0, a2
 ; RV32ZICOND-NEXT:    ret
 ;
 ; RV64ZICOND-LABEL: icmp_and:
 ; RV64ZICOND:       # %bb.0:
-; RV64ZICOND-NEXT:    snez a1, a1
 ; RV64ZICOND-NEXT:    snez a0, a0
-; RV64ZICOND-NEXT:    and a0, a0, a1
+; RV64ZICOND-NEXT:    czero.eqz a0, a0, a1
 ; RV64ZICOND-NEXT:    ret
   %3 = icmp ne i64 %y, 0
   %4 = icmp ne i64 %x, 0
@@ -32,20 +30,18 @@ define i32 @icmp_and_select(i64 %x, i64 %y, i32 %z) {
 ; RV32ZICOND:       # %bb.0:
 ; RV32ZICOND-NEXT:    sgtz a5, a3
 ; RV32ZICOND-NEXT:    snez a2, a2
-; RV32ZICOND-NEXT:    or a0, a0, a1
-; RV32ZICOND-NEXT:    czero.eqz a1, a5, a3
+; RV32ZICOND-NEXT:    czero.eqz a5, a5, a3
 ; RV32ZICOND-NEXT:    czero.nez a2, a2, a3
-; RV32ZICOND-NEXT:    or a1, a2, a1
-; RV32ZICOND-NEXT:    snez a0, a0
-; RV32ZICOND-NEXT:    and a0, a0, a1
+; RV32ZICOND-NEXT:    or a2, a2, a5
+; RV32ZICOND-NEXT:    or a0, a0, a1
+; RV32ZICOND-NEXT:    czero.eqz a0, a2, a0
 ; RV32ZICOND-NEXT:    czero.eqz a0, a4, a0
 ; RV32ZICOND-NEXT:    ret
 ;
 ; RV64ZICOND-LABEL: icmp_and_select:
 ; RV64ZICOND:       # %bb.0:
 ; RV64ZICOND-NEXT:    sgtz a1, a1
-; RV64ZICOND-NEXT:    snez a0, a0
-; RV64ZICOND-NEXT:    and a0, a0, a1
+; RV64ZICOND-NEXT:    czero.eqz a0, a1, a0
 ; RV64ZICOND-NEXT:    czero.eqz a0, a2, a0
 ; RV64ZICOND-NEXT:    ret
   %3 = icmp sgt i64 %y, 0
@@ -61,21 +57,17 @@ define i32 @icmp_and_and(i64 %x, i64 %y, i64 %z) {
 ; RV32ZICOND:       # %bb.0:
 ; RV32ZICOND-NEXT:    or a2, a2, a3
 ; RV32ZICOND-NEXT:    or a0, a0, a1
-; RV32ZICOND-NEXT:    or a4, a4, a5
 ; RV32ZICOND-NEXT:    snez a1, a2
-; RV32ZICOND-NEXT:    snez a0, a0
-; RV32ZICOND-NEXT:    and a0, a1, a0
-; RV32ZICOND-NEXT:    snez a1, a4
-; RV32ZICOND-NEXT:    and a0, a1, a0
+; RV32ZICOND-NEXT:    czero.eqz a0, a1, a0
+; RV32ZICOND-NEXT:    or a4, a4, a5
+; RV32ZICOND-NEXT:    czero.eqz a0, a0, a4
 ; RV32ZICOND-NEXT:    ret
 ;
 ; RV64ZICOND-LABEL: icmp_and_and:
 ; RV64ZICOND:       # %bb.0:
 ; RV64ZICOND-NEXT:    snez a1, a1
-; RV64ZICOND-NEXT:    snez a0, a0
-; RV64ZICOND-NEXT:    and a0, a1, a0
-; RV64ZICOND-NEXT:    snez a1, a2
-; RV64ZICOND-NEXT:    and a0, a1, a0
+; RV64ZICOND-NEXT:    czero.eqz a0, a1, a0
+; RV64ZICOND-NEXT:    czero.eqz a0, a0, a2
 ; RV64ZICOND-NEXT:    ret
   %4 = icmp ne i64 %y, 0
   %5 = icmp ne i64 %x, 0

>From 3c613249725a159572f4f01238903a56773a0359 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Tue, 4 Nov 2025 16:23:06 -0800
Subject: [PATCH 05/10] [RISCV] Use Subtarget.hasCZEROLike()

---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 2d178586a0827..c1ad9fe43f6e4 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -2791,7 +2791,7 @@ static bool isCzeroCompatible(const SDValue Op) {
 // %Res = czero.eqz %And, %X
 bool RISCVTargetLowering::shouldNormalizeToSelectSequence(LLVMContext &, EVT VT,
                                                           SDNode *N) const {
-  if (Subtarget.hasStdExtZicond() || Subtarget.hasVendorXVentanaCondOps()) {
+  if (Subtarget.hasCZEROLike()) {
     assert(
         N->getOpcode() == ISD::SELECT &&
         "shouldNormalizeToSelectSequence() called with non-SELECT operation");
@@ -2805,7 +2805,7 @@ bool RISCVTargetLowering::shouldNormalizeToSelectSequence(LLVMContext &, EVT VT,
 }
 
 bool RISCVTargetLowering::hasConditionalZero() const {
-  return Subtarget.hasStdExtZicond() || Subtarget.hasVendorXVentanaCondOps();
+  return Subtarget.hasCZEROLike();
 }
 
 bool RISCVTargetLowering::isLegalElementTypeForRVV(EVT ScalarTy) const {
@@ -16164,7 +16164,7 @@ static SDValue reverseZExtICmpCombine(SDNode *N, SelectionDAG &DAG,
 
 static SDValue reduceANDOfSetCC(SDNode *N, SelectionDAG &DAG,
                                 const RISCVSubtarget &Subtarget) {
-  if (Subtarget.hasStdExtZicond() || Subtarget.hasVendorXVentanaCondOps()) {
+  if (Subtarget.hasCZEROLike()) {
     // (and (i1) f, (setcc c, 0, ne)) -> (select c, f, 0) -> (czero.nez f, c)
     // (and (i1) f, (setcc c, 0, eq)) -> (select c, 0, f) -> (czero.eqz f, c)
     // (and (setcc c, 0, ne), (i1) g) -> (select c, g, 0) -> (czero.nez g, c)

>From dac0c92a538202a56b17ff377b480f90baa02b4b Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Tue, 4 Nov 2025 16:24:41 -0800
Subject: [PATCH 06/10] Don't use references to SDValue's

---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index c1ad9fe43f6e4..ce85074d22920 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -2795,9 +2795,9 @@ bool RISCVTargetLowering::shouldNormalizeToSelectSequence(LLVMContext &, EVT VT,
     assert(
         N->getOpcode() == ISD::SELECT &&
         "shouldNormalizeToSelectSequence() called with non-SELECT operation");
-    const SDValue &CondV = N->getOperand(0);
-    const SDValue &TrueV = N->getOperand(1);
-    const SDValue &FalseV = N->getOperand(2);
+    const SDValue CondV = N->getOperand(0);
+    const SDValue TrueV = N->getOperand(1);
+    const SDValue FalseV = N->getOperand(2);
     if (CondV.hasOneUse() && isCzeroCompatible(CondV) && isNullConstant(FalseV))
       return true;
   }

>From aa394a99eefac72996bb23e2fd6fb0380c1b5a76 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Tue, 4 Nov 2025 16:28:06 -0800
Subject: [PATCH 07/10] Rename reduceANDOfSetCC() -> combineANDOfSetCCToCZERO()

---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index ce85074d22920..875a4e1aa0f73 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -16162,8 +16162,8 @@ static SDValue reverseZExtICmpCombine(SDNode *N, SelectionDAG &DAG,
   return DAG.getNode(ISD::ZERO_EXTEND, DL, VT, Res);
 }
 
-static SDValue reduceANDOfSetCC(SDNode *N, SelectionDAG &DAG,
-                                const RISCVSubtarget &Subtarget) {
+static SDValue combineANDOfSetCCToCZERO(SDNode *N, SelectionDAG &DAG,
+                                        const RISCVSubtarget &Subtarget) {
   if (Subtarget.hasCZEROLike()) {
     // (and (i1) f, (setcc c, 0, ne)) -> (select c, f, 0) -> (czero.nez f, c)
     // (and (i1) f, (setcc c, 0, eq)) -> (select c, 0, f) -> (czero.eqz f, c)
@@ -16244,7 +16244,7 @@ static SDValue performANDCombine(SDNode *N,
 
   if (SDValue V = reverseZExtICmpCombine(N, DAG, Subtarget))
     return V;
-  if (SDValue V = reduceANDOfSetCC(N, DAG, Subtarget))
+  if (SDValue V = combineANDOfSetCCToCZERO(N, DAG, Subtarget))
     return V;
   if (SDValue V = combineBinOpToReduce(N, DAG, Subtarget))
     return V;

>From 487d3f8c9fac0a6ce626e39180a3234fe67ad847 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Tue, 4 Nov 2025 18:20:38 -0800
Subject: [PATCH 08/10] Remove unused variable

---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 875a4e1aa0f73..42755dddc1c0e 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -2796,7 +2796,6 @@ bool RISCVTargetLowering::shouldNormalizeToSelectSequence(LLVMContext &, EVT VT,
         N->getOpcode() == ISD::SELECT &&
         "shouldNormalizeToSelectSequence() called with non-SELECT operation");
     const SDValue CondV = N->getOperand(0);
-    const SDValue TrueV = N->getOperand(1);
     const SDValue FalseV = N->getOperand(2);
     if (CondV.hasOneUse() && isCzeroCompatible(CondV) && isNullConstant(FalseV))
       return true;

>From c45fff724b402a58816805e24f1a92d412df3dc0 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Thu, 6 Nov 2025 16:37:09 -0800
Subject: [PATCH 09/10] Revert prior attempt

---
 llvm/include/llvm/CodeGen/TargetLowering.h    |  8 +--
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 10 +--
 .../Target/AArch64/AArch64ISelLowering.cpp    |  4 +-
 llvm/lib/Target/AArch64/AArch64ISelLowering.h |  3 +-
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp   | 66 +------------------
 llvm/lib/Target/RISCV/RISCVISelLowering.h     | 11 ++--
 llvm/test/CodeGen/RISCV/zicond-opts.ll        | 32 +++++----
 7 files changed, 36 insertions(+), 98 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index dac4dee97fbe5..78f63b4406eb0 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -2489,8 +2489,8 @@ class LLVM_ABI TargetLoweringBase {
   /// select(N0|N1, X, Y) => select(N0, select(N1, X, Y, Y)) if it is likely
   /// that it saves us from materializing N0 and N1 in an integer register.
   /// Targets that are able to perform and/or on flags should return false here.
-  virtual bool shouldNormalizeToSelectSequence(LLVMContext &Context, EVT VT,
-                                               SDNode *) const {
+  virtual bool shouldNormalizeToSelectSequence(LLVMContext &Context,
+                                               EVT VT) const {
     // If a target has multiple condition registers, then it likely has logical
     // operations on those registers.
     if (hasMultipleConditionRegisters(VT))
@@ -2502,10 +2502,6 @@ class LLVM_ABI TargetLoweringBase {
       Action != TypeSplitVector;
   }
 
-  // Return true is targets has a conditional zero-ing instruction
-  // i.e. select cond, x, 0
-  virtual bool hasConditionalZero() const { return false; }
-
   virtual bool isProfitableToCombineMinNumMaxNum(EVT VT) const { return true; }
 
   /// Return true if a select of constants (select Cond, C1, C2) should be
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 8a9ed7a7eac46..46c4bb85a7420 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -12202,9 +12202,7 @@ static SDValue foldBoolSelectToLogic(SDNode *N, const SDLoc &DL,
 
   // select Cond, T, Cond --> and Cond, freeze(T)
   // select Cond, T, 0    --> and Cond, freeze(T)
-  // select Cond, T, 0 is a conditional zero
-  if (Cond == F || (!TLI.hasConditionalZero() &&
-                    isNullOrNullSplat(F, /* AllowUndefs */ true)))
+  if (Cond == F || isNullOrNullSplat(F, /* AllowUndefs */ true))
     return matcher.getNode(ISD::AND, DL, VT, Cond, DAG.getFreeze(T));
 
   // select Cond, T, 1 --> or (not Cond), freeze(T)
@@ -12215,9 +12213,7 @@ static SDValue foldBoolSelectToLogic(SDNode *N, const SDLoc &DL,
   }
 
   // select Cond, 0, F --> and (not Cond), freeze(F)
-  // select Cond, 0, F is a conditional zero
-  if (!TLI.hasConditionalZero() &&
-      isNullOrNullSplat(T, /* AllowUndefs */ true)) {
+  if (isNullOrNullSplat(T, /* AllowUndefs */ true)) {
     SDValue NotCond =
         matcher.getNode(ISD::XOR, DL, VT, Cond, DAG.getAllOnesConstant(DL, VT));
     return matcher.getNode(ISD::AND, DL, VT, NotCond, DAG.getFreeze(F));
@@ -12391,7 +12387,7 @@ SDValue DAGCombiner::visitSELECT(SDNode *N) {
     // and we always transform to the left side if we know that we can further
     // optimize the combination of the conditions.
     bool normalizeToSequence =
-        TLI.shouldNormalizeToSelectSequence(*DAG.getContext(), VT, N);
+        TLI.shouldNormalizeToSelectSequence(*DAG.getContext(), VT);
     // select (and Cond0, Cond1), X, Y
     //   -> select Cond0, (select Cond1, X, Y), Y
     if (N0->getOpcode() == ISD::AND && N0->hasOneUse()) {
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index e1e9b37d6e5bf..60aa61e993b26 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -29457,8 +29457,8 @@ bool AArch64TargetLowering::functionArgumentNeedsConsecutiveRegisters(
   return all_equal(ValueVTs);
 }
 
-bool AArch64TargetLowering::shouldNormalizeToSelectSequence(LLVMContext &, EVT,
-                                                            SDNode *) const {
+bool AArch64TargetLowering::shouldNormalizeToSelectSequence(LLVMContext &,
+                                                            EVT) const {
   return false;
 }
 
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h
index 52b1a6ab4c90d..2cb8ed29f252a 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h
@@ -867,8 +867,7 @@ class AArch64TargetLowering : public TargetLowering {
                                        SmallVectorImpl<SDValue> &Results,
                                        SelectionDAG &DAG) const;
 
-  bool shouldNormalizeToSelectSequence(LLVMContext &, EVT,
-                                       SDNode *) const override;
+  bool shouldNormalizeToSelectSequence(LLVMContext &, EVT) const override;
 
   void finalizeLowering(MachineFunction &MF) const override;
 
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 42755dddc1c0e..e0cf739f67d9b 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -2763,50 +2763,6 @@ bool RISCVTargetLowering::mergeStoresAfterLegalization(EVT VT) const {
          (VT.isFixedLengthVector() && VT.getVectorElementType() == MVT::i1);
 }
 
-// Can the given operation be interchanged with a Zicond::CZERO operation
-// Must be:
-// - a SETCC instruction
-// - Must compare a value for [in]equality against 0
-static bool isCzeroCompatible(const SDValue Op) {
-  if (Op.getValueType() == MVT::i1 && Op.getOpcode() == ISD::SETCC &&
-      isNullConstant(Op.getOperand(1))) {
-    ISD::CondCode CondCode = cast<CondCodeSDNode>(Op.getOperand(2))->get();
-    return CondCode == ISD::SETNE || CondCode == ISD::SETEQ;
-  }
-  return false;
-}
-
-// Disable normalizing for most cases
-// select(N0&N1, X, Y) => select(N0, select(N1, X, Y), Y) and
-// select(N0|N1, X, Y) => select(N0, Y, select(N1, X, Y))
-// For select(N0, select(N1, X, Y), Y), if Y=0 and N0=setcc(eqz || nez):
-// %N1 = setcc [any_cond] %A, %B
-// %CZ = czero.eqz %N1, X
-// %Res = czero.eqz %N0, %CZ
-// ...
-// But for select(N0&N1, X, Y):
-// %N0 = setcc [eq/ne] %C, 0
-// %N1 = setcc [any_cond] %A, %B
-// %And = and %N0, %N1
-// %Res = czero.eqz %And, %X
-bool RISCVTargetLowering::shouldNormalizeToSelectSequence(LLVMContext &, EVT VT,
-                                                          SDNode *N) const {
-  if (Subtarget.hasCZEROLike()) {
-    assert(
-        N->getOpcode() == ISD::SELECT &&
-        "shouldNormalizeToSelectSequence() called with non-SELECT operation");
-    const SDValue CondV = N->getOperand(0);
-    const SDValue FalseV = N->getOperand(2);
-    if (CondV.hasOneUse() && isCzeroCompatible(CondV) && isNullConstant(FalseV))
-      return true;
-  }
-  return false;
-}
-
-bool RISCVTargetLowering::hasConditionalZero() const {
-  return Subtarget.hasCZEROLike();
-}
-
 bool RISCVTargetLowering::isLegalElementTypeForRVV(EVT ScalarTy) const {
   if (!ScalarTy.isSimple())
     return false;
@@ -16161,25 +16117,6 @@ static SDValue reverseZExtICmpCombine(SDNode *N, SelectionDAG &DAG,
   return DAG.getNode(ISD::ZERO_EXTEND, DL, VT, Res);
 }
 
-static SDValue combineANDOfSetCCToCZERO(SDNode *N, SelectionDAG &DAG,
-                                        const RISCVSubtarget &Subtarget) {
-  if (Subtarget.hasCZEROLike()) {
-    // (and (i1) f, (setcc c, 0, ne)) -> (select c, f, 0) -> (czero.nez f, c)
-    // (and (i1) f, (setcc c, 0, eq)) -> (select c, 0, f) -> (czero.eqz f, c)
-    // (and (setcc c, 0, ne), (i1) g) -> (select c, g, 0) -> (czero.nez g, c)
-    // (and (setcc c, 0, eq), (i1) g) -> (select c, 0, g) -> (czero.eqz g, c)
-    const bool CzeroOp1 = isCzeroCompatible(N->getOperand(1));
-    if (CzeroOp1 || isCzeroCompatible(N->getOperand(0))) {
-      const SDValue I1Op = CzeroOp1 ? N->getOperand(0) : N->getOperand(1);
-      const SDValue SetCCOp = CzeroOp1 ? N->getOperand(1) : N->getOperand(0);
-      SDLoc DL(N);
-      return DAG.getNode(ISD::SELECT, DL, MVT::i1, SetCCOp, I1Op,
-                         DAG.getConstant(0, DL, MVT::i1));
-    }
-  }
-  return SDValue();
-}
-
 static SDValue reduceANDOfAtomicLoad(SDNode *N,
                                      TargetLowering::DAGCombinerInfo &DCI) {
   SelectionDAG &DAG = DCI.DAG;
@@ -16243,8 +16180,7 @@ static SDValue performANDCombine(SDNode *N,
 
   if (SDValue V = reverseZExtICmpCombine(N, DAG, Subtarget))
     return V;
-  if (SDValue V = combineANDOfSetCCToCZERO(N, DAG, Subtarget))
-    return V;
+
   if (SDValue V = combineBinOpToReduce(N, DAG, Subtarget))
     return V;
   if (SDValue V = combineBinOpOfExtractToReduceTree(N, DAG, Subtarget))
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.h b/llvm/lib/Target/RISCV/RISCVISelLowering.h
index 7a2eca41b4955..9e3e2a9443625 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.h
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.h
@@ -603,10 +603,13 @@ class RISCVTargetLowering : public TargetLowering {
   /// this override can be removed.
   bool mergeStoresAfterLegalization(EVT VT) const override;
 
-  bool shouldNormalizeToSelectSequence(LLVMContext &, EVT VT,
-                                       SDNode *N) const override;
-
-  bool hasConditionalZero() const override;
+  /// Disable normalizing
+  /// select(N0&N1, X, Y) => select(N0, select(N1, X, Y), Y) and
+  /// select(N0|N1, X, Y) => select(N0, select(N1, X, Y, Y))
+  /// RISC-V doesn't have flags so it's better to perform the and/or in a GPR.
+  bool shouldNormalizeToSelectSequence(LLVMContext &, EVT) const override {
+    return false;
+  }
 
   /// Disables storing and loading vectors by default when there are function
   /// calls between the load and store, since these are more expensive than just
diff --git a/llvm/test/CodeGen/RISCV/zicond-opts.ll b/llvm/test/CodeGen/RISCV/zicond-opts.ll
index baa6bcb284098..c74469fc0e8ce 100644
--- a/llvm/test/CodeGen/RISCV/zicond-opts.ll
+++ b/llvm/test/CodeGen/RISCV/zicond-opts.ll
@@ -8,14 +8,16 @@ define i32 @icmp_and(i64 %x, i64 %y) {
 ; RV32ZICOND:       # %bb.0:
 ; RV32ZICOND-NEXT:    or a2, a2, a3
 ; RV32ZICOND-NEXT:    or a0, a0, a1
+; RV32ZICOND-NEXT:    snez a1, a2
 ; RV32ZICOND-NEXT:    snez a0, a0
-; RV32ZICOND-NEXT:    czero.eqz a0, a0, a2
+; RV32ZICOND-NEXT:    and a0, a0, a1
 ; RV32ZICOND-NEXT:    ret
 ;
 ; RV64ZICOND-LABEL: icmp_and:
 ; RV64ZICOND:       # %bb.0:
+; RV64ZICOND-NEXT:    snez a1, a1
 ; RV64ZICOND-NEXT:    snez a0, a0
-; RV64ZICOND-NEXT:    czero.eqz a0, a0, a1
+; RV64ZICOND-NEXT:    and a0, a0, a1
 ; RV64ZICOND-NEXT:    ret
   %3 = icmp ne i64 %y, 0
   %4 = icmp ne i64 %x, 0
@@ -30,18 +32,20 @@ define i32 @icmp_and_select(i64 %x, i64 %y, i32 %z) {
 ; RV32ZICOND:       # %bb.0:
 ; RV32ZICOND-NEXT:    sgtz a5, a3
 ; RV32ZICOND-NEXT:    snez a2, a2
-; RV32ZICOND-NEXT:    czero.eqz a5, a5, a3
-; RV32ZICOND-NEXT:    czero.nez a2, a2, a3
-; RV32ZICOND-NEXT:    or a2, a2, a5
 ; RV32ZICOND-NEXT:    or a0, a0, a1
-; RV32ZICOND-NEXT:    czero.eqz a0, a2, a0
+; RV32ZICOND-NEXT:    czero.eqz a1, a5, a3
+; RV32ZICOND-NEXT:    czero.nez a2, a2, a3
+; RV32ZICOND-NEXT:    or a1, a2, a1
+; RV32ZICOND-NEXT:    snez a0, a0
+; RV32ZICOND-NEXT:    and a0, a0, a1
 ; RV32ZICOND-NEXT:    czero.eqz a0, a4, a0
 ; RV32ZICOND-NEXT:    ret
 ;
 ; RV64ZICOND-LABEL: icmp_and_select:
 ; RV64ZICOND:       # %bb.0:
 ; RV64ZICOND-NEXT:    sgtz a1, a1
-; RV64ZICOND-NEXT:    czero.eqz a0, a1, a0
+; RV64ZICOND-NEXT:    snez a0, a0
+; RV64ZICOND-NEXT:    and a0, a0, a1
 ; RV64ZICOND-NEXT:    czero.eqz a0, a2, a0
 ; RV64ZICOND-NEXT:    ret
   %3 = icmp sgt i64 %y, 0
@@ -57,17 +61,21 @@ define i32 @icmp_and_and(i64 %x, i64 %y, i64 %z) {
 ; RV32ZICOND:       # %bb.0:
 ; RV32ZICOND-NEXT:    or a2, a2, a3
 ; RV32ZICOND-NEXT:    or a0, a0, a1
-; RV32ZICOND-NEXT:    snez a1, a2
-; RV32ZICOND-NEXT:    czero.eqz a0, a1, a0
 ; RV32ZICOND-NEXT:    or a4, a4, a5
-; RV32ZICOND-NEXT:    czero.eqz a0, a0, a4
+; RV32ZICOND-NEXT:    snez a1, a2
+; RV32ZICOND-NEXT:    snez a0, a0
+; RV32ZICOND-NEXT:    and a0, a1, a0
+; RV32ZICOND-NEXT:    snez a1, a4
+; RV32ZICOND-NEXT:    and a0, a1, a0
 ; RV32ZICOND-NEXT:    ret
 ;
 ; RV64ZICOND-LABEL: icmp_and_and:
 ; RV64ZICOND:       # %bb.0:
 ; RV64ZICOND-NEXT:    snez a1, a1
-; RV64ZICOND-NEXT:    czero.eqz a0, a1, a0
-; RV64ZICOND-NEXT:    czero.eqz a0, a0, a2
+; RV64ZICOND-NEXT:    snez a0, a0
+; RV64ZICOND-NEXT:    and a0, a1, a0
+; RV64ZICOND-NEXT:    snez a1, a2
+; RV64ZICOND-NEXT:    and a0, a1, a0
 ; RV64ZICOND-NEXT:    ret
   %4 = icmp ne i64 %y, 0
   %5 = icmp ne i64 %x, 0

>From 6adb9d575ec854d2da53f92b2f058659451ebec2 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Thu, 6 Nov 2025 15:07:25 -0800
Subject: [PATCH 10/10] Implement fully in MachineCombine

---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 43 ++++++++++++++++++++-
 llvm/test/CodeGen/RISCV/zicond-opts.ll      | 34 +++++++---------
 2 files changed, 55 insertions(+), 22 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index e0cf739f67d9b..3dc6c4816501c 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -16117,6 +16117,45 @@ static SDValue reverseZExtICmpCombine(SDNode *N, SelectionDAG &DAG,
   return DAG.getNode(ISD::ZERO_EXTEND, DL, VT, Res);
 }
 
+// (and (i1) f, (setcc c, 0, ne)) -> (czero.nez f, c)
+// (and (i1) f, (setcc c, 0, eq)) -> (czero.eqz f, c)
+// (and (setcc c, 0, ne), (i1) g) -> (czero.nez g, c)
+// (and (setcc c, 0, eq), (i1) g) -> (czero.eqz g, c)
+static SDValue combineANDOfSETCCToCZERO(SDNode *N,
+                                        SelectionDAG &DAG,
+                                        const RISCVSubtarget &Subtarget) {
+  if (!Subtarget.hasCZEROLike())
+    return SDValue();
+
+  SDValue N0 = N->getOperand(0);
+  SDValue N1 = N->getOperand(1);
+
+  auto IsEqualCompZero = [](SDValue &V) -> bool {
+    if (V.getOpcode() == ISD::SETCC && isNullConstant(V.getOperand(1))) {
+      ISD::CondCode CC = cast<CondCodeSDNode>(V.getOperand(2))->get();
+      if (ISD::isIntEqualitySetCC(CC))
+        return true;
+    }
+    return false;
+  };
+
+  if (!IsEqualCompZero(N0))
+    std::swap(N0, N1);
+  if (!IsEqualCompZero(N0))
+    return SDValue();
+
+  KnownBits Known = DAG.computeKnownBits(N1);
+  if (Known.getMaxValue().sgt(1))
+    return SDValue();
+
+  unsigned CzeroOpcode = (cast<CondCodeSDNode>(N0.getOperand(2))->get() == ISD::SETNE) ?
+    RISCVISD::CZERO_EQZ : RISCVISD::CZERO_NEZ;
+
+  EVT VT = N->getValueType(0);
+  SDLoc DL(N);
+  return DAG.getNode(CzeroOpcode, DL, VT, N1, N0.getOperand(0));
+}
+
 static SDValue reduceANDOfAtomicLoad(SDNode *N,
                                      TargetLowering::DAGCombinerInfo &DCI) {
   SelectionDAG &DAG = DCI.DAG;
@@ -16180,7 +16219,9 @@ static SDValue performANDCombine(SDNode *N,
 
   if (SDValue V = reverseZExtICmpCombine(N, DAG, Subtarget))
     return V;
-
+  if (DCI.isAfterLegalizeDAG())
+    if (SDValue V = combineANDOfSETCCToCZERO(N, DAG, Subtarget))
+      return V;
   if (SDValue V = combineBinOpToReduce(N, DAG, Subtarget))
     return V;
   if (SDValue V = combineBinOpOfExtractToReduceTree(N, DAG, Subtarget))
diff --git a/llvm/test/CodeGen/RISCV/zicond-opts.ll b/llvm/test/CodeGen/RISCV/zicond-opts.ll
index c74469fc0e8ce..5095076a53115 100644
--- a/llvm/test/CodeGen/RISCV/zicond-opts.ll
+++ b/llvm/test/CodeGen/RISCV/zicond-opts.ll
@@ -7,17 +7,15 @@ define i32 @icmp_and(i64 %x, i64 %y) {
 ; RV32ZICOND-LABEL: icmp_and:
 ; RV32ZICOND:       # %bb.0:
 ; RV32ZICOND-NEXT:    or a2, a2, a3
+; RV32ZICOND-NEXT:    snez a2, a2
 ; RV32ZICOND-NEXT:    or a0, a0, a1
-; RV32ZICOND-NEXT:    snez a1, a2
-; RV32ZICOND-NEXT:    snez a0, a0
-; RV32ZICOND-NEXT:    and a0, a0, a1
+; RV32ZICOND-NEXT:    czero.eqz a0, a2, a0
 ; RV32ZICOND-NEXT:    ret
 ;
 ; RV64ZICOND-LABEL: icmp_and:
 ; RV64ZICOND:       # %bb.0:
 ; RV64ZICOND-NEXT:    snez a1, a1
-; RV64ZICOND-NEXT:    snez a0, a0
-; RV64ZICOND-NEXT:    and a0, a0, a1
+; RV64ZICOND-NEXT:    czero.eqz a0, a1, a0
 ; RV64ZICOND-NEXT:    ret
   %3 = icmp ne i64 %y, 0
   %4 = icmp ne i64 %x, 0
@@ -32,20 +30,18 @@ define i32 @icmp_and_select(i64 %x, i64 %y, i32 %z) {
 ; RV32ZICOND:       # %bb.0:
 ; RV32ZICOND-NEXT:    sgtz a5, a3
 ; RV32ZICOND-NEXT:    snez a2, a2
-; RV32ZICOND-NEXT:    or a0, a0, a1
-; RV32ZICOND-NEXT:    czero.eqz a1, a5, a3
+; RV32ZICOND-NEXT:    czero.eqz a5, a5, a3
 ; RV32ZICOND-NEXT:    czero.nez a2, a2, a3
-; RV32ZICOND-NEXT:    or a1, a2, a1
-; RV32ZICOND-NEXT:    snez a0, a0
-; RV32ZICOND-NEXT:    and a0, a0, a1
+; RV32ZICOND-NEXT:    or a2, a2, a5
+; RV32ZICOND-NEXT:    or a0, a0, a1
+; RV32ZICOND-NEXT:    czero.eqz a0, a2, a0
 ; RV32ZICOND-NEXT:    czero.eqz a0, a4, a0
 ; RV32ZICOND-NEXT:    ret
 ;
 ; RV64ZICOND-LABEL: icmp_and_select:
 ; RV64ZICOND:       # %bb.0:
 ; RV64ZICOND-NEXT:    sgtz a1, a1
-; RV64ZICOND-NEXT:    snez a0, a0
-; RV64ZICOND-NEXT:    and a0, a0, a1
+; RV64ZICOND-NEXT:    czero.eqz a0, a1, a0
 ; RV64ZICOND-NEXT:    czero.eqz a0, a2, a0
 ; RV64ZICOND-NEXT:    ret
   %3 = icmp sgt i64 %y, 0
@@ -61,21 +57,17 @@ define i32 @icmp_and_and(i64 %x, i64 %y, i64 %z) {
 ; RV32ZICOND:       # %bb.0:
 ; RV32ZICOND-NEXT:    or a2, a2, a3
 ; RV32ZICOND-NEXT:    or a0, a0, a1
-; RV32ZICOND-NEXT:    or a4, a4, a5
-; RV32ZICOND-NEXT:    snez a1, a2
 ; RV32ZICOND-NEXT:    snez a0, a0
-; RV32ZICOND-NEXT:    and a0, a1, a0
-; RV32ZICOND-NEXT:    snez a1, a4
-; RV32ZICOND-NEXT:    and a0, a1, a0
+; RV32ZICOND-NEXT:    czero.eqz a0, a0, a2
+; RV32ZICOND-NEXT:    or a4, a4, a5
+; RV32ZICOND-NEXT:    czero.eqz a0, a0, a4
 ; RV32ZICOND-NEXT:    ret
 ;
 ; RV64ZICOND-LABEL: icmp_and_and:
 ; RV64ZICOND:       # %bb.0:
-; RV64ZICOND-NEXT:    snez a1, a1
 ; RV64ZICOND-NEXT:    snez a0, a0
-; RV64ZICOND-NEXT:    and a0, a1, a0
-; RV64ZICOND-NEXT:    snez a1, a2
-; RV64ZICOND-NEXT:    and a0, a1, a0
+; RV64ZICOND-NEXT:    czero.eqz a0, a0, a1
+; RV64ZICOND-NEXT:    czero.eqz a0, a0, a2
 ; RV64ZICOND-NEXT:    ret
   %4 = icmp ne i64 %y, 0
   %5 = icmp ne i64 %x, 0



More information about the llvm-commits mailing list