Skip to content

Commit

Permalink
[RISCV] Introduce RISCVISD::CZERO_{EQZ,NEZ} nodes produce them when z…
Browse files Browse the repository at this point in the history
…icond is present in lowerSELECT

This patch is a step towards altering how we handle the emission of
condops. Marking ISD::SELECT as legal is a major change in the codegen
path, and gives few options for maintaining the old codegen path when
it is believed to be better (e.g. a better branchless sequence is
possible using non-zicond instructions, or the branch-based sequence is
preferable).

This removes the existing SelectionDAG patterns and moves the logic into
lowerSELECT. Along some small codegen changes you'll note a few minor
regressions in the generated code quality - this are due to the fact
that by lowering the SELECT node early we miss out on combines that
would kick in later when setcc condcodes that aren't natively supported
have been expanded (thus exposing opportunities for optimisation by
performing logical negation and swapping truev/falsev). I've opted to
split out work that addresses these into follow-on patches (especially
as zicond is still 'experimental').

matchSetCC is a straight-forward translation from the version in
RISCVISelDAGToDAG. Ideally, in the future it can be converted to a
helper shared between both files.

Differential Revision: https://reviews.llvm.org/D155083
  • Loading branch information
asb committed Jul 14, 2023
1 parent b6c2f10 commit 5c5a1a2
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 90 deletions.
123 changes: 122 additions & 1 deletion llvm/lib/Target/RISCV/RISCVISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
if (Subtarget.is64Bit())
setOperationAction(ISD::ABS, MVT::i32, Custom);

if (!Subtarget.hasStdExtZicond() && !Subtarget.hasVendorXVentanaCondOps() &&
if (!Subtarget.hasVendorXVentanaCondOps() &&
!Subtarget.hasVendorXTHeadCondMov())
setOperationAction(ISD::SELECT, XLenVT, Custom);

Expand Down Expand Up @@ -5762,6 +5762,63 @@ static SDValue combineSelectToBinOp(SDNode *N, SelectionDAG &DAG,
return SDValue();
}

/// RISC-V doesn't have general instructions for integer setne/seteq, but we can
/// check for equality with 0. This function emits nodes that convert the
/// seteq/setne into something that can be compared with 0.
/// Based on RISCVDAGToDAGISel::selectSETCC but modified to produce
/// target-independent SelectionDAG nodes rather than machine nodes.
static bool selectSETCC(SDValue N, ISD::CondCode ExpectedCCVal, SDValue &Val,
SelectionDAG &DAG) {
assert(ISD::isIntEqualitySetCC(ExpectedCCVal) &&
"Unexpected condition code!");

// We're looking for a setcc.
if (N->getOpcode() != ISD::SETCC)
return false;

// Must be an equality comparison.
ISD::CondCode CCVal = cast<CondCodeSDNode>(N->getOperand(2))->get();
if (CCVal != ExpectedCCVal)
return false;

SDValue LHS = N->getOperand(0);
SDValue RHS = N->getOperand(1);

if (!LHS.getValueType().isInteger())
return false;

// If the RHS side is 0, we don't need any extra instructions, return the LHS.
if (isNullConstant(RHS)) {
Val = LHS;
return true;
}

SDLoc DL(N);

if (auto *C = dyn_cast<ConstantSDNode>(RHS)) {
int64_t CVal = C->getSExtValue();
// If the RHS is -2048, we can use xori to produce 0 if the LHS is -2048 and
// non-zero otherwise.
if (CVal == -2048) {
Val = DAG.getNode(ISD::XOR, DL, N->getValueType(0), LHS,
DAG.getConstant(CVal, DL, N->getValueType(0)));
return true;
}
// If the RHS is [-2047,2048], we can use addi with -RHS to produce 0 if the
// LHS is equal to the RHS and non-zero otherwise.
if (isInt<12>(CVal) || CVal == 2048) {
Val = DAG.getNode(ISD::ADD, DL, N->getValueType(0), LHS,
DAG.getConstant(-CVal, DL, N->getValueType(0)));
return true;
}
}

// If nothing else we can XOR the LHS and RHS to produce zero if they are
// equal and a non-zero value if they aren't.
Val = DAG.getNode(ISD::XOR, DL, N->getValueType(0), LHS, RHS);
return true;
}

SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const {
SDValue CondV = Op.getOperand(0);
SDValue TrueV = Op.getOperand(1);
Expand All @@ -5770,6 +5827,68 @@ SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const {
MVT VT = Op.getSimpleValueType();
MVT XLenVT = Subtarget.getXLenVT();

// When Zicond is present, emit CZERO_EQZ and CZERO_NEZ nodes to implement
// the SELECT. Performing the lowering here allows for greater control over
// when CZERO_{EQZ/NEZ} are used vs another branchless sequence or
// RISCVISD::SELECT_CC node (branch-based select).
if (Subtarget.hasStdExtZicond() && VT.isInteger()) {
SDValue NewCondV;
if (selectSETCC(CondV, ISD::SETNE, NewCondV, DAG)) {
if (isNullConstant(FalseV))
// (select (riscv_setne c), t, 0) -> (czero_eqz t, c)
return DAG.getNode(RISCVISD::CZERO_EQZ, DL, VT, TrueV, NewCondV);
else if (isNullConstant(TrueV))
// (select (riscv_setne c), 0, f) -> (czero_nez f, c)
return DAG.getNode(RISCVISD::CZERO_NEZ, DL, VT, FalseV, NewCondV);
// (select (riscv_setne c), t, f) -> (or (czero_eqz t, c), (czero_nez f,
// c)
return DAG.getNode(
ISD::OR, DL, VT,
DAG.getNode(RISCVISD::CZERO_EQZ, DL, VT, TrueV, NewCondV),
DAG.getNode(RISCVISD::CZERO_NEZ, DL, VT, FalseV, NewCondV));
}
if (selectSETCC(CondV, ISD::SETEQ, NewCondV, DAG)) {
if (isNullConstant(FalseV))
// (select (riscv_seteq c), t, 0) -> (czero_nez t, c)
return DAG.getNode(RISCVISD::CZERO_NEZ, DL, VT, TrueV, NewCondV);
else if (isNullConstant(TrueV))
// (select (riscv_seteq c), 0, f) -> (czero_eqz f, c)
return DAG.getNode(RISCVISD::CZERO_EQZ, DL, VT, FalseV, NewCondV);
// (select (riscv_seteq c), t, f) -> (or (czero_eqz f, c), (czero_nez t,
// c)
return DAG.getNode(
ISD::OR, DL, VT,
DAG.getNode(RISCVISD::CZERO_EQZ, DL, VT, FalseV, NewCondV),
DAG.getNode(RISCVISD::CZERO_NEZ, DL, VT, TrueV, NewCondV));
}
if (isNullConstant(FalseV)) {
// (select c, t, 0) -> (czero_eqz t, c)
return DAG.getNode(RISCVISD::CZERO_EQZ, DL, VT, TrueV, CondV);
}
if (isNullConstant(TrueV)) {
// (select c, 0, f) -> (czero_nez f, c)
return DAG.getNode(RISCVISD::CZERO_NEZ, DL, VT, FalseV, CondV);
}
if (TrueV.getOpcode() == ISD::AND &&
(TrueV.getOperand(0) == FalseV || TrueV.getOperand(1) == FalseV)) {
// (select c, (and f, x), f) -> (or (and f, x), (czero_nez f, c))
return DAG.getNode(
ISD::OR, DL, VT, TrueV,
DAG.getNode(RISCVISD::CZERO_NEZ, DL, VT, FalseV, CondV));
}
if (FalseV.getOpcode() == ISD::AND &&
(FalseV.getOperand(0) == TrueV || FalseV.getOperand(1) == TrueV)) {
// (select c, t, (and t, x)) -> (or (czero_eqz t, c), (and t, x))
return DAG.getNode(
ISD::OR, DL, VT, FalseV,
DAG.getNode(RISCVISD::CZERO_EQZ, DL, VT, TrueV, CondV));
}
// (select c, t, f) -> (or (czero_eqz t, c), (czero_nez f, c))
return DAG.getNode(ISD::OR, DL, VT,
DAG.getNode(RISCVISD::CZERO_EQZ, DL, VT, TrueV, CondV),
DAG.getNode(RISCVISD::CZERO_NEZ, DL, VT, FalseV, CondV));
}

// Lower vector SELECTs to VSELECTs by splatting the condition.
if (VT.isVector()) {
MVT SplatCondVT = VT.changeVectorElementType(MVT::i1);
Expand Down Expand Up @@ -15901,6 +16020,8 @@ const char *RISCVTargetLowering::getTargetNodeName(unsigned Opcode) const {
NODE_NAME_CASE(READ_CSR)
NODE_NAME_CASE(WRITE_CSR)
NODE_NAME_CASE(SWAP_CSR)
NODE_NAME_CASE(CZERO_EQZ)
NODE_NAME_CASE(CZERO_NEZ)
}
// clang-format on
return nullptr;
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/Target/RISCV/RISCVISelLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,11 @@ enum NodeType : unsigned {
// the value read before the modification and the new chain pointer.
SWAP_CSR,

// Branchless select operations, matching the semantics of the instructions
// defined in zicond.
CZERO_EQZ,
CZERO_NEZ,

// FP to 32 bit int conversions for RV64. These are used to keep track of the
// result being sign extended to 64 bit. These saturate out of range inputs.
STRICT_FCVT_W_RV64 = ISD::FIRST_TARGET_STRICTFP_OPCODE,
Expand Down
36 changes: 9 additions & 27 deletions llvm/lib/Target/RISCV/RISCVInstrInfoZicond.td
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@
//
//===----------------------------------------------------------------------===//

//===----------------------------------------------------------------------===//
// RISC-V specific DAG Nodes.
//===----------------------------------------------------------------------===//

def riscv_czero_eqz : SDNode<"RISCVISD::CZERO_EQZ", SDTIntBinOp>;
def riscv_czero_nez : SDNode<"RISCVISD::CZERO_NEZ", SDTIntBinOp>;

//===----------------------------------------------------------------------===//
// Instructions
//===----------------------------------------------------------------------===//
Expand All @@ -29,33 +36,8 @@ def CZERO_NEZ : ALU_rr<0b0000111, 0b111, "czero.nez">,
//===----------------------------------------------------------------------===//

let Predicates = [HasStdExtZicond] in {
// Directly use CZERO_EQZ/CZERO_NEZ in case of any of the operands being 0.
def : Pat<(XLenVT (select (XLenVT GPR:$rc), GPR:$rs1, 0)),
(CZERO_EQZ GPR:$rs1, GPR:$rc)>;
def : Pat<(XLenVT (select (XLenVT GPR:$rc), 0, GPR:$rs1)),
(CZERO_NEZ GPR:$rs1, GPR:$rc)>;

def : Pat<(XLenVT (select (riscv_setne (XLenVT GPR:$rc)), GPR:$rs1, 0)),
def : Pat<(XLenVT (riscv_czero_eqz GPR:$rs1, GPR:$rc)),
(CZERO_EQZ GPR:$rs1, GPR:$rc)>;
def : Pat<(XLenVT (select (riscv_seteq (XLenVT GPR:$rc)), GPR:$rs1, 0)),
(CZERO_NEZ GPR:$rs1, GPR:$rc)>;
def : Pat<(XLenVT (select (riscv_setne (XLenVT GPR:$rc)), 0, GPR:$rs1)),
def : Pat<(XLenVT (riscv_czero_nez GPR:$rs1, GPR:$rc)),
(CZERO_NEZ GPR:$rs1, GPR:$rc)>;
def : Pat<(XLenVT (select (riscv_seteq (XLenVT GPR:$rc)), 0, GPR:$rs1)),
(CZERO_EQZ GPR:$rs1, GPR:$rc)>;

// Conditional AND operation patterns.
def : Pat<(XLenVT (select (XLenVT GPR:$rc), (and GPR:$rs1, GPR:$rs2), GPR:$rs1)),
(OR (AND $rs1, $rs2), (CZERO_NEZ $rs1, $rc))>;
def : Pat<(XLenVT (select (XLenVT GPR:$rc), GPR:$rs1, (and GPR:$rs1, GPR:$rs2))),
(OR (AND $rs1, $rs2), (CZERO_EQZ $rs1, $rc))>;

// Basic select pattern that selects between 2 registers.
def : Pat<(XLenVT (select (XLenVT GPR:$rc), GPR:$rs1, GPR:$rs2)),
(OR (CZERO_EQZ $rs1, $rc), (CZERO_NEZ $rs2, $rc))>;

def : Pat<(XLenVT (select (riscv_setne (XLenVT GPR:$rc)), GPR:$rs1, GPR:$rs2)),
(OR (CZERO_EQZ GPR:$rs1, GPR:$rc), (CZERO_NEZ GPR:$rs2, GPR:$rc))>;
def : Pat<(XLenVT (select (riscv_seteq (XLenVT GPR:$rc)), GPR:$rs2, GPR:$rs1)),
(OR (CZERO_EQZ GPR:$rs1, GPR:$rc), (CZERO_NEZ GPR:$rs2, GPR:$rc))>;
} // Predicates = [HasStdExtZicond]
Loading

0 comments on commit 5c5a1a2

Please sign in to comment.