Skip to content

Commit 2e21bb8

Browse files
authored
[RISCV][ISelLowering] Use Zicond for FP selects on Zfinx/Zdinx (llvm#169299)
### Summary This patch let RISCVTargetLowering::lowerSELECT to lower some floating-point select operations through an integer zicond select when: * Zicond is available, and * FP values live in GPRs (Zfinx/Zdinx), and * Select condition is an integer type. In that scenario there is no extra cost for GPR <-> "FP GPR" moves, so we can implement FP selects with a CZERO-based sequence instead of a branch. For example, for ```c float foo(int cond, float x) { return (cond != 0) ? x : 0.0f; } ``` the current lowering produces: ```asm foo: mv a2, a0 li a0, 0 beqz a2, .LBB0_2 .LBB0_1: mv a0, a1 .LBB0_2: ret ``` With this patch, when targeting rv64ima_zicond_zfinx we instead get: ```asm foo: czero.nez a2, zero, a0 czero.eqz a0, a1, a0 or a0, a2, a0 ret ``` The existing branch-based lowering is preserved for: * targets without Zicond * targets where FP registers are separate (+f, +d without zfinx/zdinx) ### Testing Adds llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll to cover: * RV64 Zfinx/Zicond vs Zfinx without Zicond * RV64 Zdinx/Zicond vs Zdinx without Zicond * RV32 Zfinx/Zicond vs Zfinx without Zicond Also adds baseline RV32F/RV64F/RV64D cases to ensure we still use branches when FP registers are separate. The tests check that: * With Zicond + Zfinx/Zdinx, FP select lowers to a CZERO+OR sequence with no conditional branches. * Without Zicond (or without Zfinx/Zdinx), we still get branch-based code and no czero.* instructions.
1 parent e110abc commit 2e21bb8

File tree

2 files changed

+842
-0
lines changed

2 files changed

+842
-0
lines changed

llvm/lib/Target/RISCV/RISCVISelLowering.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9584,6 +9584,50 @@ SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const {
95849584
if (SDValue V = lowerSelectToBinOp(Op.getNode(), DAG, Subtarget))
95859585
return V;
95869586

9587+
// When there is no cost for GPR <-> FPR, we can use zicond select for
9588+
// floating value when CondV is int type
9589+
bool FPinGPR = Subtarget.hasStdExtZfinx();
9590+
9591+
// We can handle FGPR without spliting into hi/lo parts
9592+
bool FitsInGPR = TypeSize::isKnownLE(VT.getSizeInBits(),
9593+
Subtarget.getXLenVT().getSizeInBits());
9594+
9595+
bool UseZicondForFPSel = Subtarget.hasStdExtZicond() && FPinGPR &&
9596+
VT.isFloatingPoint() && FitsInGPR;
9597+
9598+
if (UseZicondForFPSel) {
9599+
9600+
auto CastToInt = [&](SDValue V) -> SDValue {
9601+
// Treat +0.0 as int 0 to enable single 'czero' instruction generation.
9602+
if (isNullFPConstant(V))
9603+
return DAG.getConstant(0, DL, XLenVT);
9604+
9605+
if (VT == MVT::f16)
9606+
return DAG.getNode(RISCVISD::FMV_X_ANYEXTH, DL, XLenVT, V);
9607+
9608+
if (VT == MVT::f32 && Subtarget.is64Bit())
9609+
return DAG.getNode(RISCVISD::FMV_X_ANYEXTW_RV64, DL, XLenVT, V);
9610+
9611+
return DAG.getBitcast(XLenVT, V);
9612+
};
9613+
9614+
SDValue TrueVInt = CastToInt(TrueV);
9615+
SDValue FalseVInt = CastToInt(FalseV);
9616+
9617+
// Emit integer SELECT (lowers to Zicond)
9618+
SDValue ResultInt =
9619+
DAG.getNode(ISD::SELECT, DL, XLenVT, CondV, TrueVInt, FalseVInt);
9620+
9621+
// Convert back to floating VT
9622+
if (VT == MVT::f32 && Subtarget.is64Bit())
9623+
return DAG.getNode(RISCVISD::FMV_W_X_RV64, DL, VT, ResultInt);
9624+
9625+
if (VT == MVT::f16)
9626+
return DAG.getNode(RISCVISD::FMV_H_X, DL, VT, ResultInt);
9627+
9628+
return DAG.getBitcast(VT, ResultInt);
9629+
}
9630+
95879631
// When Zicond or XVentanaCondOps is present, emit CZERO_EQZ and CZERO_NEZ
95889632
// nodes to implement the SELECT. Performing the lowering here allows for
95899633
// greater control over when CZERO_{EQZ/NEZ} are used vs another branchless

0 commit comments

Comments
 (0)