[llvm] [RISCV][ISelLowering] Use Zicond for FP selects on Zfinx/Zdinx (PR #169299)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Nov 24 01:12:01 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-risc-v
Author: None (fennecJ)
<details>
<summary>Changes</summary>
### 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.
---
Full diff: https://github.com/llvm/llvm-project/pull/169299.diff
2 Files Affected:
- (modified) llvm/lib/Target/RISCV/RISCVISelLowering.cpp (+26)
- (added) llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll (+115)
``````````diff
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 19a16197272fe..e01d320e7e1ec 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -9555,6 +9555,32 @@ SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const {
if (SDValue V = lowerSelectToBinOp(Op.getNode(), DAG, Subtarget))
return V;
+ // When there is no cost for GPR <-> FGPR, we can use zicond select for floating value when CondV is int type
+ bool FPinGPR = Subtarget.hasStdExtZfinx() || Subtarget.hasStdExtZdinx();
+ bool UseZicondForFPSel = Subtarget.hasStdExtZicond() && FPinGPR && VT.isFloatingPoint() && CondV.getValueType().isInteger();
+ if (UseZicondForFPSel) {
+ MVT XLenIntVT = Subtarget.getXLenVT();
+
+ auto CastToInt = [&](SDValue V) -> SDValue {
+ if (VT == MVT::f32 && Subtarget.is64Bit()) {
+ return DAG.getNode(RISCVISD::FMV_X_ANYEXTW_RV64, DL, XLenIntVT, V);
+ }
+ return DAG.getBitcast(XLenIntVT, V);
+ };
+
+ SDValue TrueVInt = CastToInt(TrueV);
+ SDValue FalseVInt = CastToInt(FalseV);
+
+ // Emit integer SELECT (lowers to Zicond)
+ SDValue ResultInt = DAG.getNode(ISD::SELECT, DL, XLenIntVT, CondV, TrueVInt, FalseVInt);
+
+ // Convert back to floating VT
+ if (VT == MVT::f32 && Subtarget.is64Bit()) {
+ return DAG.getNode(RISCVISD::FMV_W_X_RV64, DL, VT, ResultInt);
+ }
+ return DAG.getBitcast(VT, ResultInt);
+ }
+
// When Zicond or XVentanaCondOps 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
diff --git a/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll b/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll
new file mode 100644
index 0000000000000..ddce3752584c4
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll
@@ -0,0 +1,115 @@
+; RUN: llc -mtriple=riscv64 -mattr=+zfinx,+zicond -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV64ZFINX_ZICOND
+; RUN: llc -mtriple=riscv64 -mattr=+zfinx -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV64ZFINX_NOZICOND
+; RUN: llc -mtriple=riscv64 -mattr=+zdinx,+zicond -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV64ZDINX_ZICOND
+; RUN: llc -mtriple=riscv64 -mattr=+zdinx -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV64ZDINX_NOZICOND
+; RUN: llc -mtriple=riscv64 -mattr=+f -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV64F
+; RUN: llc -mtriple=riscv64 -mattr=+d -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV64D
+
+; RUN: llc -mtriple=riscv32 -mattr=+zfinx,+zicond -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV32ZFINX_ZICOND
+; RUN: llc -mtriple=riscv32 -mattr=+zfinx -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV32ZFINX_NOZICOND
+; RUN: llc -mtriple=riscv32 -mattr=+f -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV32F
+
+
+; This test checks that floating-point SELECT is lowered through integer
+; SELECT (and thus to Zicond czero.* sequence) when FP values live in GPRs
+; (Zfinx/Zdinx) and Zicond is enabled. When Zicond is disabled, we expect
+; a branch-based lowering instead.
+
+; -----------------------------------------------------------------------------
+; float select with i1 condition (Zfinx)
+; -----------------------------------------------------------------------------
+
+define float @select_f32_i1(i1 %cond, float %t, float %f) nounwind {
+; RV64ZFINX_ZICOND-LABEL: select_f32_i1:
+; RV64ZFINX_ZICOND: czero
+; RV64ZFINX_ZICOND: czero
+; RV64ZFINX_ZICOND: or
+; RV64ZFINX_ZICOND-NOT: b{{(eq|ne)z?}}
+; RV64ZFINX_ZICOND: ret
+
+; RV64ZFINX_NOZICOND-LABEL: select_f32_i1:
+; RV64ZFINX_NOZICOND: b{{(eq|ne)z?}}
+; RV64ZFINX_NOZICOND-NOT: czero.eqz
+; RV64ZFINX_NOZICOND-NOT: czero.nez
+
+; RV64F-LABEL: select_f32_i1:
+; RV64F: b{{(eq|ne)z?}}
+; RV64F-NOT: czero.eqz
+; RV64F-NOT: czero.nez
+
+; RV32ZFINX_ZICOND-LABEL: select_f32_i1:
+; RV32ZFINX_ZICOND: czero
+; RV32ZFINX_ZICOND: czero
+; RV32ZFINX_ZICOND: or
+; RV32ZFINX_ZICOND-NOT: b{{(eq|ne)z?}}
+; RV32ZFINX_ZICOND: ret
+
+; RV32ZFINX_NOZICOND-LABEL: select_f32_i1:
+; RV32ZFINX_NOZICOND: b{{(eq|ne)z?}}
+; RV32ZFINX_NOZICOND-NOT: czero.eqz
+; RV32ZFINX_NOZICOND-NOT: czero.nez
+
+; RV32F-LABEL: select_f32_i1:
+; RV32F: b{{(eq|ne)z?}}
+; RV32F-NOT: czero.eqz
+; RV32F-NOT: czero.nez
+
+entry:
+ %sel = select i1 %cond, float %t, float %f
+ ret float %sel
+}
+
+; -----------------------------------------------------------------------------
+; double select with i1 condition (Zdinx)
+; -----------------------------------------------------------------------------
+
+define double @select_f64_i1(i1 %cond, double %t, double %f) nounwind {
+; RV64ZDINX_ZICOND-LABEL: select_f64_i1:
+; RV64ZDINX_ZICOND: czero
+; RV64ZDINX_ZICOND: czero
+; RV64ZDINX_ZICOND: or
+; RV64ZDINX_ZICOND-NOT: b{{(eq|ne)z?}}
+; RV64ZDINX_ZICOND: ret
+
+; RV64ZDINX_NOZICOND-LABEL: select_f64_i1:
+; RV64ZDINX_NOZICOND: b{{(eq|ne)z?}}
+; RV64ZDINX_NOZICOND-NOT: czero.eqz
+; RV64ZDINX_NOZICOND-NOT: czero.nez
+
+; RV64D-LABEL: select_f64_i1:
+; RV64D: b{{(eq|ne)z?}}
+; RV64D-NOT: czero.eqz
+; RV64D-NOT: czero.nez
+
+entry:
+ %sel = select i1 %cond, double %t, double %f
+ ret double %sel
+}
+
+; -----------------------------------------------------------------------------
+; double select with floating-point compare condition (a > b ? c : d), Zdinx
+; -----------------------------------------------------------------------------
+
+define double @select_f64_fcmp(double %a, double %b, double %c, double %d) nounwind {
+; RV64ZDINX_ZICOND-LABEL: select_f64_fcmp:
+; RV64ZDINX_ZICOND: czero
+; RV64ZDINX_ZICOND: czero
+; RV64ZDINX_ZICOND: or
+; RV64ZDINX_ZICOND-NOT: b{{(eq|ne)z?}}
+; RV64ZDINX_ZICOND: ret
+
+; RV64ZDINX_NOZICOND-LABEL: select_f64_fcmp:
+; RV64ZDINX_NOZICOND: b{{(eq|ne)z?}}
+; RV64ZDINX_NOZICOND-NOT: czero.eqz
+; RV64ZDINX_NOZICOND-NOT: czero.nez
+
+; RV64D-LABEL: select_f64_fcmp:
+; RV64D: b{{(eq|ne)z?}}
+; RV64D-NOT: czero.eqz
+; RV64D-NOT: czero.nez
+
+entry:
+ %cmp = fcmp ogt double %a, %b
+ %sel = select i1 %cmp, double %c, double %d
+ ret double %sel
+}
\ No newline at end of file
``````````
</details>
https://github.com/llvm/llvm-project/pull/169299
More information about the llvm-commits
mailing list