[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
- Previous message: [llvm] [RISCV] Optimize (and (icmp x, 0, neq), (icmp y, 0, neq)) utilizing zicond extension (PR #166469)
- Next message: [llvm] [RISCV] Optimize (and (icmp x, 0, neq), (icmp y, 0, neq)) utilizing zicond extension (PR #166469)
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
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
- Previous message: [llvm] [RISCV] Optimize (and (icmp x, 0, neq), (icmp y, 0, neq)) utilizing zicond extension (PR #166469)
- Next message: [llvm] [RISCV] Optimize (and (icmp x, 0, neq), (icmp y, 0, neq)) utilizing zicond extension (PR #166469)
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
More information about the llvm-commits
mailing list