Skip to content

[RISCV] Optimize (and (icmp x, 0, eq), (icmp y, 0, eq)) utilizing zicond extension #147627

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions llvm/include/llvm/CodeGen/TargetLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -2449,8 +2449,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())
Expand All @@ -2462,6 +2462,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
Expand Down
8 changes: 5 additions & 3 deletions llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12050,7 +12050,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)
Expand All @@ -12061,7 +12063,7 @@ static SDValue foldBoolSelectToLogic(SDNode *N, const SDLoc &DL,
}

// select Cond, 0, F --> and (not Cond), freeze(F)
if (isNullOrNullSplat(T, /* AllowUndefs */ true)) {
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));
Expand Down Expand Up @@ -12214,7 +12216,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()) {
Expand Down
4 changes: 2 additions & 2 deletions llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28262,8 +28262,8 @@ bool AArch64TargetLowering::functionArgumentNeedsConsecutiveRegisters(
return all_equal(ValueVTs);
}

bool AArch64TargetLowering::shouldNormalizeToSelectSequence(LLVMContext &,
EVT) const {
bool AArch64TargetLowering::shouldNormalizeToSelectSequence(LLVMContext &, EVT,
SDNode *) const {
return false;
}

Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/Target/AArch64/AArch64ISelLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -836,7 +836,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;

Expand Down
54 changes: 54 additions & 0 deletions llvm/lib/Target/RISCV/RISCVISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2688,6 +2688,31 @@ bool RISCVTargetLowering::mergeStoresAfterLegalization(EVT VT) const {
(VT.isFixedLengthVector() && VT.getVectorElementType() == MVT::i1);
}

// 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))
// If y == 0 and N0 == setcc(eqz || nez) -> czero (select(N1, X, 0), N0)
bool RISCVTargetLowering::shouldNormalizeToSelectSequence(LLVMContext &, EVT VT,
SDNode *N) const {
if (Subtarget.hasStdExtZicond() || Subtarget.hasVendorXVentanaCondOps()) {
assert(
N->getOpcode() == ISD::SELECT &&
"shouldNormalizeTooSelectSequence() called with non-SELECT operation");
Copy link
Preview

Copilot AI Jul 9, 2025

Choose a reason for hiding this comment

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

Typo in the assertion message: "shouldNormalizeTooSelectSequence" should be "shouldNormalizeToSelectSequence".

Suggested change
"shouldNormalizeTooSelectSequence() called with non-SELECT operation");
"shouldNormalizeToSelectSequence() called with non-SELECT operation");

Copilot uses AI. Check for mistakes.

const SDValue &CondV = N->getOperand(0);
if (CondV.getOpcode() == ISD::SETCC && isNullConstant(N->getOperand(2))) {
ISD::CondCode CondCode = cast<CondCodeSDNode>(CondV.getOperand(2))->get();
if (CondCode == ISD::SETNE || CondCode == ISD::SETEQ) {
return true;
}
}
}
return false;
}

bool RISCVTargetLowering::hasConditionalZero() const {
return Subtarget.hasStdExtZicond() || Subtarget.hasVendorXVentanaCondOps();
}

bool RISCVTargetLowering::isLegalElementTypeForRVV(EVT ScalarTy) const {
if (!ScalarTy.isSimple())
return false;
Expand Down Expand Up @@ -15731,6 +15756,35 @@ static SDValue performANDCombine(SDNode *N,
if (SDValue V = reverseZExtICmpCombine(N, DAG, Subtarget))
return V;

if (Subtarget.hasStdExtZicond() || Subtarget.hasVendorXVentanaCondOps()) {
auto IsCzeroCompatible = [](const SDValue &Op0,
const SDValue &Op1) -> bool {
if (Op0.getValueType() == MVT::i1 && Op1.getOpcode() == ISD::SETCC &&
isNullConstant(Op1.getOperand(1))) {
ISD::CondCode CondCode = cast<CondCodeSDNode>(Op1.getOperand(2))->get();
return CondCode == ISD::SETNE || CondCode == ISD::SETEQ;
}
return false;
};
// (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)
if (IsCzeroCompatible(N->getOperand(0), N->getOperand(1)) ||
IsCzeroCompatible(N->getOperand(1), N->getOperand(0))) {
const bool CzeroOp1 =
IsCzeroCompatible(N->getOperand(0), N->getOperand(1));
const SDValue &I1Op = CzeroOp1 ? N->getOperand(0) : N->getOperand(1);
const SDValue &SetCCOp = CzeroOp1 ? N->getOperand(1) : N->getOperand(0);

ISD::CondCode CondCode =
cast<CondCodeSDNode>(SetCCOp.getOperand(2))->get();
SDLoc DL(N);
const SDValue &Condition = SetCCOp.getOperand(0);
return DAG.getNode(ISD::SELECT, DL, MVT::i1, SetCCOp, I1Op, DAG.getConstant(0, DL, MVT::i1));
}
}

if (SDValue V = combineBinOpToReduce(N, DAG, Subtarget))
return V;
if (SDValue V = combineBinOpOfExtractToReduceTree(N, DAG, Subtarget))
Expand Down
11 changes: 4 additions & 7 deletions llvm/lib/Target/RISCV/RISCVISelLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -598,13 +598,10 @@ 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) const override {
return false;
}
bool shouldNormalizeToSelectSequence(LLVMContext &, EVT VT,
SDNode *N) const override;

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
Expand Down
20 changes: 7 additions & 13 deletions llvm/test/CodeGen/RISCV/zicond-opts.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -32,21 +30,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
Expand Down
Loading