From a5bc43edf36373213c3697014ecefacf8df58668 Mon Sep 17 00:00:00 2001 From: bababuck Date: Mon, 3 Nov 2025 10:25:08 -0800 Subject: [PATCH 01/15] [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 Date: Mon, 3 Nov 2025 08:02:00 -0800 Subject: [PATCH 02/15] [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 &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 Date: Mon, 3 Nov 2025 08:05:57 -0800 Subject: [PATCH 03/15] [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 Date: Mon, 3 Nov 2025 08:06:35 -0800 Subject: [PATCH 04/15] [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(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 Date: Tue, 4 Nov 2025 16:23:06 -0800 Subject: [PATCH 05/15] [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 Date: Tue, 4 Nov 2025 16:24:41 -0800 Subject: [PATCH 06/15] 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 Date: Tue, 4 Nov 2025 16:28:06 -0800 Subject: [PATCH 07/15] 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 Date: Tue, 4 Nov 2025 18:20:38 -0800 Subject: [PATCH 08/15] 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 Date: Thu, 6 Nov 2025 16:37:09 -0800 Subject: [PATCH 09/15] 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 &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(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 Date: Thu, 6 Nov 2025 15:07:25 -0800 Subject: [PATCH 10/15] 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(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(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 From 81af8cf248eb3edcb5e20542ada80dac9ad1e64b Mon Sep 17 00:00:00 2001 From: bababuck Date: Thu, 6 Nov 2025 16:51:08 -0800 Subject: [PATCH 11/15] Lint fixes --- llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index 3dc6c4816501c..1da11dc9bbfbf 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -16121,8 +16121,7 @@ static SDValue reverseZExtICmpCombine(SDNode *N, SelectionDAG &DAG, // (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, +static SDValue combineANDOfSETCCToCZERO(SDNode *N, SelectionDAG &DAG, const RISCVSubtarget &Subtarget) { if (!Subtarget.hasCZEROLike()) return SDValue(); @@ -16148,8 +16147,10 @@ static SDValue combineANDOfSETCCToCZERO(SDNode *N, if (Known.getMaxValue().sgt(1)) return SDValue(); - unsigned CzeroOpcode = (cast(N0.getOperand(2))->get() == ISD::SETNE) ? - RISCVISD::CZERO_EQZ : RISCVISD::CZERO_NEZ; + unsigned CzeroOpcode = + (cast(N0.getOperand(2))->get() == ISD::SETNE) + ? RISCVISD::CZERO_EQZ + : RISCVISD::CZERO_NEZ; EVT VT = N->getValueType(0); SDLoc DL(N); From 42d3e4ec804ee357181c1f095edeed8850f3711d Mon Sep 17 00:00:00 2001 From: bababuck Date: Thu, 6 Nov 2025 16:55:54 -0800 Subject: [PATCH 12/15] Check max unsigned value rather than signed for determining if the value is i1 --- llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index 1da11dc9bbfbf..e6112d0979687 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -16144,7 +16144,7 @@ static SDValue combineANDOfSETCCToCZERO(SDNode *N, SelectionDAG &DAG, return SDValue(); KnownBits Known = DAG.computeKnownBits(N1); - if (Known.getMaxValue().sgt(1)) + if (Known.getMaxValue().ugt(1)) return SDValue(); unsigned CzeroOpcode = From 5e06c5fec5209e0ba353244c639e6017fcf138d6 Mon Sep 17 00:00:00 2001 From: bababuck Date: Thu, 6 Nov 2025 17:03:14 -0800 Subject: [PATCH 13/15] Update other tests --- llvm/test/CodeGen/RISCV/xaluo.ll | 42 +++++++++++++++----------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/llvm/test/CodeGen/RISCV/xaluo.ll b/llvm/test/CodeGen/RISCV/xaluo.ll index bf6802deeffdc..93b68b0a95b48 100644 --- a/llvm/test/CodeGen/RISCV/xaluo.ll +++ b/llvm/test/CodeGen/RISCV/xaluo.ll @@ -1834,13 +1834,12 @@ define zeroext i1 @umulo.i64(i64 %v1, i64 %v2, ptr %res) { ; RV32ZICOND-NEXT: mul a5, a3, a0 ; RV32ZICOND-NEXT: mul a6, a1, a2 ; RV32ZICOND-NEXT: mulhu a7, a0, a2 -; RV32ZICOND-NEXT: snez t0, a3 +; RV32ZICOND-NEXT: add a5, a6, a5 +; RV32ZICOND-NEXT: snez a6, a3 ; RV32ZICOND-NEXT: mulhu a3, a3, a0 -; RV32ZICOND-NEXT: mul t1, a0, a2 +; RV32ZICOND-NEXT: mul t0, a0, a2 ; RV32ZICOND-NEXT: mulhu a0, a1, a2 -; RV32ZICOND-NEXT: snez a1, a1 -; RV32ZICOND-NEXT: add a5, a6, a5 -; RV32ZICOND-NEXT: and a1, a1, t0 +; RV32ZICOND-NEXT: czero.eqz a1, a6, a1 ; RV32ZICOND-NEXT: snez a0, a0 ; RV32ZICOND-NEXT: snez a2, a3 ; RV32ZICOND-NEXT: add a5, a7, a5 @@ -1848,7 +1847,7 @@ define zeroext i1 @umulo.i64(i64 %v1, i64 %v2, ptr %res) { ; RV32ZICOND-NEXT: sltu a1, a5, a7 ; RV32ZICOND-NEXT: or a0, a0, a2 ; RV32ZICOND-NEXT: or a0, a0, a1 -; RV32ZICOND-NEXT: sw t1, 0(a4) +; RV32ZICOND-NEXT: sw t0, 0(a4) ; RV32ZICOND-NEXT: sw a5, 4(a4) ; RV32ZICOND-NEXT: ret ; @@ -3690,11 +3689,10 @@ define i64 @umulo.select.i64(i64 %v1, i64 %v2) { ; RV32ZICOND-NEXT: mul a5, a1, a2 ; RV32ZICOND-NEXT: snez a6, a3 ; RV32ZICOND-NEXT: add a4, a5, a4 -; RV32ZICOND-NEXT: snez a5, a1 -; RV32ZICOND-NEXT: and a5, a5, a6 -; RV32ZICOND-NEXT: mulhu a6, a1, a2 -; RV32ZICOND-NEXT: snez a6, a6 -; RV32ZICOND-NEXT: or a5, a5, a6 +; RV32ZICOND-NEXT: mulhu a5, a1, a2 +; RV32ZICOND-NEXT: czero.eqz a6, a6, a1 +; RV32ZICOND-NEXT: snez a5, a5 +; RV32ZICOND-NEXT: or a5, a6, a5 ; RV32ZICOND-NEXT: mulhu a6, a0, a2 ; RV32ZICOND-NEXT: add a4, a6, a4 ; RV32ZICOND-NEXT: sltu a4, a4, a6 @@ -3783,18 +3781,17 @@ define i1 @umulo.not.i64(i64 %v1, i64 %v2) { ; RV32ZICOND: # %bb.0: # %entry ; RV32ZICOND-NEXT: mul a4, a3, a0 ; RV32ZICOND-NEXT: mul a5, a1, a2 -; RV32ZICOND-NEXT: mulhu a6, a0, a2 +; RV32ZICOND-NEXT: add a4, a5, a4 +; RV32ZICOND-NEXT: mulhu a5, a0, a2 ; RV32ZICOND-NEXT: mulhu a0, a3, a0 ; RV32ZICOND-NEXT: snez a3, a3 ; RV32ZICOND-NEXT: mulhu a2, a1, a2 -; RV32ZICOND-NEXT: snez a1, a1 -; RV32ZICOND-NEXT: add a4, a5, a4 -; RV32ZICOND-NEXT: and a1, a1, a3 +; RV32ZICOND-NEXT: czero.eqz a1, a3, a1 ; RV32ZICOND-NEXT: snez a2, a2 ; RV32ZICOND-NEXT: snez a0, a0 -; RV32ZICOND-NEXT: add a4, a6, a4 +; RV32ZICOND-NEXT: add a4, a5, a4 ; RV32ZICOND-NEXT: or a1, a1, a2 -; RV32ZICOND-NEXT: sltu a2, a4, a6 +; RV32ZICOND-NEXT: sltu a2, a4, a5 ; RV32ZICOND-NEXT: or a0, a1, a0 ; RV32ZICOND-NEXT: or a0, a0, a2 ; RV32ZICOND-NEXT: xori a0, a0, 1 @@ -5156,18 +5153,17 @@ define zeroext i1 @umulo.br.i64(i64 %v1, i64 %v2) { ; RV32ZICOND: # %bb.0: # %entry ; RV32ZICOND-NEXT: mul a4, a3, a0 ; RV32ZICOND-NEXT: mul a5, a1, a2 -; RV32ZICOND-NEXT: mulhu a6, a0, a2 +; RV32ZICOND-NEXT: add a4, a5, a4 +; RV32ZICOND-NEXT: mulhu a5, a0, a2 ; RV32ZICOND-NEXT: mulhu a0, a3, a0 ; RV32ZICOND-NEXT: snez a3, a3 ; RV32ZICOND-NEXT: mulhu a2, a1, a2 -; RV32ZICOND-NEXT: snez a1, a1 -; RV32ZICOND-NEXT: add a4, a5, a4 -; RV32ZICOND-NEXT: and a1, a1, a3 +; RV32ZICOND-NEXT: czero.eqz a1, a3, a1 ; RV32ZICOND-NEXT: snez a2, a2 ; RV32ZICOND-NEXT: snez a0, a0 -; RV32ZICOND-NEXT: add a4, a6, a4 +; RV32ZICOND-NEXT: add a4, a5, a4 ; RV32ZICOND-NEXT: or a1, a1, a2 -; RV32ZICOND-NEXT: sltu a2, a4, a6 +; RV32ZICOND-NEXT: sltu a2, a4, a5 ; RV32ZICOND-NEXT: or a0, a1, a0 ; RV32ZICOND-NEXT: or a0, a0, a2 ; RV32ZICOND-NEXT: beqz a0, .LBB64_2 From 3f2ee0d3fe2e9cd439a5f0922b6a585485abc8cf Mon Sep 17 00:00:00 2001 From: bababuck Date: Thu, 6 Nov 2025 17:44:44 -0800 Subject: [PATCH 14/15] Add new tests for multiple use case --- llvm/test/CodeGen/RISCV/zicond-opts.ll | 87 ++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/llvm/test/CodeGen/RISCV/zicond-opts.ll b/llvm/test/CodeGen/RISCV/zicond-opts.ll index 5095076a53115..4a0ebd326e9aa 100644 --- a/llvm/test/CodeGen/RISCV/zicond-opts.ll +++ b/llvm/test/CodeGen/RISCV/zicond-opts.ll @@ -24,6 +24,93 @@ define i32 @icmp_and(i64 %x, i64 %y) { ret i32 %6 } +; Make sure we choose the replace the single use icmp +define i32 @icmp_and_x_multiple_uses(i64 %x, i64 %y) { +; RV32ZICOND-LABEL: icmp_and_x_multiple_uses: +; RV32ZICOND: # %bb.0: +; RV32ZICOND-NEXT: or a2, a2, a3 +; RV32ZICOND-NEXT: or a0, a0, a1 +; RV32ZICOND-NEXT: snez a1, a2 +; RV32ZICOND-NEXT: snez a2, a0 +; RV32ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV32ZICOND-NEXT: add a0, a0, a2 +; RV32ZICOND-NEXT: ret +; +; RV64ZICOND-LABEL: icmp_and_x_multiple_uses: +; RV64ZICOND: # %bb.0: +; RV64ZICOND-NEXT: snez a1, a1 +; RV64ZICOND-NEXT: snez a2, a0 +; RV64ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV64ZICOND-NEXT: add a0, a0, a2 +; RV64ZICOND-NEXT: ret + %3 = icmp ne i64 %y, 0 + %4 = icmp ne i64 %x, 0 + %5 = and i1 %4, %3 + %6 = zext i1 %5 to i32 + %7 = zext i1 %4 to i32 + %8 = add i32 %6, %7 + ret i32 %8 +} + +; Make sure we choose the replace the single use icmp +define i32 @icmp_and_y_multiple_uses(i64 %x, i64 %y) { +; RV32ZICOND-LABEL: icmp_and_y_multiple_uses: +; RV32ZICOND: # %bb.0: +; RV32ZICOND-NEXT: or a2, a2, a3 +; RV32ZICOND-NEXT: snez a2, a2 +; RV32ZICOND-NEXT: or a0, a0, a1 +; RV32ZICOND-NEXT: czero.eqz a0, a2, a0 +; RV32ZICOND-NEXT: add a0, a0, a2 +; RV32ZICOND-NEXT: ret +; +; RV64ZICOND-LABEL: icmp_and_y_multiple_uses: +; RV64ZICOND: # %bb.0: +; RV64ZICOND-NEXT: snez a1, a1 +; RV64ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV64ZICOND-NEXT: add a0, a0, a1 +; RV64ZICOND-NEXT: ret + %3 = icmp ne i64 %y, 0 + %4 = icmp ne i64 %x, 0 + %5 = and i1 %4, %3 + %6 = zext i1 %5 to i32 + %7 = zext i1 %3 to i32 + %8 = add i32 %6, %7 + ret i32 %8 +} + +; Both icmp's have multiple uses, don't optimize +define i32 @icmp_and_xy_multiple_uses(i64 %x, i64 %y) { +; RV32ZICOND-LABEL: icmp_and_xy_multiple_uses: +; RV32ZICOND: # %bb.0: +; RV32ZICOND-NEXT: or a2, a2, a3 +; RV32ZICOND-NEXT: or a0, a0, a1 +; RV32ZICOND-NEXT: snez a1, a2 +; RV32ZICOND-NEXT: snez a2, a0 +; RV32ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV32ZICOND-NEXT: add a1, a1, a2 +; RV32ZICOND-NEXT: add a0, a0, a1 +; RV32ZICOND-NEXT: ret +; +; RV64ZICOND-LABEL: icmp_and_xy_multiple_uses: +; RV64ZICOND: # %bb.0: +; RV64ZICOND-NEXT: snez a1, a1 +; RV64ZICOND-NEXT: snez a2, a0 +; RV64ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV64ZICOND-NEXT: add a1, a1, a2 +; RV64ZICOND-NEXT: add a0, a0, a1 +; RV64ZICOND-NEXT: ret + %3 = icmp ne i64 %y, 0 + %4 = icmp ne i64 %x, 0 + %5 = and i1 %4, %3 + %6 = zext i1 %5 to i32 + %7 = zext i1 %3 to i32 + %8 = zext i1 %4 to i32 + %9 = add i32 %6, %7 + %10 = add i32 %9, %8 + ret i32 %10 +} + + ; (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: From d0af9b197db01a1b2d83cec1b019d0fd0c0518e7 Mon Sep 17 00:00:00 2001 From: bababuck Date: Thu, 6 Nov 2025 17:54:35 -0800 Subject: [PATCH 15/15] Only optimize if the SNEQ/SEQ has a single use --- llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 4 +-- llvm/test/CodeGen/RISCV/zicond-opts.ll | 30 ++++++++++----------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index e6112d0979687..fa73d8fb665d6 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -16138,9 +16138,9 @@ static SDValue combineANDOfSETCCToCZERO(SDNode *N, SelectionDAG &DAG, return false; }; - if (!IsEqualCompZero(N0)) + if (!IsEqualCompZero(N0) || !N0.hasOneUse()) std::swap(N0, N1); - if (!IsEqualCompZero(N0)) + if (!IsEqualCompZero(N0) || !N0.hasOneUse()) return SDValue(); KnownBits Known = DAG.computeKnownBits(N1); diff --git a/llvm/test/CodeGen/RISCV/zicond-opts.ll b/llvm/test/CodeGen/RISCV/zicond-opts.ll index 4a0ebd326e9aa..6db90d8698dd3 100644 --- a/llvm/test/CodeGen/RISCV/zicond-opts.ll +++ b/llvm/test/CodeGen/RISCV/zicond-opts.ll @@ -30,18 +30,16 @@ define i32 @icmp_and_x_multiple_uses(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 a2, a0 -; RV32ZICOND-NEXT: czero.eqz a0, a1, a0 -; RV32ZICOND-NEXT: add a0, a0, a2 +; RV32ZICOND-NEXT: snez a0, a0 +; RV32ZICOND-NEXT: czero.eqz a1, a0, a2 +; RV32ZICOND-NEXT: add a0, a1, a0 ; RV32ZICOND-NEXT: ret ; ; RV64ZICOND-LABEL: icmp_and_x_multiple_uses: ; RV64ZICOND: # %bb.0: -; RV64ZICOND-NEXT: snez a1, a1 -; RV64ZICOND-NEXT: snez a2, a0 -; RV64ZICOND-NEXT: czero.eqz a0, a1, a0 -; RV64ZICOND-NEXT: add a0, a0, a2 +; RV64ZICOND-NEXT: snez a0, a0 +; RV64ZICOND-NEXT: czero.eqz a1, a0, a1 +; RV64ZICOND-NEXT: add a0, a1, a0 ; RV64ZICOND-NEXT: ret %3 = icmp ne i64 %y, 0 %4 = icmp ne i64 %x, 0 @@ -85,19 +83,19 @@ define i32 @icmp_and_xy_multiple_uses(i64 %x, i64 %y) { ; RV32ZICOND-NEXT: or a2, a2, a3 ; RV32ZICOND-NEXT: or a0, a0, a1 ; RV32ZICOND-NEXT: snez a1, a2 -; RV32ZICOND-NEXT: snez a2, a0 -; RV32ZICOND-NEXT: czero.eqz a0, a1, a0 -; RV32ZICOND-NEXT: add a1, a1, a2 -; RV32ZICOND-NEXT: add a0, a0, a1 +; RV32ZICOND-NEXT: snez a0, a0 +; RV32ZICOND-NEXT: and a2, a0, a1 +; RV32ZICOND-NEXT: add a0, a1, a0 +; RV32ZICOND-NEXT: add a0, a2, a0 ; RV32ZICOND-NEXT: ret ; ; RV64ZICOND-LABEL: icmp_and_xy_multiple_uses: ; RV64ZICOND: # %bb.0: ; RV64ZICOND-NEXT: snez a1, a1 -; RV64ZICOND-NEXT: snez a2, a0 -; RV64ZICOND-NEXT: czero.eqz a0, a1, a0 -; RV64ZICOND-NEXT: add a1, a1, a2 -; RV64ZICOND-NEXT: add a0, a0, a1 +; RV64ZICOND-NEXT: snez a0, a0 +; RV64ZICOND-NEXT: and a2, a0, a1 +; RV64ZICOND-NEXT: add a0, a1, a0 +; RV64ZICOND-NEXT: add a0, a2, a0 ; RV64ZICOND-NEXT: ret %3 = icmp ne i64 %y, 0 %4 = icmp ne i64 %x, 0