From 2623c956ccd37ac1e16f807f4bfc24c639c3c768 Mon Sep 17 00:00:00 2001 From: fennecJ Date: Mon, 24 Nov 2025 16:25:36 +0800 Subject: [PATCH 01/15] [RISCV][ISelLowering] Use Zicond for FP selects on Zfinx/Zdinx When Zfinx or Zdinx is enabled, FP values live in the integer register file, so there is no GPR<->FPR move cost. In this configuration we can profitably lower floating-point `select` nodes through an integer `ISD::SELECT` so that the existing Zicond patterns generate branchless czero/cmov sequences instead of control-flow branches. This covers patterns such as: // Case 1: integer condition float sel(int cond, float a, float b) { return cond ? a : b; } // Case 2: floating-point condition float cmp_sel(float a, float b, float c, float d) { return (a > b) ? c : d; } In Case 1 we bitcast the FP operands to an XLen integer type, form an integer `select` with the original integer condition `cond`, and then bitcast the result back to float, allowing it to be lowered to a Zicond-based cmov/czero sequence. In Case 2 the floating-point compare is already lowered to an integer 0/1 value in a GPR (e.g. `flt.s` / `fle.s` / `feq.s`), so we can reuse that integer result as the Zicond condition and apply the same scheme. The transformation is gated on both Zicond and Zfinx/Zdinx, so classic F/D implementations (where FP values live in a separate FP register file) continue to use the existing branch-based lowering and do not pay for extra GPR<->FPR moves. FP semantics are unchanged: we only reinterpret the FP operands as integers; no numeric conversions are introduced. --- llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 30 +++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index be53f51afe79f..2fa8140d2d846 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -9584,6 +9584,36 @@ 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 From 8ea2e880e08fa9f94d1435c9786e4b53212c4aa5 Mon Sep 17 00:00:00 2001 From: fennecJ Date: Mon, 24 Nov 2025 16:38:11 +0800 Subject: [PATCH 02/15] Add test for zicond zf/dinx select lowering * Floating-point `select` is lowered through Zicond only when both Zicond and Zfinx/Zdinx extensions are enabled. * When both Zicond and Zfinx/Zdinx are enabled, branch-based lowering is replaced by a Zicond-backed integer `select` (no control-flow branches are emitted for these cases). --- .../CodeGen/RISCV/zicond-fp-select-zfinx.ll | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll 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 From 2d43f3780965b7ada3808ddd7d1863e4ffb534c9 Mon Sep 17 00:00:00 2001 From: fennecJ Date: Tue, 25 Nov 2025 01:23:21 +0800 Subject: [PATCH 03/15] Refine FPinGPR checking logic Zdinx implies Zfinx so it's sufficient to check only Zfinx here. --- 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 2fa8140d2d846..da74bd0dc1f0e 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -9586,7 +9586,7 @@ SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const { // 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 FPinGPR = Subtarget.hasStdExtZfinx(); bool UseZicondForFPSel = Subtarget.hasStdExtZicond() && FPinGPR && VT.isFloatingPoint() && CondV.getValueType().isInteger(); From 1336a591862770946a4b06e7aca7cf0df8d2e981 Mon Sep 17 00:00:00 2001 From: fennecJ Date: Tue, 25 Nov 2025 02:53:01 +0800 Subject: [PATCH 04/15] Fix f16 Zicond SELECT lowering with Zhinx --- llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index da74bd0dc1f0e..ad7d90548f771 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -9594,6 +9594,9 @@ SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const { MVT XLenIntVT = Subtarget.getXLenVT(); auto CastToInt = [&](SDValue V) -> SDValue { + if (VT == MVT::f16) { + return DAG.getNode(RISCVISD::FMV_X_ANYEXTH, DL, XLenIntVT, V); + } if (VT == MVT::f32 && Subtarget.is64Bit()) { return DAG.getNode(RISCVISD::FMV_X_ANYEXTW_RV64, DL, XLenIntVT, V); } @@ -9611,6 +9614,9 @@ SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const { if (VT == MVT::f32 && Subtarget.is64Bit()) { return DAG.getNode(RISCVISD::FMV_W_X_RV64, DL, VT, ResultInt); } + if (VT == MVT::f16) { + return DAG.getNode(RISCVISD::FMV_H_X, DL, VT, ResultInt); + } return DAG.getBitcast(VT, ResultInt); } From 6a1e9cc80c43cdb823872fa44f456018cc8646d6 Mon Sep 17 00:00:00 2001 From: fennecJ Date: Tue, 25 Nov 2025 03:06:56 +0800 Subject: [PATCH 05/15] Add unit test for half zicond lowering --- .../CodeGen/RISCV/zicond-fp-select-zfinx.ll | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll b/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll index ddce3752584c4..a8e10a028404f 100644 --- a/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll +++ b/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll @@ -112,4 +112,48 @@ entry: %cmp = fcmp ogt double %a, %b %sel = select i1 %cmp, double %c, double %d ret double %sel +} + +; ----------------------------------------------------------------------------- +; half select with i1 condition (cond ? a : b), Zfinx +; ----------------------------------------------------------------------------- + +define dso_local noundef half @select_half_i1(i1 %cond, half %a, half %b) nounwind { +; RV64ZFINX_ZICOND-LABEL: select_half_i1: +; RV64ZFINX_ZICOND: czero +; RV64ZFINX_ZICOND: czero +; RV64ZFINX_ZICOND: or +; RV64ZFINX_ZICOND-NOT: b{{(eq|ne)z?}} +; RV64ZFINX_ZICOND: ret + +; RV64ZFINX_NOZICOND-LABEL: select_half_i1: +; RV64ZFINX_NOZICOND: b{{(eq|ne)z?}} +; RV64ZFINX_NOZICOND-NOT: czero.eqz +; RV64ZFINX_NOZICOND-NOT: czero.nez + +; RV64F-LABEL: select_half_i1: +; RV64F: b{{(eq|ne)z?}} +; RV64F-NOT: czero.eqz +; RV64F-NOT: czero.nez + +; RV32ZFINX_ZICOND-LABEL: select_half_i1: +; RV32ZFINX_ZICOND: czero +; RV32ZFINX_ZICOND: czero +; RV32ZFINX_ZICOND: or +; RV32ZFINX_ZICOND-NOT: b{{(eq|ne)z?}} +; RV32ZFINX_ZICOND: ret + +; RV32ZFINX_NOZICOND-LABEL: select_half_i1: +; RV32ZFINX_NOZICOND: b{{(eq|ne)z?}} +; RV32ZFINX_NOZICOND-NOT: czero.eqz +; RV32ZFINX_NOZICOND-NOT: czero.nez + +; RV32F-LABEL: select_half_i1: +; RV32F: b{{(eq|ne)z?}} +; RV32F-NOT: czero.eqz +; RV32F-NOT: czero.nez + +entry: + %sel = select i1 %cond, half %a, half %b + ret half %sel } \ No newline at end of file From aa4eba6d535e80fec1b3705274871793a8ad81d7 Mon Sep 17 00:00:00 2001 From: fennecJ Date: Tue, 25 Nov 2025 03:42:47 +0800 Subject: [PATCH 06/15] Revise zicond with zfinx implement * CondV should always be int, no need to check * Omit the if statement bracket when there is only oneline body --- llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 22 ++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index ad7d90548f771..e1bff48fa8d5d 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -9587,19 +9587,19 @@ SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const { // 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(); - bool UseZicondForFPSel = Subtarget.hasStdExtZicond() && FPinGPR && - VT.isFloatingPoint() && - CondV.getValueType().isInteger(); + bool UseZicondForFPSel = + Subtarget.hasStdExtZicond() && FPinGPR && VT.isFloatingPoint(); + if (UseZicondForFPSel) { MVT XLenIntVT = Subtarget.getXLenVT(); auto CastToInt = [&](SDValue V) -> SDValue { - if (VT == MVT::f16) { + if (VT == MVT::f16) return DAG.getNode(RISCVISD::FMV_X_ANYEXTH, DL, XLenIntVT, V); - } - if (VT == MVT::f32 && Subtarget.is64Bit()) { + + if (VT == MVT::f32 && Subtarget.is64Bit()) return DAG.getNode(RISCVISD::FMV_X_ANYEXTW_RV64, DL, XLenIntVT, V); - } + return DAG.getBitcast(XLenIntVT, V); }; @@ -9611,12 +9611,12 @@ SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const { DAG.getNode(ISD::SELECT, DL, XLenIntVT, CondV, TrueVInt, FalseVInt); // Convert back to floating VT - if (VT == MVT::f32 && Subtarget.is64Bit()) { + if (VT == MVT::f32 && Subtarget.is64Bit()) return DAG.getNode(RISCVISD::FMV_W_X_RV64, DL, VT, ResultInt); - } - if (VT == MVT::f16) { + + if (VT == MVT::f16) return DAG.getNode(RISCVISD::FMV_H_X, DL, VT, ResultInt); - } + return DAG.getBitcast(VT, ResultInt); } From 1682fb67862b38c563447b3aed4f43f3f5eac597 Mon Sep 17 00:00:00 2001 From: fennecJ Date: Tue, 25 Nov 2025 04:03:03 +0800 Subject: [PATCH 07/15] Update test file via update_llc_test_checks.py --- .../CodeGen/RISCV/zicond-fp-select-zfinx.ll | 342 ++++++++++++------ 1 file changed, 239 insertions(+), 103 deletions(-) diff --git a/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll b/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll index a8e10a028404f..e3b1449925ba0 100644 --- a/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll +++ b/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll @@ -1,13 +1,16 @@ -; 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 +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6 +; Zicond with zfinx(implies by zdinx) ; 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 +; Zicond with zfinx(implies by zhinx) +; RUN: llc -mtriple=riscv64 -mattr=+zhinx,+zicond -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV64ZHINX_ZICOND + +; Baseline with classic FP registers (no *inx); zicond select should NOT trigger +; RUN: llc -mtriple=riscv64 -mattr=+f,+d -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV64FD + +; Check same optimize work on 32bit machine ; 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 @@ -20,40 +23,57 @@ ; ----------------------------------------------------------------------------- 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 - +; RV64ZDINX_ZICOND-LABEL: select_f32_i1: +; RV64ZDINX_ZICOND: # %bb.0: # %entry +; RV64ZDINX_ZICOND-NEXT: # kill: def $x12_w killed $x12_w def $x12 +; RV64ZDINX_ZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV64ZDINX_ZICOND-NEXT: andi a0, a0, 1 +; RV64ZDINX_ZICOND-NEXT: czero.nez a2, a2, a0 +; RV64ZDINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV64ZDINX_ZICOND-NEXT: or a0, a0, a2 +; RV64ZDINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV64ZDINX_ZICOND-NEXT: ret +; +; RV64ZDINX_NOZICOND-LABEL: select_f32_i1: +; RV64ZDINX_NOZICOND: # %bb.0: # %entry +; RV64ZDINX_NOZICOND-NEXT: andi a3, a0, 1 +; RV64ZDINX_NOZICOND-NEXT: mv a0, a1 +; RV64ZDINX_NOZICOND-NEXT: bnez a3, .LBB0_2 +; RV64ZDINX_NOZICOND-NEXT: # %bb.1: # %entry +; RV64ZDINX_NOZICOND-NEXT: mv a0, a2 +; RV64ZDINX_NOZICOND-NEXT: .LBB0_2: # %entry +; RV64ZDINX_NOZICOND-NEXT: ret +; +; RV64ZHINX_ZICOND-LABEL: select_f32_i1: +; RV64ZHINX_ZICOND: # %bb.0: # %entry +; RV64ZHINX_ZICOND-NEXT: # kill: def $x12_w killed $x12_w def $x12 +; RV64ZHINX_ZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV64ZHINX_ZICOND-NEXT: andi a0, a0, 1 +; RV64ZHINX_ZICOND-NEXT: czero.nez a2, a2, a0 +; RV64ZHINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV64ZHINX_ZICOND-NEXT: or a0, a0, a2 +; RV64ZHINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV64ZHINX_ZICOND-NEXT: ret +; +; RV64FD-LABEL: select_f32_i1: +; RV64FD: # %bb.0: # %entry +; RV64FD-NEXT: andi a0, a0, 1 +; RV64FD-NEXT: bnez a0, .LBB0_2 +; RV64FD-NEXT: # %bb.1: # %entry +; RV64FD-NEXT: fmv.s fa0, fa1 +; RV64FD-NEXT: .LBB0_2: # %entry +; RV64FD-NEXT: ret +; ; 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 - +; RV32ZFINX_ZICOND: # %bb.0: # %entry +; RV32ZFINX_ZICOND-NEXT: # kill: def $x12_w killed $x12_w def $x12 +; RV32ZFINX_ZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV32ZFINX_ZICOND-NEXT: andi a0, a0, 1 +; RV32ZFINX_ZICOND-NEXT: czero.nez a2, a2, a0 +; RV32ZFINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV32ZFINX_ZICOND-NEXT: or a0, a0, a2 +; RV32ZFINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV32ZFINX_ZICOND-NEXT: ret entry: %sel = select i1 %cond, float %t, float %f ret float %sel @@ -65,22 +85,50 @@ entry: 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_ZICOND: # %bb.0: # %entry +; RV64ZDINX_ZICOND-NEXT: andi a0, a0, 1 +; RV64ZDINX_ZICOND-NEXT: czero.nez a2, a2, a0 +; RV64ZDINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV64ZDINX_ZICOND-NEXT: or a0, a0, a2 +; RV64ZDINX_ZICOND-NEXT: 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 - +; RV64ZDINX_NOZICOND: # %bb.0: # %entry +; RV64ZDINX_NOZICOND-NEXT: andi a3, a0, 1 +; RV64ZDINX_NOZICOND-NEXT: mv a0, a1 +; RV64ZDINX_NOZICOND-NEXT: bnez a3, .LBB1_2 +; RV64ZDINX_NOZICOND-NEXT: # %bb.1: # %entry +; RV64ZDINX_NOZICOND-NEXT: mv a0, a2 +; RV64ZDINX_NOZICOND-NEXT: .LBB1_2: # %entry +; RV64ZDINX_NOZICOND-NEXT: ret +; +; RV64ZHINX_ZICOND-LABEL: select_f64_i1: +; RV64ZHINX_ZICOND: # %bb.0: # %entry +; RV64ZHINX_ZICOND-NEXT: andi a0, a0, 1 +; RV64ZHINX_ZICOND-NEXT: czero.nez a2, a2, a0 +; RV64ZHINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV64ZHINX_ZICOND-NEXT: or a0, a0, a2 +; RV64ZHINX_ZICOND-NEXT: ret +; +; RV64FD-LABEL: select_f64_i1: +; RV64FD: # %bb.0: # %entry +; RV64FD-NEXT: andi a0, a0, 1 +; RV64FD-NEXT: bnez a0, .LBB1_2 +; RV64FD-NEXT: # %bb.1: # %entry +; RV64FD-NEXT: fmv.d fa0, fa1 +; RV64FD-NEXT: .LBB1_2: # %entry +; RV64FD-NEXT: ret +; +; RV32ZFINX_ZICOND-LABEL: select_f64_i1: +; RV32ZFINX_ZICOND: # %bb.0: # %entry +; RV32ZFINX_ZICOND-NEXT: andi a0, a0, 1 +; RV32ZFINX_ZICOND-NEXT: czero.nez a3, a3, a0 +; RV32ZFINX_ZICOND-NEXT: czero.eqz a1, a1, a0 +; RV32ZFINX_ZICOND-NEXT: czero.nez a4, a4, a0 +; RV32ZFINX_ZICOND-NEXT: czero.eqz a2, a2, a0 +; RV32ZFINX_ZICOND-NEXT: or a0, a1, a3 +; RV32ZFINX_ZICOND-NEXT: or a1, a2, a4 +; RV32ZFINX_ZICOND-NEXT: ret entry: %sel = select i1 %cond, double %t, double %f ret double %sel @@ -92,22 +140,79 @@ entry: 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_ZICOND: # %bb.0: # %entry +; RV64ZDINX_ZICOND-NEXT: flt.d a0, a1, a0 +; RV64ZDINX_ZICOND-NEXT: czero.nez a1, a3, a0 +; RV64ZDINX_ZICOND-NEXT: czero.eqz a0, a2, a0 +; RV64ZDINX_ZICOND-NEXT: or a0, a0, a1 +; RV64ZDINX_ZICOND-NEXT: 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 - +; RV64ZDINX_NOZICOND: # %bb.0: # %entry +; RV64ZDINX_NOZICOND-NEXT: flt.d a1, a1, a0 +; RV64ZDINX_NOZICOND-NEXT: mv a0, a2 +; RV64ZDINX_NOZICOND-NEXT: bnez a1, .LBB2_2 +; RV64ZDINX_NOZICOND-NEXT: # %bb.1: # %entry +; RV64ZDINX_NOZICOND-NEXT: mv a0, a3 +; RV64ZDINX_NOZICOND-NEXT: .LBB2_2: # %entry +; RV64ZDINX_NOZICOND-NEXT: ret +; +; RV64ZHINX_ZICOND-LABEL: select_f64_fcmp: +; RV64ZHINX_ZICOND: # %bb.0: # %entry +; RV64ZHINX_ZICOND-NEXT: addi sp, sp, -32 +; RV64ZHINX_ZICOND-NEXT: sd ra, 24(sp) # 8-byte Folded Spill +; RV64ZHINX_ZICOND-NEXT: sd s0, 16(sp) # 8-byte Folded Spill +; RV64ZHINX_ZICOND-NEXT: sd s1, 8(sp) # 8-byte Folded Spill +; RV64ZHINX_ZICOND-NEXT: mv s0, a3 +; RV64ZHINX_ZICOND-NEXT: mv s1, a2 +; RV64ZHINX_ZICOND-NEXT: call __gtdf2 +; RV64ZHINX_ZICOND-NEXT: sgtz a0, a0 +; RV64ZHINX_ZICOND-NEXT: czero.nez a1, s0, a0 +; RV64ZHINX_ZICOND-NEXT: czero.eqz a0, s1, a0 +; RV64ZHINX_ZICOND-NEXT: or a0, a0, a1 +; RV64ZHINX_ZICOND-NEXT: ld ra, 24(sp) # 8-byte Folded Reload +; RV64ZHINX_ZICOND-NEXT: ld s0, 16(sp) # 8-byte Folded Reload +; RV64ZHINX_ZICOND-NEXT: ld s1, 8(sp) # 8-byte Folded Reload +; RV64ZHINX_ZICOND-NEXT: addi sp, sp, 32 +; RV64ZHINX_ZICOND-NEXT: ret +; +; RV64FD-LABEL: select_f64_fcmp: +; RV64FD: # %bb.0: # %entry +; RV64FD-NEXT: flt.d a0, fa1, fa0 +; RV64FD-NEXT: fmv.d fa0, fa2 +; RV64FD-NEXT: bnez a0, .LBB2_2 +; RV64FD-NEXT: # %bb.1: # %entry +; RV64FD-NEXT: fmv.d fa0, fa3 +; RV64FD-NEXT: .LBB2_2: # %entry +; RV64FD-NEXT: ret +; +; RV32ZFINX_ZICOND-LABEL: select_f64_fcmp: +; RV32ZFINX_ZICOND: # %bb.0: # %entry +; RV32ZFINX_ZICOND-NEXT: addi sp, sp, -32 +; RV32ZFINX_ZICOND-NEXT: sw ra, 28(sp) # 4-byte Folded Spill +; RV32ZFINX_ZICOND-NEXT: sw s0, 24(sp) # 4-byte Folded Spill +; RV32ZFINX_ZICOND-NEXT: sw s1, 20(sp) # 4-byte Folded Spill +; RV32ZFINX_ZICOND-NEXT: sw s2, 16(sp) # 4-byte Folded Spill +; RV32ZFINX_ZICOND-NEXT: sw s3, 12(sp) # 4-byte Folded Spill +; RV32ZFINX_ZICOND-NEXT: mv s0, a7 +; RV32ZFINX_ZICOND-NEXT: mv s1, a6 +; RV32ZFINX_ZICOND-NEXT: mv s2, a5 +; RV32ZFINX_ZICOND-NEXT: mv s3, a4 +; RV32ZFINX_ZICOND-NEXT: call __gtdf2 +; RV32ZFINX_ZICOND-NEXT: sgtz a0, a0 +; RV32ZFINX_ZICOND-NEXT: czero.nez a1, s1, a0 +; RV32ZFINX_ZICOND-NEXT: czero.eqz a2, s3, a0 +; RV32ZFINX_ZICOND-NEXT: czero.nez a3, s0, a0 +; RV32ZFINX_ZICOND-NEXT: czero.eqz a4, s2, a0 +; RV32ZFINX_ZICOND-NEXT: or a0, a2, a1 +; RV32ZFINX_ZICOND-NEXT: or a1, a4, a3 +; RV32ZFINX_ZICOND-NEXT: lw ra, 28(sp) # 4-byte Folded Reload +; RV32ZFINX_ZICOND-NEXT: lw s0, 24(sp) # 4-byte Folded Reload +; RV32ZFINX_ZICOND-NEXT: lw s1, 20(sp) # 4-byte Folded Reload +; RV32ZFINX_ZICOND-NEXT: lw s2, 16(sp) # 4-byte Folded Reload +; RV32ZFINX_ZICOND-NEXT: lw s3, 12(sp) # 4-byte Folded Reload +; RV32ZFINX_ZICOND-NEXT: addi sp, sp, 32 +; RV32ZFINX_ZICOND-NEXT: ret entry: %cmp = fcmp ogt double %a, %b %sel = select i1 %cmp, double %c, double %d @@ -119,41 +224,72 @@ entry: ; ----------------------------------------------------------------------------- define dso_local noundef half @select_half_i1(i1 %cond, half %a, half %b) nounwind { -; RV64ZFINX_ZICOND-LABEL: select_half_i1: -; RV64ZFINX_ZICOND: czero -; RV64ZFINX_ZICOND: czero -; RV64ZFINX_ZICOND: or -; RV64ZFINX_ZICOND-NOT: b{{(eq|ne)z?}} -; RV64ZFINX_ZICOND: ret - -; RV64ZFINX_NOZICOND-LABEL: select_half_i1: -; RV64ZFINX_NOZICOND: b{{(eq|ne)z?}} -; RV64ZFINX_NOZICOND-NOT: czero.eqz -; RV64ZFINX_NOZICOND-NOT: czero.nez - -; RV64F-LABEL: select_half_i1: -; RV64F: b{{(eq|ne)z?}} -; RV64F-NOT: czero.eqz -; RV64F-NOT: czero.nez - +; RV64ZDINX_ZICOND-LABEL: select_half_i1: +; RV64ZDINX_ZICOND: # %bb.0: # %entry +; RV64ZDINX_ZICOND-NEXT: # kill: def $x12_w killed $x12_w def $x12 +; RV64ZDINX_ZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV64ZDINX_ZICOND-NEXT: andi a0, a0, 1 +; RV64ZDINX_ZICOND-NEXT: czero.nez a2, a2, a0 +; RV64ZDINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV64ZDINX_ZICOND-NEXT: or a0, a0, a2 +; RV64ZDINX_ZICOND-NEXT: lui a1, 1048560 +; RV64ZDINX_ZICOND-NEXT: or a0, a0, a1 +; RV64ZDINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV64ZDINX_ZICOND-NEXT: ret +; +; RV64ZDINX_NOZICOND-LABEL: select_half_i1: +; RV64ZDINX_NOZICOND: # %bb.0: # %entry +; RV64ZDINX_NOZICOND-NEXT: # kill: def $x12_w killed $x12_w def $x12 +; RV64ZDINX_NOZICOND-NEXT: andi a0, a0, 1 +; RV64ZDINX_NOZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV64ZDINX_NOZICOND-NEXT: bnez a0, .LBB3_2 +; RV64ZDINX_NOZICOND-NEXT: # %bb.1: # %entry +; RV64ZDINX_NOZICOND-NEXT: mv a1, a2 +; RV64ZDINX_NOZICOND-NEXT: .LBB3_2: # %entry +; RV64ZDINX_NOZICOND-NEXT: lui a0, 1048560 +; RV64ZDINX_NOZICOND-NEXT: or a0, a1, a0 +; RV64ZDINX_NOZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV64ZDINX_NOZICOND-NEXT: ret +; +; RV64ZHINX_ZICOND-LABEL: select_half_i1: +; RV64ZHINX_ZICOND: # %bb.0: # %entry +; RV64ZHINX_ZICOND-NEXT: # kill: def $x12_h killed $x12_h def $x12 +; RV64ZHINX_ZICOND-NEXT: # kill: def $x11_h killed $x11_h def $x11 +; RV64ZHINX_ZICOND-NEXT: andi a0, a0, 1 +; RV64ZHINX_ZICOND-NEXT: czero.nez a2, a2, a0 +; RV64ZHINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV64ZHINX_ZICOND-NEXT: or a0, a0, a2 +; RV64ZHINX_ZICOND-NEXT: # kill: def $x10_h killed $x10_h killed $x10 +; RV64ZHINX_ZICOND-NEXT: ret +; +; RV64FD-LABEL: select_half_i1: +; RV64FD: # %bb.0: # %entry +; RV64FD-NEXT: andi a0, a0, 1 +; RV64FD-NEXT: bnez a0, .LBB3_2 +; RV64FD-NEXT: # %bb.1: # %entry +; RV64FD-NEXT: fmv.x.w a0, fa1 +; RV64FD-NEXT: j .LBB3_3 +; RV64FD-NEXT: .LBB3_2: +; RV64FD-NEXT: fmv.x.w a0, fa0 +; RV64FD-NEXT: .LBB3_3: # %entry +; RV64FD-NEXT: lui a1, 1048560 +; RV64FD-NEXT: or a0, a0, a1 +; RV64FD-NEXT: fmv.w.x fa0, a0 +; RV64FD-NEXT: ret +; ; RV32ZFINX_ZICOND-LABEL: select_half_i1: -; RV32ZFINX_ZICOND: czero -; RV32ZFINX_ZICOND: czero -; RV32ZFINX_ZICOND: or -; RV32ZFINX_ZICOND-NOT: b{{(eq|ne)z?}} -; RV32ZFINX_ZICOND: ret - -; RV32ZFINX_NOZICOND-LABEL: select_half_i1: -; RV32ZFINX_NOZICOND: b{{(eq|ne)z?}} -; RV32ZFINX_NOZICOND-NOT: czero.eqz -; RV32ZFINX_NOZICOND-NOT: czero.nez - -; RV32F-LABEL: select_half_i1: -; RV32F: b{{(eq|ne)z?}} -; RV32F-NOT: czero.eqz -; RV32F-NOT: czero.nez - +; RV32ZFINX_ZICOND: # %bb.0: # %entry +; RV32ZFINX_ZICOND-NEXT: # kill: def $x12_w killed $x12_w def $x12 +; RV32ZFINX_ZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV32ZFINX_ZICOND-NEXT: andi a0, a0, 1 +; RV32ZFINX_ZICOND-NEXT: czero.nez a2, a2, a0 +; RV32ZFINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV32ZFINX_ZICOND-NEXT: or a0, a0, a2 +; RV32ZFINX_ZICOND-NEXT: lui a1, 1048560 +; RV32ZFINX_ZICOND-NEXT: or a0, a0, a1 +; RV32ZFINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV32ZFINX_ZICOND-NEXT: ret entry: %sel = select i1 %cond, half %a, half %b ret half %sel -} \ No newline at end of file +} From 3e1813552af2bfab9e176553eb4bd3d3cd238a34 Mon Sep 17 00:00:00 2001 From: fennecJ Date: Tue, 25 Nov 2025 11:33:02 +0800 Subject: [PATCH 08/15] Handle RV32 with f64 Zdinx When target machine has only 32 bits, we need to split 64bit f64 VT into 2 32 bits reg for selection. --- llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index e1bff48fa8d5d..285e745aa61ac 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -9593,6 +9593,26 @@ SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const { if (UseZicondForFPSel) { MVT XLenIntVT = Subtarget.getXLenVT(); + // Handle RV32 with f64 (Zdinx): Split into two 32-bit integer selects. + if (VT == MVT::f64 && !Subtarget.is64Bit()) { + SDValue TrueSplit = DAG.getNode(RISCVISD::SplitF64, DL, + DAG.getVTList(MVT::i32, MVT::i32), TrueV); + SDValue FalseSplit = DAG.getNode( + RISCVISD::SplitF64, DL, DAG.getVTList(MVT::i32, MVT::i32), FalseV); + + SDValue TrueLo = TrueSplit.getValue(0); + SDValue TrueHi = TrueSplit.getValue(1); + SDValue FalseLo = FalseSplit.getValue(0); + SDValue FalseHi = FalseSplit.getValue(1); + + SDValue ResLo = + DAG.getNode(ISD::SELECT, DL, MVT::i32, CondV, TrueLo, FalseLo); + SDValue ResHi = + DAG.getNode(ISD::SELECT, DL, MVT::i32, CondV, TrueHi, FalseHi); + + return DAG.getNode(RISCVISD::BuildPairF64, DL, MVT::f64, ResLo, ResHi); + } + auto CastToInt = [&](SDValue V) -> SDValue { if (VT == MVT::f16) return DAG.getNode(RISCVISD::FMV_X_ANYEXTH, DL, XLenIntVT, V); From 23f5ad70613f0b71a75b92cb006203c3a7a73241 Mon Sep 17 00:00:00 2001 From: fennecJ Date: Tue, 25 Nov 2025 11:43:26 +0800 Subject: [PATCH 09/15] Add testcase for rv32zdinx --- .../CodeGen/RISCV/zicond-fp-select-zfinx.ll | 172 +++++++++++++++++- 1 file changed, 169 insertions(+), 3 deletions(-) diff --git a/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll b/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll index e3b1449925ba0..156a97549e9e4 100644 --- a/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll +++ b/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll @@ -1,17 +1,19 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6 ; Zicond with zfinx(implies by zdinx) ; 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=+zdinx -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV64ZDINX_NOZICOND ; Zicond with zfinx(implies by zhinx) ; RUN: llc -mtriple=riscv64 -mattr=+zhinx,+zicond -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV64ZHINX_ZICOND ; Baseline with classic FP registers (no *inx); zicond select should NOT trigger -; RUN: llc -mtriple=riscv64 -mattr=+f,+d -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV64FD +; RUN: llc -mtriple=riscv64 -mattr=+f,+d -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV64FD ; Check same optimize work on 32bit machine ; 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=+zdinx,+zicond -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV32ZDINX_ZICOND +; RUN: llc -mtriple=riscv32 -mattr=+zdinx -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV32ZDINX_NOZICOND ; This test checks that floating-point SELECT is lowered through integer ; SELECT (and thus to Zicond czero.* sequence) when FP values live in GPRs @@ -74,6 +76,37 @@ define float @select_f32_i1(i1 %cond, float %t, float %f) nounwind { ; RV32ZFINX_ZICOND-NEXT: or a0, a0, a2 ; RV32ZFINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 ; RV32ZFINX_ZICOND-NEXT: ret +; +; RV32ZFINX_NOZICOND-LABEL: select_f32_i1: +; RV32ZFINX_NOZICOND: # %bb.0: # %entry +; RV32ZFINX_NOZICOND-NEXT: andi a3, a0, 1 +; RV32ZFINX_NOZICOND-NEXT: mv a0, a1 +; RV32ZFINX_NOZICOND-NEXT: bnez a3, .LBB0_2 +; RV32ZFINX_NOZICOND-NEXT: # %bb.1: # %entry +; RV32ZFINX_NOZICOND-NEXT: mv a0, a2 +; RV32ZFINX_NOZICOND-NEXT: .LBB0_2: # %entry +; RV32ZFINX_NOZICOND-NEXT: ret +; +; RV32ZDINX_ZICOND-LABEL: select_f32_i1: +; RV32ZDINX_ZICOND: # %bb.0: # %entry +; RV32ZDINX_ZICOND-NEXT: # kill: def $x12_w killed $x12_w def $x12 +; RV32ZDINX_ZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV32ZDINX_ZICOND-NEXT: andi a0, a0, 1 +; RV32ZDINX_ZICOND-NEXT: czero.nez a2, a2, a0 +; RV32ZDINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV32ZDINX_ZICOND-NEXT: or a0, a0, a2 +; RV32ZDINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV32ZDINX_ZICOND-NEXT: ret +; +; RV32ZDINX_NOZICOND-LABEL: select_f32_i1: +; RV32ZDINX_NOZICOND: # %bb.0: # %entry +; RV32ZDINX_NOZICOND-NEXT: andi a3, a0, 1 +; RV32ZDINX_NOZICOND-NEXT: mv a0, a1 +; RV32ZDINX_NOZICOND-NEXT: bnez a3, .LBB0_2 +; RV32ZDINX_NOZICOND-NEXT: # %bb.1: # %entry +; RV32ZDINX_NOZICOND-NEXT: mv a0, a2 +; RV32ZDINX_NOZICOND-NEXT: .LBB0_2: # %entry +; RV32ZDINX_NOZICOND-NEXT: ret entry: %sel = select i1 %cond, float %t, float %f ret float %sel @@ -129,6 +162,47 @@ define double @select_f64_i1(i1 %cond, double %t, double %f) nounwind { ; RV32ZFINX_ZICOND-NEXT: or a0, a1, a3 ; RV32ZFINX_ZICOND-NEXT: or a1, a2, a4 ; RV32ZFINX_ZICOND-NEXT: ret +; +; RV32ZFINX_NOZICOND-LABEL: select_f64_i1: +; RV32ZFINX_NOZICOND: # %bb.0: # %entry +; RV32ZFINX_NOZICOND-NEXT: andi a5, a0, 1 +; RV32ZFINX_NOZICOND-NEXT: mv a0, a1 +; RV32ZFINX_NOZICOND-NEXT: bnez a5, .LBB1_2 +; RV32ZFINX_NOZICOND-NEXT: # %bb.1: # %entry +; RV32ZFINX_NOZICOND-NEXT: mv a0, a3 +; RV32ZFINX_NOZICOND-NEXT: mv a2, a4 +; RV32ZFINX_NOZICOND-NEXT: .LBB1_2: # %entry +; RV32ZFINX_NOZICOND-NEXT: mv a1, a2 +; RV32ZFINX_NOZICOND-NEXT: ret +; +; RV32ZDINX_ZICOND-LABEL: select_f64_i1: +; RV32ZDINX_ZICOND: # %bb.0: # %entry +; RV32ZDINX_ZICOND-NEXT: andi a0, a0, 1 +; RV32ZDINX_ZICOND-NEXT: czero.nez a3, a3, a0 +; RV32ZDINX_ZICOND-NEXT: czero.eqz a1, a1, a0 +; RV32ZDINX_ZICOND-NEXT: czero.nez a4, a4, a0 +; RV32ZDINX_ZICOND-NEXT: czero.eqz a2, a2, a0 +; RV32ZDINX_ZICOND-NEXT: or a0, a1, a3 +; RV32ZDINX_ZICOND-NEXT: or a1, a2, a4 +; RV32ZDINX_ZICOND-NEXT: ret +; +; RV32ZDINX_NOZICOND-LABEL: select_f64_i1: +; RV32ZDINX_NOZICOND: # %bb.0: # %entry +; RV32ZDINX_NOZICOND-NEXT: andi a0, a0, 1 +; RV32ZDINX_NOZICOND-NEXT: bnez a0, .LBB1_2 +; RV32ZDINX_NOZICOND-NEXT: # %bb.1: # %entry +; RV32ZDINX_NOZICOND-NEXT: mv a7, a4 +; RV32ZDINX_NOZICOND-NEXT: mv a6, a3 +; RV32ZDINX_NOZICOND-NEXT: mv a4, a6 +; RV32ZDINX_NOZICOND-NEXT: mv a5, a7 +; RV32ZDINX_NOZICOND-NEXT: j .LBB1_3 +; RV32ZDINX_NOZICOND-NEXT: .LBB1_2: +; RV32ZDINX_NOZICOND-NEXT: mv a5, a2 +; RV32ZDINX_NOZICOND-NEXT: mv a4, a1 +; RV32ZDINX_NOZICOND-NEXT: .LBB1_3: # %entry +; RV32ZDINX_NOZICOND-NEXT: mv a0, a4 +; RV32ZDINX_NOZICOND-NEXT: mv a1, a5 +; RV32ZDINX_NOZICOND-NEXT: ret entry: %sel = select i1 %cond, double %t, double %f ret double %sel @@ -213,6 +287,57 @@ define double @select_f64_fcmp(double %a, double %b, double %c, double %d) nounw ; RV32ZFINX_ZICOND-NEXT: lw s3, 12(sp) # 4-byte Folded Reload ; RV32ZFINX_ZICOND-NEXT: addi sp, sp, 32 ; RV32ZFINX_ZICOND-NEXT: ret +; +; RV32ZFINX_NOZICOND-LABEL: select_f64_fcmp: +; RV32ZFINX_NOZICOND: # %bb.0: # %entry +; RV32ZFINX_NOZICOND-NEXT: addi sp, sp, -32 +; RV32ZFINX_NOZICOND-NEXT: sw ra, 28(sp) # 4-byte Folded Spill +; RV32ZFINX_NOZICOND-NEXT: sw s0, 24(sp) # 4-byte Folded Spill +; RV32ZFINX_NOZICOND-NEXT: sw s1, 20(sp) # 4-byte Folded Spill +; RV32ZFINX_NOZICOND-NEXT: sw s2, 16(sp) # 4-byte Folded Spill +; RV32ZFINX_NOZICOND-NEXT: sw s3, 12(sp) # 4-byte Folded Spill +; RV32ZFINX_NOZICOND-NEXT: mv s1, a7 +; RV32ZFINX_NOZICOND-NEXT: mv s3, a6 +; RV32ZFINX_NOZICOND-NEXT: mv s0, a5 +; RV32ZFINX_NOZICOND-NEXT: mv s2, a4 +; RV32ZFINX_NOZICOND-NEXT: call __gtdf2 +; RV32ZFINX_NOZICOND-NEXT: bgtz a0, .LBB2_2 +; RV32ZFINX_NOZICOND-NEXT: # %bb.1: # %entry +; RV32ZFINX_NOZICOND-NEXT: mv s2, s3 +; RV32ZFINX_NOZICOND-NEXT: mv s0, s1 +; RV32ZFINX_NOZICOND-NEXT: .LBB2_2: # %entry +; RV32ZFINX_NOZICOND-NEXT: mv a0, s2 +; RV32ZFINX_NOZICOND-NEXT: mv a1, s0 +; RV32ZFINX_NOZICOND-NEXT: lw ra, 28(sp) # 4-byte Folded Reload +; RV32ZFINX_NOZICOND-NEXT: lw s0, 24(sp) # 4-byte Folded Reload +; RV32ZFINX_NOZICOND-NEXT: lw s1, 20(sp) # 4-byte Folded Reload +; RV32ZFINX_NOZICOND-NEXT: lw s2, 16(sp) # 4-byte Folded Reload +; RV32ZFINX_NOZICOND-NEXT: lw s3, 12(sp) # 4-byte Folded Reload +; RV32ZFINX_NOZICOND-NEXT: addi sp, sp, 32 +; RV32ZFINX_NOZICOND-NEXT: ret +; +; RV32ZDINX_ZICOND-LABEL: select_f64_fcmp: +; RV32ZDINX_ZICOND: # %bb.0: # %entry +; RV32ZDINX_ZICOND-NEXT: flt.d a0, a2, a0 +; RV32ZDINX_ZICOND-NEXT: czero.nez a1, a6, a0 +; RV32ZDINX_ZICOND-NEXT: czero.eqz a2, a4, a0 +; RV32ZDINX_ZICOND-NEXT: czero.nez a3, a7, a0 +; RV32ZDINX_ZICOND-NEXT: czero.eqz a4, a5, a0 +; RV32ZDINX_ZICOND-NEXT: or a0, a2, a1 +; RV32ZDINX_ZICOND-NEXT: or a1, a4, a3 +; RV32ZDINX_ZICOND-NEXT: ret +; +; RV32ZDINX_NOZICOND-LABEL: select_f64_fcmp: +; RV32ZDINX_NOZICOND: # %bb.0: # %entry +; RV32ZDINX_NOZICOND-NEXT: flt.d a0, a2, a0 +; RV32ZDINX_NOZICOND-NEXT: bnez a0, .LBB2_2 +; RV32ZDINX_NOZICOND-NEXT: # %bb.1: # %entry +; RV32ZDINX_NOZICOND-NEXT: mv a4, a6 +; RV32ZDINX_NOZICOND-NEXT: mv a5, a7 +; RV32ZDINX_NOZICOND-NEXT: .LBB2_2: # %entry +; RV32ZDINX_NOZICOND-NEXT: mv a0, a4 +; RV32ZDINX_NOZICOND-NEXT: mv a1, a5 +; RV32ZDINX_NOZICOND-NEXT: ret entry: %cmp = fcmp ogt double %a, %b %sel = select i1 %cmp, double %c, double %d @@ -289,6 +414,47 @@ define dso_local noundef half @select_half_i1(i1 %cond, half %a, half %b) nounwi ; RV32ZFINX_ZICOND-NEXT: or a0, a0, a1 ; RV32ZFINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 ; RV32ZFINX_ZICOND-NEXT: ret +; +; RV32ZFINX_NOZICOND-LABEL: select_half_i1: +; RV32ZFINX_NOZICOND: # %bb.0: # %entry +; RV32ZFINX_NOZICOND-NEXT: # kill: def $x12_w killed $x12_w def $x12 +; RV32ZFINX_NOZICOND-NEXT: andi a0, a0, 1 +; RV32ZFINX_NOZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV32ZFINX_NOZICOND-NEXT: bnez a0, .LBB3_2 +; RV32ZFINX_NOZICOND-NEXT: # %bb.1: # %entry +; RV32ZFINX_NOZICOND-NEXT: mv a1, a2 +; RV32ZFINX_NOZICOND-NEXT: .LBB3_2: # %entry +; RV32ZFINX_NOZICOND-NEXT: lui a0, 1048560 +; RV32ZFINX_NOZICOND-NEXT: or a0, a1, a0 +; RV32ZFINX_NOZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV32ZFINX_NOZICOND-NEXT: ret +; +; RV32ZDINX_ZICOND-LABEL: select_half_i1: +; RV32ZDINX_ZICOND: # %bb.0: # %entry +; RV32ZDINX_ZICOND-NEXT: # kill: def $x12_w killed $x12_w def $x12 +; RV32ZDINX_ZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV32ZDINX_ZICOND-NEXT: andi a0, a0, 1 +; RV32ZDINX_ZICOND-NEXT: czero.nez a2, a2, a0 +; RV32ZDINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV32ZDINX_ZICOND-NEXT: or a0, a0, a2 +; RV32ZDINX_ZICOND-NEXT: lui a1, 1048560 +; RV32ZDINX_ZICOND-NEXT: or a0, a0, a1 +; RV32ZDINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV32ZDINX_ZICOND-NEXT: ret +; +; RV32ZDINX_NOZICOND-LABEL: select_half_i1: +; RV32ZDINX_NOZICOND: # %bb.0: # %entry +; RV32ZDINX_NOZICOND-NEXT: # kill: def $x12_w killed $x12_w def $x12 +; RV32ZDINX_NOZICOND-NEXT: andi a0, a0, 1 +; RV32ZDINX_NOZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV32ZDINX_NOZICOND-NEXT: bnez a0, .LBB3_2 +; RV32ZDINX_NOZICOND-NEXT: # %bb.1: # %entry +; RV32ZDINX_NOZICOND-NEXT: mv a1, a2 +; RV32ZDINX_NOZICOND-NEXT: .LBB3_2: # %entry +; RV32ZDINX_NOZICOND-NEXT: lui a0, 1048560 +; RV32ZDINX_NOZICOND-NEXT: or a0, a1, a0 +; RV32ZDINX_NOZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV32ZDINX_NOZICOND-NEXT: ret entry: %sel = select i1 %cond, half %a, half %b ret half %sel From dafc8b59d7e678dd6e9fe78c12d25ff7cd8bdc9e Mon Sep 17 00:00:00 2001 From: fennecJ Date: Tue, 25 Nov 2025 16:35:18 +0800 Subject: [PATCH 10/15] Skip Zicond for FP sel when we need to split regs This patch disables the Zicond optimization for floating-point selects when the value type exceeds XLen (e.g., f64 on RV32 +zdinx). In these cases, using Zicond requires handling split registers, which results in a higher dynamic instruction count compared to the standard branch-based lowering (e.g., ~8 instructions vs ~5-7 instructions for appended sample code). Thus, there is no benefit to using Zicond here ```asm define double @select_f64_fcmp(double %a, double %b, double %c, double %d) nounwind { entry: %cmp = fcmp ogt double %a, %b %sel = select i1 %cmp, double %c, double %d ret double %sel } ``` Branch version: Executes 5 or 7 instruction ```asm ; RV32ZDINX_NOZICOND-LABEL: select_f64_fcmp: ; RV32ZDINX_NOZICOND: # %bb.0: # %entry ; RV32ZDINX_NOZICOND-NEXT: flt.d a0, a2, a0 ; RV32ZDINX_NOZICOND-NEXT: bnez a0, .LBB2_2 ; RV32ZDINX_NOZICOND-NEXT: # %bb.1: # %entry ; RV32ZDINX_NOZICOND-NEXT: mv a4, a6 ; RV32ZDINX_NOZICOND-NEXT: mv a5, a7 ; RV32ZDINX_NOZICOND-NEXT: .LBB2_2: # %entry ; RV32ZDINX_NOZICOND-NEXT: mv a0, a4 ; RV32ZDINX_NOZICOND-NEXT: mv a1, a5 ; RV32ZDINX_NOZICOND-NEXT: ret ``` Zicond version: Always executes 8 instructions. ```asm ; RV32ZDINX_ZICOND-LABEL: select_f64_fcmp: ; RV32ZDINX_ZICOND: # %bb.0: # %entry ; RV32ZDINX_ZICOND-NEXT: flt.d a0, a2, a0 ; RV32ZDINX_ZICOND-NEXT: czero.nez a1, a6, a0 ; RV32ZDINX_ZICOND-NEXT: czero.eqz a2, a4, a0 ; RV32ZDINX_ZICOND-NEXT: czero.nez a3, a7, a0 ; RV32ZDINX_ZICOND-NEXT: czero.eqz a4, a5, a0 ; RV32ZDINX_ZICOND-NEXT: or a0, a2, a1 ; RV32ZDINX_ZICOND-NEXT: or a1, a4, a3 ; RV32ZDINX_ZICOND-NEXT: ret ``` --- llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 29 ++++------------- .../CodeGen/RISCV/zicond-fp-select-zfinx.ll | 32 ++++++++++++------- 2 files changed, 27 insertions(+), 34 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index 285e745aa61ac..2885874aab659 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -9587,31 +9587,16 @@ SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const { // 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(); - bool UseZicondForFPSel = - Subtarget.hasStdExtZicond() && FPinGPR && VT.isFloatingPoint(); - if (UseZicondForFPSel) { - MVT XLenIntVT = Subtarget.getXLenVT(); - - // Handle RV32 with f64 (Zdinx): Split into two 32-bit integer selects. - if (VT == MVT::f64 && !Subtarget.is64Bit()) { - SDValue TrueSplit = DAG.getNode(RISCVISD::SplitF64, DL, - DAG.getVTList(MVT::i32, MVT::i32), TrueV); - SDValue FalseSplit = DAG.getNode( - RISCVISD::SplitF64, DL, DAG.getVTList(MVT::i32, MVT::i32), FalseV); - - SDValue TrueLo = TrueSplit.getValue(0); - SDValue TrueHi = TrueSplit.getValue(1); - SDValue FalseLo = FalseSplit.getValue(0); - SDValue FalseHi = FalseSplit.getValue(1); + // We can handle FGPR without spliting into hi/lo parts + bool FitsInGPR = TypeSize::isKnownLE(VT.getSizeInBits(), + Subtarget.getXLenVT().getSizeInBits()); - SDValue ResLo = - DAG.getNode(ISD::SELECT, DL, MVT::i32, CondV, TrueLo, FalseLo); - SDValue ResHi = - DAG.getNode(ISD::SELECT, DL, MVT::i32, CondV, TrueHi, FalseHi); + bool UseZicondForFPSel = Subtarget.hasStdExtZicond() && FPinGPR && + VT.isFloatingPoint() && FitsInGPR; - return DAG.getNode(RISCVISD::BuildPairF64, DL, MVT::f64, ResLo, ResHi); - } + if (UseZicondForFPSel) { + MVT XLenIntVT = Subtarget.getXLenVT(); auto CastToInt = [&](SDValue V) -> SDValue { if (VT == MVT::f16) diff --git a/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll b/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll index 156a97549e9e4..5950bb899cef5 100644 --- a/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll +++ b/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll @@ -178,12 +178,19 @@ define double @select_f64_i1(i1 %cond, double %t, double %f) nounwind { ; RV32ZDINX_ZICOND-LABEL: select_f64_i1: ; RV32ZDINX_ZICOND: # %bb.0: # %entry ; RV32ZDINX_ZICOND-NEXT: andi a0, a0, 1 -; RV32ZDINX_ZICOND-NEXT: czero.nez a3, a3, a0 -; RV32ZDINX_ZICOND-NEXT: czero.eqz a1, a1, a0 -; RV32ZDINX_ZICOND-NEXT: czero.nez a4, a4, a0 -; RV32ZDINX_ZICOND-NEXT: czero.eqz a2, a2, a0 -; RV32ZDINX_ZICOND-NEXT: or a0, a1, a3 -; RV32ZDINX_ZICOND-NEXT: or a1, a2, a4 +; RV32ZDINX_ZICOND-NEXT: bnez a0, .LBB1_2 +; RV32ZDINX_ZICOND-NEXT: # %bb.1: # %entry +; RV32ZDINX_ZICOND-NEXT: mv a7, a4 +; RV32ZDINX_ZICOND-NEXT: mv a6, a3 +; RV32ZDINX_ZICOND-NEXT: mv a4, a6 +; RV32ZDINX_ZICOND-NEXT: mv a5, a7 +; RV32ZDINX_ZICOND-NEXT: j .LBB1_3 +; RV32ZDINX_ZICOND-NEXT: .LBB1_2: +; RV32ZDINX_ZICOND-NEXT: mv a5, a2 +; RV32ZDINX_ZICOND-NEXT: mv a4, a1 +; RV32ZDINX_ZICOND-NEXT: .LBB1_3: # %entry +; RV32ZDINX_ZICOND-NEXT: mv a0, a4 +; RV32ZDINX_ZICOND-NEXT: mv a1, a5 ; RV32ZDINX_ZICOND-NEXT: ret ; ; RV32ZDINX_NOZICOND-LABEL: select_f64_i1: @@ -319,12 +326,13 @@ define double @select_f64_fcmp(double %a, double %b, double %c, double %d) nounw ; RV32ZDINX_ZICOND-LABEL: select_f64_fcmp: ; RV32ZDINX_ZICOND: # %bb.0: # %entry ; RV32ZDINX_ZICOND-NEXT: flt.d a0, a2, a0 -; RV32ZDINX_ZICOND-NEXT: czero.nez a1, a6, a0 -; RV32ZDINX_ZICOND-NEXT: czero.eqz a2, a4, a0 -; RV32ZDINX_ZICOND-NEXT: czero.nez a3, a7, a0 -; RV32ZDINX_ZICOND-NEXT: czero.eqz a4, a5, a0 -; RV32ZDINX_ZICOND-NEXT: or a0, a2, a1 -; RV32ZDINX_ZICOND-NEXT: or a1, a4, a3 +; RV32ZDINX_ZICOND-NEXT: bnez a0, .LBB2_2 +; RV32ZDINX_ZICOND-NEXT: # %bb.1: # %entry +; RV32ZDINX_ZICOND-NEXT: mv a4, a6 +; RV32ZDINX_ZICOND-NEXT: mv a5, a7 +; RV32ZDINX_ZICOND-NEXT: .LBB2_2: # %entry +; RV32ZDINX_ZICOND-NEXT: mv a0, a4 +; RV32ZDINX_ZICOND-NEXT: mv a1, a5 ; RV32ZDINX_ZICOND-NEXT: ret ; ; RV32ZDINX_NOZICOND-LABEL: select_f64_fcmp: From a63b00307780eca524a74db6ede207221544225a Mon Sep 17 00:00:00 2001 From: fennecJ Date: Tue, 25 Nov 2025 23:18:00 +0800 Subject: [PATCH 11/15] Optimize Zicond lowering for FP selects with +0.0 When lowering a floating-point SELECT to integer Zicond operations on RV64, converting an f32 +0.0 results in a RISCVISD::FMV_X_ANYEXTW_RV64 node. This target-specific node implies a register move somehow obscures the underlying constant zero value from the instruction selector, preventing the backend from pattern-matching a single `czero` instruction. For the following example: ```asm define dso_local noundef float @select_i1_f32_0(i1 %cond, float %t) nounwind { entry: %sel = select i1 %cond, float %t, float 0.000000e+00 ret float %sel } ``` On RV64 (e.g., +zicond +zdinx), this previously resulted in: ```asm czero.nez a2, zero, a0 czero.eqz a0, a1, a0 or a0, a2, a0 ret ``` Since the "else" value is zero, we can utilize the mechanism that czero like instruction will store zero into rd based on cond reg and optimized this scenario in to a single instruction. By explicitly detecting `+0.0` and lowering it to a constant integer 0, this commit enables the generation of: ```asm czero.eqz a0, a1, a0 ret ``` --- llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 7 ++ .../CodeGen/RISCV/zicond-fp-select-zfinx.ll | 83 +++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index 2885874aab659..a2297375bf522 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -9599,6 +9599,13 @@ SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const { MVT XLenIntVT = Subtarget.getXLenVT(); auto CastToInt = [&](SDValue V) -> SDValue { + // Treat +0.0 as integer 0 to enable single 'czero' instruction + // generation. + if (auto *CFP = dyn_cast(V)) { + if (CFP->isZero() && !CFP->isNegative()) + return DAG.getConstant(0, DL, XLenIntVT); + } + if (VT == MVT::f16) return DAG.getNode(RISCVISD::FMV_X_ANYEXTH, DL, XLenIntVT, V); diff --git a/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll b/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll index 5950bb899cef5..0d789bca291d9 100644 --- a/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll +++ b/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll @@ -467,3 +467,86 @@ entry: %sel = select i1 %cond, half %a, half %b ret half %sel } + +; ----------------------------------------------------------------------------- +; Test select with i1 condition and zero ret val (cond ? a : 0), Zfinx +; ----------------------------------------------------------------------------- +define dso_local noundef float @select_i1_f32_0(i1 %cond, float %t) nounwind { +; RV64ZDINX_ZICOND-LABEL: select_i1_f32_0: +; RV64ZDINX_ZICOND: # %bb.0: # %entry +; RV64ZDINX_ZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV64ZDINX_ZICOND-NEXT: andi a0, a0, 1 +; RV64ZDINX_ZICOND-NEXT: czero.nez a2, zero, a0 +; RV64ZDINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV64ZDINX_ZICOND-NEXT: or a0, a0, a2 +; RV64ZDINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV64ZDINX_ZICOND-NEXT: ret +; +; RV64ZDINX_NOZICOND-LABEL: select_i1_f32_0: +; RV64ZDINX_NOZICOND: # %bb.0: # %entry +; RV64ZDINX_NOZICOND-NEXT: andi a2, a0, 1 +; RV64ZDINX_NOZICOND-NEXT: mv a0, a1 +; RV64ZDINX_NOZICOND-NEXT: bnez a2, .LBB4_2 +; RV64ZDINX_NOZICOND-NEXT: # %bb.1: # %entry +; RV64ZDINX_NOZICOND-NEXT: li a0, 0 +; RV64ZDINX_NOZICOND-NEXT: .LBB4_2: # %entry +; RV64ZDINX_NOZICOND-NEXT: ret +; +; RV64ZHINX_ZICOND-LABEL: select_i1_f32_0: +; RV64ZHINX_ZICOND: # %bb.0: # %entry +; RV64ZHINX_ZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV64ZHINX_ZICOND-NEXT: andi a0, a0, 1 +; RV64ZHINX_ZICOND-NEXT: czero.nez a2, zero, a0 +; RV64ZHINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV64ZHINX_ZICOND-NEXT: or a0, a0, a2 +; RV64ZHINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV64ZHINX_ZICOND-NEXT: ret +; +; RV64FD-LABEL: select_i1_f32_0: +; RV64FD: # %bb.0: # %entry +; RV64FD-NEXT: andi a0, a0, 1 +; RV64FD-NEXT: bnez a0, .LBB4_2 +; RV64FD-NEXT: # %bb.1: # %entry +; RV64FD-NEXT: fmv.w.x fa0, zero +; RV64FD-NEXT: .LBB4_2: # %entry +; RV64FD-NEXT: ret +; +; RV32ZFINX_ZICOND-LABEL: select_i1_f32_0: +; RV32ZFINX_ZICOND: # %bb.0: # %entry +; RV32ZFINX_ZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV32ZFINX_ZICOND-NEXT: andi a0, a0, 1 +; RV32ZFINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV32ZFINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV32ZFINX_ZICOND-NEXT: ret +; +; RV32ZFINX_NOZICOND-LABEL: select_i1_f32_0: +; RV32ZFINX_NOZICOND: # %bb.0: # %entry +; RV32ZFINX_NOZICOND-NEXT: andi a2, a0, 1 +; RV32ZFINX_NOZICOND-NEXT: mv a0, a1 +; RV32ZFINX_NOZICOND-NEXT: bnez a2, .LBB4_2 +; RV32ZFINX_NOZICOND-NEXT: # %bb.1: # %entry +; RV32ZFINX_NOZICOND-NEXT: li a0, 0 +; RV32ZFINX_NOZICOND-NEXT: .LBB4_2: # %entry +; RV32ZFINX_NOZICOND-NEXT: ret +; +; RV32ZDINX_ZICOND-LABEL: select_i1_f32_0: +; RV32ZDINX_ZICOND: # %bb.0: # %entry +; RV32ZDINX_ZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV32ZDINX_ZICOND-NEXT: andi a0, a0, 1 +; RV32ZDINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV32ZDINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV32ZDINX_ZICOND-NEXT: ret +; +; RV32ZDINX_NOZICOND-LABEL: select_i1_f32_0: +; RV32ZDINX_NOZICOND: # %bb.0: # %entry +; RV32ZDINX_NOZICOND-NEXT: andi a2, a0, 1 +; RV32ZDINX_NOZICOND-NEXT: mv a0, a1 +; RV32ZDINX_NOZICOND-NEXT: bnez a2, .LBB4_2 +; RV32ZDINX_NOZICOND-NEXT: # %bb.1: # %entry +; RV32ZDINX_NOZICOND-NEXT: li a0, 0 +; RV32ZDINX_NOZICOND-NEXT: .LBB4_2: # %entry +; RV32ZDINX_NOZICOND-NEXT: ret +entry: + %sel = select i1 %cond, float %t, float 0.000000e+00 + ret float %sel +} From 082474e595bbe9b2b6cf97e698be64e8f566690a Mon Sep 17 00:00:00 2001 From: fennecJ Date: Wed, 26 Nov 2025 00:04:44 +0800 Subject: [PATCH 12/15] Update unit test result to align newest commit --- llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll | 4 ---- 1 file changed, 4 deletions(-) diff --git a/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll b/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll index 0d789bca291d9..ab9882194c37e 100644 --- a/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll +++ b/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll @@ -476,9 +476,7 @@ define dso_local noundef float @select_i1_f32_0(i1 %cond, float %t) nounwind { ; RV64ZDINX_ZICOND: # %bb.0: # %entry ; RV64ZDINX_ZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 ; RV64ZDINX_ZICOND-NEXT: andi a0, a0, 1 -; RV64ZDINX_ZICOND-NEXT: czero.nez a2, zero, a0 ; RV64ZDINX_ZICOND-NEXT: czero.eqz a0, a1, a0 -; RV64ZDINX_ZICOND-NEXT: or a0, a0, a2 ; RV64ZDINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 ; RV64ZDINX_ZICOND-NEXT: ret ; @@ -496,9 +494,7 @@ define dso_local noundef float @select_i1_f32_0(i1 %cond, float %t) nounwind { ; RV64ZHINX_ZICOND: # %bb.0: # %entry ; RV64ZHINX_ZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 ; RV64ZHINX_ZICOND-NEXT: andi a0, a0, 1 -; RV64ZHINX_ZICOND-NEXT: czero.nez a2, zero, a0 ; RV64ZHINX_ZICOND-NEXT: czero.eqz a0, a1, a0 -; RV64ZHINX_ZICOND-NEXT: or a0, a0, a2 ; RV64ZHINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 ; RV64ZHINX_ZICOND-NEXT: ret ; From 3b721ca2322bb924ba2024d669d9be5da9596f20 Mon Sep 17 00:00:00 2001 From: fennecJ Date: Wed, 26 Nov 2025 14:53:34 +0800 Subject: [PATCH 13/15] Refine const zero casting logic --- llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 11 +-- .../CodeGen/RISCV/zicond-fp-select-zfinx.ll | 90 +++++++++++++++++++ 2 files changed, 94 insertions(+), 7 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index a2297375bf522..d9712554e4d34 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -9584,7 +9584,7 @@ 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 + // When there is no cost for GPR <-> FPR, we can use zicond select for // floating value when CondV is int type bool FPinGPR = Subtarget.hasStdExtZfinx(); @@ -9599,12 +9599,9 @@ SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const { MVT XLenIntVT = Subtarget.getXLenVT(); auto CastToInt = [&](SDValue V) -> SDValue { - // Treat +0.0 as integer 0 to enable single 'czero' instruction - // generation. - if (auto *CFP = dyn_cast(V)) { - if (CFP->isZero() && !CFP->isNegative()) - return DAG.getConstant(0, DL, XLenIntVT); - } + // Treat +0.0 as int 0 to enable single 'czero' instruction generation. + if (isNullFPConstant(V)) + return DAG.getConstant(0, DL, XLenIntVT); if (VT == MVT::f16) return DAG.getNode(RISCVISD::FMV_X_ANYEXTH, DL, XLenIntVT, V); diff --git a/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll b/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll index ab9882194c37e..d3083e4196529 100644 --- a/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll +++ b/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll @@ -546,3 +546,93 @@ entry: %sel = select i1 %cond, float %t, float 0.000000e+00 ret float %sel } + +; ----------------------------------------------------------------------------- +; Test select with i1 condition and zero ret val for half fp (cond ? a : 0) +; ----------------------------------------------------------------------------- +define dso_local noundef half @select_i1_half_0(i1 %cond, half %val) nounwind { +; RV64ZDINX_ZICOND-LABEL: select_i1_half_0: +; RV64ZDINX_ZICOND: # %bb.0: # %entry +; RV64ZDINX_ZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV64ZDINX_ZICOND-NEXT: andi a0, a0, 1 +; RV64ZDINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV64ZDINX_ZICOND-NEXT: lui a1, 1048560 +; RV64ZDINX_ZICOND-NEXT: or a0, a0, a1 +; RV64ZDINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV64ZDINX_ZICOND-NEXT: ret +; +; RV64ZDINX_NOZICOND-LABEL: select_i1_half_0: +; RV64ZDINX_NOZICOND: # %bb.0: # %entry +; RV64ZDINX_NOZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV64ZDINX_NOZICOND-NEXT: slli a0, a0, 63 +; RV64ZDINX_NOZICOND-NEXT: srai a0, a0, 63 +; RV64ZDINX_NOZICOND-NEXT: and a0, a0, a1 +; RV64ZDINX_NOZICOND-NEXT: lui a1, 1048560 +; RV64ZDINX_NOZICOND-NEXT: or a0, a0, a1 +; RV64ZDINX_NOZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV64ZDINX_NOZICOND-NEXT: ret +; +; RV64ZHINX_ZICOND-LABEL: select_i1_half_0: +; RV64ZHINX_ZICOND: # %bb.0: # %entry +; RV64ZHINX_ZICOND-NEXT: # kill: def $x11_h killed $x11_h def $x11 +; RV64ZHINX_ZICOND-NEXT: andi a0, a0, 1 +; RV64ZHINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV64ZHINX_ZICOND-NEXT: # kill: def $x10_h killed $x10_h killed $x10 +; RV64ZHINX_ZICOND-NEXT: ret +; +; RV64FD-LABEL: select_i1_half_0: +; RV64FD: # %bb.0: # %entry +; RV64FD-NEXT: fmv.x.w a1, fa0 +; RV64FD-NEXT: slli a0, a0, 63 +; RV64FD-NEXT: srai a0, a0, 63 +; RV64FD-NEXT: and a0, a0, a1 +; RV64FD-NEXT: lui a1, 1048560 +; RV64FD-NEXT: or a0, a0, a1 +; RV64FD-NEXT: fmv.w.x fa0, a0 +; RV64FD-NEXT: ret +; +; RV32ZFINX_ZICOND-LABEL: select_i1_half_0: +; RV32ZFINX_ZICOND: # %bb.0: # %entry +; RV32ZFINX_ZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV32ZFINX_ZICOND-NEXT: andi a0, a0, 1 +; RV32ZFINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV32ZFINX_ZICOND-NEXT: lui a1, 1048560 +; RV32ZFINX_ZICOND-NEXT: or a0, a0, a1 +; RV32ZFINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV32ZFINX_ZICOND-NEXT: ret +; +; RV32ZFINX_NOZICOND-LABEL: select_i1_half_0: +; RV32ZFINX_NOZICOND: # %bb.0: # %entry +; RV32ZFINX_NOZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV32ZFINX_NOZICOND-NEXT: slli a0, a0, 31 +; RV32ZFINX_NOZICOND-NEXT: srai a0, a0, 31 +; RV32ZFINX_NOZICOND-NEXT: and a0, a0, a1 +; RV32ZFINX_NOZICOND-NEXT: lui a1, 1048560 +; RV32ZFINX_NOZICOND-NEXT: or a0, a0, a1 +; RV32ZFINX_NOZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV32ZFINX_NOZICOND-NEXT: ret +; +; RV32ZDINX_ZICOND-LABEL: select_i1_half_0: +; RV32ZDINX_ZICOND: # %bb.0: # %entry +; RV32ZDINX_ZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV32ZDINX_ZICOND-NEXT: andi a0, a0, 1 +; RV32ZDINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV32ZDINX_ZICOND-NEXT: lui a1, 1048560 +; RV32ZDINX_ZICOND-NEXT: or a0, a0, a1 +; RV32ZDINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV32ZDINX_ZICOND-NEXT: ret +; +; RV32ZDINX_NOZICOND-LABEL: select_i1_half_0: +; RV32ZDINX_NOZICOND: # %bb.0: # %entry +; RV32ZDINX_NOZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV32ZDINX_NOZICOND-NEXT: slli a0, a0, 31 +; RV32ZDINX_NOZICOND-NEXT: srai a0, a0, 31 +; RV32ZDINX_NOZICOND-NEXT: and a0, a0, a1 +; RV32ZDINX_NOZICOND-NEXT: lui a1, 1048560 +; RV32ZDINX_NOZICOND-NEXT: or a0, a0, a1 +; RV32ZDINX_NOZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV32ZDINX_NOZICOND-NEXT: ret +entry: + %sel = select i1 %cond, half %val, half 0xH0000 + ret half %sel +} From ef97fca1f4eabdc47698877b3d86a67f5b99025c Mon Sep 17 00:00:00 2001 From: fennecJ Date: Wed, 26 Nov 2025 17:29:47 +0800 Subject: [PATCH 14/15] Add test for F16 select feeding into arithmetic op --- .../CodeGen/RISCV/zicond-fp-select-zfinx.ll | 176 +++++++++++++++++- 1 file changed, 168 insertions(+), 8 deletions(-) diff --git a/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll b/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll index d3083e4196529..b505c84166eb1 100644 --- a/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll +++ b/llvm/test/CodeGen/RISCV/zicond-fp-select-zfinx.ll @@ -182,8 +182,7 @@ define double @select_f64_i1(i1 %cond, double %t, double %f) nounwind { ; RV32ZDINX_ZICOND-NEXT: # %bb.1: # %entry ; RV32ZDINX_ZICOND-NEXT: mv a7, a4 ; RV32ZDINX_ZICOND-NEXT: mv a6, a3 -; RV32ZDINX_ZICOND-NEXT: mv a4, a6 -; RV32ZDINX_ZICOND-NEXT: mv a5, a7 +; RV32ZDINX_ZICOND-NEXT: fmv.d a4, a6 ; RV32ZDINX_ZICOND-NEXT: j .LBB1_3 ; RV32ZDINX_ZICOND-NEXT: .LBB1_2: ; RV32ZDINX_ZICOND-NEXT: mv a5, a2 @@ -200,8 +199,7 @@ define double @select_f64_i1(i1 %cond, double %t, double %f) nounwind { ; RV32ZDINX_NOZICOND-NEXT: # %bb.1: # %entry ; RV32ZDINX_NOZICOND-NEXT: mv a7, a4 ; RV32ZDINX_NOZICOND-NEXT: mv a6, a3 -; RV32ZDINX_NOZICOND-NEXT: mv a4, a6 -; RV32ZDINX_NOZICOND-NEXT: mv a5, a7 +; RV32ZDINX_NOZICOND-NEXT: fmv.d a4, a6 ; RV32ZDINX_NOZICOND-NEXT: j .LBB1_3 ; RV32ZDINX_NOZICOND-NEXT: .LBB1_2: ; RV32ZDINX_NOZICOND-NEXT: mv a5, a2 @@ -328,8 +326,7 @@ define double @select_f64_fcmp(double %a, double %b, double %c, double %d) nounw ; RV32ZDINX_ZICOND-NEXT: flt.d a0, a2, a0 ; RV32ZDINX_ZICOND-NEXT: bnez a0, .LBB2_2 ; RV32ZDINX_ZICOND-NEXT: # %bb.1: # %entry -; RV32ZDINX_ZICOND-NEXT: mv a4, a6 -; RV32ZDINX_ZICOND-NEXT: mv a5, a7 +; RV32ZDINX_ZICOND-NEXT: fmv.d a4, a6 ; RV32ZDINX_ZICOND-NEXT: .LBB2_2: # %entry ; RV32ZDINX_ZICOND-NEXT: mv a0, a4 ; RV32ZDINX_ZICOND-NEXT: mv a1, a5 @@ -340,8 +337,7 @@ define double @select_f64_fcmp(double %a, double %b, double %c, double %d) nounw ; RV32ZDINX_NOZICOND-NEXT: flt.d a0, a2, a0 ; RV32ZDINX_NOZICOND-NEXT: bnez a0, .LBB2_2 ; RV32ZDINX_NOZICOND-NEXT: # %bb.1: # %entry -; RV32ZDINX_NOZICOND-NEXT: mv a4, a6 -; RV32ZDINX_NOZICOND-NEXT: mv a5, a7 +; RV32ZDINX_NOZICOND-NEXT: fmv.d a4, a6 ; RV32ZDINX_NOZICOND-NEXT: .LBB2_2: # %entry ; RV32ZDINX_NOZICOND-NEXT: mv a0, a4 ; RV32ZDINX_NOZICOND-NEXT: mv a1, a5 @@ -636,3 +632,167 @@ entry: %sel = select i1 %cond, half %val, half 0xH0000 ret half %sel } + +; ----------------------------------------------------------------------------- +; Test select with i1 condition and zero value for half fp, feeding into fadd ((cond ? a : 0) + 1.0) +; ----------------------------------------------------------------------------- +define half @select_i1_half_0_add(i1 %cond, half %val) nounwind { +; RV64ZDINX_ZICOND-LABEL: select_i1_half_0_add: +; RV64ZDINX_ZICOND: # %bb.0: # %entry +; RV64ZDINX_ZICOND-NEXT: addi sp, sp, -16 +; RV64ZDINX_ZICOND-NEXT: sd ra, 8(sp) # 8-byte Folded Spill +; RV64ZDINX_ZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV64ZDINX_ZICOND-NEXT: andi a0, a0, 1 +; RV64ZDINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV64ZDINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV64ZDINX_ZICOND-NEXT: call __extendhfsf2 +; RV64ZDINX_ZICOND-NEXT: lui a1, 260096 +; RV64ZDINX_ZICOND-NEXT: fadd.s a0, a0, a1 +; RV64ZDINX_ZICOND-NEXT: call __truncsfhf2 +; RV64ZDINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w def $x10 +; RV64ZDINX_ZICOND-NEXT: lui a1, 1048560 +; RV64ZDINX_ZICOND-NEXT: or a0, a0, a1 +; RV64ZDINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV64ZDINX_ZICOND-NEXT: ld ra, 8(sp) # 8-byte Folded Reload +; RV64ZDINX_ZICOND-NEXT: addi sp, sp, 16 +; RV64ZDINX_ZICOND-NEXT: ret +; +; RV64ZDINX_NOZICOND-LABEL: select_i1_half_0_add: +; RV64ZDINX_NOZICOND: # %bb.0: # %entry +; RV64ZDINX_NOZICOND-NEXT: addi sp, sp, -16 +; RV64ZDINX_NOZICOND-NEXT: sd ra, 8(sp) # 8-byte Folded Spill +; RV64ZDINX_NOZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV64ZDINX_NOZICOND-NEXT: slli a0, a0, 63 +; RV64ZDINX_NOZICOND-NEXT: srai a0, a0, 63 +; RV64ZDINX_NOZICOND-NEXT: and a0, a0, a1 +; RV64ZDINX_NOZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV64ZDINX_NOZICOND-NEXT: call __extendhfsf2 +; RV64ZDINX_NOZICOND-NEXT: lui a1, 260096 +; RV64ZDINX_NOZICOND-NEXT: fadd.s a0, a0, a1 +; RV64ZDINX_NOZICOND-NEXT: call __truncsfhf2 +; RV64ZDINX_NOZICOND-NEXT: # kill: def $x10_w killed $x10_w def $x10 +; RV64ZDINX_NOZICOND-NEXT: lui a1, 1048560 +; RV64ZDINX_NOZICOND-NEXT: or a0, a0, a1 +; RV64ZDINX_NOZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV64ZDINX_NOZICOND-NEXT: ld ra, 8(sp) # 8-byte Folded Reload +; RV64ZDINX_NOZICOND-NEXT: addi sp, sp, 16 +; RV64ZDINX_NOZICOND-NEXT: ret +; +; RV64ZHINX_ZICOND-LABEL: select_i1_half_0_add: +; RV64ZHINX_ZICOND: # %bb.0: # %entry +; RV64ZHINX_ZICOND-NEXT: # kill: def $x11_h killed $x11_h def $x11 +; RV64ZHINX_ZICOND-NEXT: andi a0, a0, 1 +; RV64ZHINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV64ZHINX_ZICOND-NEXT: li a1, 15 +; RV64ZHINX_ZICOND-NEXT: slli a1, a1, 10 +; RV64ZHINX_ZICOND-NEXT: fadd.h a0, a0, a1 +; RV64ZHINX_ZICOND-NEXT: ret +; +; RV64FD-LABEL: select_i1_half_0_add: +; RV64FD: # %bb.0: # %entry +; RV64FD-NEXT: addi sp, sp, -16 +; RV64FD-NEXT: sd ra, 8(sp) # 8-byte Folded Spill +; RV64FD-NEXT: fmv.x.w a1, fa0 +; RV64FD-NEXT: slli a0, a0, 63 +; RV64FD-NEXT: srai a0, a0, 63 +; RV64FD-NEXT: and a0, a0, a1 +; RV64FD-NEXT: fmv.w.x fa0, a0 +; RV64FD-NEXT: call __extendhfsf2 +; RV64FD-NEXT: lui a0, 260096 +; RV64FD-NEXT: fmv.w.x fa5, a0 +; RV64FD-NEXT: fadd.s fa0, fa0, fa5 +; RV64FD-NEXT: call __truncsfhf2 +; RV64FD-NEXT: fmv.x.w a0, fa0 +; RV64FD-NEXT: lui a1, 1048560 +; RV64FD-NEXT: or a0, a0, a1 +; RV64FD-NEXT: fmv.w.x fa0, a0 +; RV64FD-NEXT: ld ra, 8(sp) # 8-byte Folded Reload +; RV64FD-NEXT: addi sp, sp, 16 +; RV64FD-NEXT: ret +; +; RV32ZFINX_ZICOND-LABEL: select_i1_half_0_add: +; RV32ZFINX_ZICOND: # %bb.0: # %entry +; RV32ZFINX_ZICOND-NEXT: addi sp, sp, -16 +; RV32ZFINX_ZICOND-NEXT: sw ra, 12(sp) # 4-byte Folded Spill +; RV32ZFINX_ZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV32ZFINX_ZICOND-NEXT: andi a0, a0, 1 +; RV32ZFINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV32ZFINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV32ZFINX_ZICOND-NEXT: call __extendhfsf2 +; RV32ZFINX_ZICOND-NEXT: lui a1, 260096 +; RV32ZFINX_ZICOND-NEXT: fadd.s a0, a0, a1 +; RV32ZFINX_ZICOND-NEXT: call __truncsfhf2 +; RV32ZFINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w def $x10 +; RV32ZFINX_ZICOND-NEXT: lui a1, 1048560 +; RV32ZFINX_ZICOND-NEXT: or a0, a0, a1 +; RV32ZFINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV32ZFINX_ZICOND-NEXT: lw ra, 12(sp) # 4-byte Folded Reload +; RV32ZFINX_ZICOND-NEXT: addi sp, sp, 16 +; RV32ZFINX_ZICOND-NEXT: ret +; +; RV32ZFINX_NOZICOND-LABEL: select_i1_half_0_add: +; RV32ZFINX_NOZICOND: # %bb.0: # %entry +; RV32ZFINX_NOZICOND-NEXT: addi sp, sp, -16 +; RV32ZFINX_NOZICOND-NEXT: sw ra, 12(sp) # 4-byte Folded Spill +; RV32ZFINX_NOZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV32ZFINX_NOZICOND-NEXT: slli a0, a0, 31 +; RV32ZFINX_NOZICOND-NEXT: srai a0, a0, 31 +; RV32ZFINX_NOZICOND-NEXT: and a0, a0, a1 +; RV32ZFINX_NOZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV32ZFINX_NOZICOND-NEXT: call __extendhfsf2 +; RV32ZFINX_NOZICOND-NEXT: lui a1, 260096 +; RV32ZFINX_NOZICOND-NEXT: fadd.s a0, a0, a1 +; RV32ZFINX_NOZICOND-NEXT: call __truncsfhf2 +; RV32ZFINX_NOZICOND-NEXT: # kill: def $x10_w killed $x10_w def $x10 +; RV32ZFINX_NOZICOND-NEXT: lui a1, 1048560 +; RV32ZFINX_NOZICOND-NEXT: or a0, a0, a1 +; RV32ZFINX_NOZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV32ZFINX_NOZICOND-NEXT: lw ra, 12(sp) # 4-byte Folded Reload +; RV32ZFINX_NOZICOND-NEXT: addi sp, sp, 16 +; RV32ZFINX_NOZICOND-NEXT: ret +; +; RV32ZDINX_ZICOND-LABEL: select_i1_half_0_add: +; RV32ZDINX_ZICOND: # %bb.0: # %entry +; RV32ZDINX_ZICOND-NEXT: addi sp, sp, -16 +; RV32ZDINX_ZICOND-NEXT: sw ra, 12(sp) # 4-byte Folded Spill +; RV32ZDINX_ZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV32ZDINX_ZICOND-NEXT: andi a0, a0, 1 +; RV32ZDINX_ZICOND-NEXT: czero.eqz a0, a1, a0 +; RV32ZDINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV32ZDINX_ZICOND-NEXT: call __extendhfsf2 +; RV32ZDINX_ZICOND-NEXT: lui a1, 260096 +; RV32ZDINX_ZICOND-NEXT: fadd.s a0, a0, a1 +; RV32ZDINX_ZICOND-NEXT: call __truncsfhf2 +; RV32ZDINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w def $x10 +; RV32ZDINX_ZICOND-NEXT: lui a1, 1048560 +; RV32ZDINX_ZICOND-NEXT: or a0, a0, a1 +; RV32ZDINX_ZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV32ZDINX_ZICOND-NEXT: lw ra, 12(sp) # 4-byte Folded Reload +; RV32ZDINX_ZICOND-NEXT: addi sp, sp, 16 +; RV32ZDINX_ZICOND-NEXT: ret +; +; RV32ZDINX_NOZICOND-LABEL: select_i1_half_0_add: +; RV32ZDINX_NOZICOND: # %bb.0: # %entry +; RV32ZDINX_NOZICOND-NEXT: addi sp, sp, -16 +; RV32ZDINX_NOZICOND-NEXT: sw ra, 12(sp) # 4-byte Folded Spill +; RV32ZDINX_NOZICOND-NEXT: # kill: def $x11_w killed $x11_w def $x11 +; RV32ZDINX_NOZICOND-NEXT: slli a0, a0, 31 +; RV32ZDINX_NOZICOND-NEXT: srai a0, a0, 31 +; RV32ZDINX_NOZICOND-NEXT: and a0, a0, a1 +; RV32ZDINX_NOZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV32ZDINX_NOZICOND-NEXT: call __extendhfsf2 +; RV32ZDINX_NOZICOND-NEXT: lui a1, 260096 +; RV32ZDINX_NOZICOND-NEXT: fadd.s a0, a0, a1 +; RV32ZDINX_NOZICOND-NEXT: call __truncsfhf2 +; RV32ZDINX_NOZICOND-NEXT: # kill: def $x10_w killed $x10_w def $x10 +; RV32ZDINX_NOZICOND-NEXT: lui a1, 1048560 +; RV32ZDINX_NOZICOND-NEXT: or a0, a0, a1 +; RV32ZDINX_NOZICOND-NEXT: # kill: def $x10_w killed $x10_w killed $x10 +; RV32ZDINX_NOZICOND-NEXT: lw ra, 12(sp) # 4-byte Folded Reload +; RV32ZDINX_NOZICOND-NEXT: addi sp, sp, 16 +; RV32ZDINX_NOZICOND-NEXT: ret +entry: + %sel = select i1 %cond, half %val, half 0xH0000 + %add = fadd half %sel, 1.0 + ret half %add +} From 7cd5abcd8a62ffd15f0d66fe041a8f65cfbb6468 Mon Sep 17 00:00:00 2001 From: fennecJ Date: Thu, 27 Nov 2025 11:43:17 +0800 Subject: [PATCH 15/15] Refine FP Zicond ISel logic We can reuse XLenVT --- llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index d9712554e4d34..c11540f19e70e 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -9596,20 +9596,19 @@ SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const { VT.isFloatingPoint() && FitsInGPR; if (UseZicondForFPSel) { - MVT XLenIntVT = Subtarget.getXLenVT(); auto CastToInt = [&](SDValue V) -> SDValue { // Treat +0.0 as int 0 to enable single 'czero' instruction generation. if (isNullFPConstant(V)) - return DAG.getConstant(0, DL, XLenIntVT); + return DAG.getConstant(0, DL, XLenVT); if (VT == MVT::f16) - return DAG.getNode(RISCVISD::FMV_X_ANYEXTH, DL, XLenIntVT, V); + return DAG.getNode(RISCVISD::FMV_X_ANYEXTH, DL, XLenVT, V); if (VT == MVT::f32 && Subtarget.is64Bit()) - return DAG.getNode(RISCVISD::FMV_X_ANYEXTW_RV64, DL, XLenIntVT, V); + return DAG.getNode(RISCVISD::FMV_X_ANYEXTW_RV64, DL, XLenVT, V); - return DAG.getBitcast(XLenIntVT, V); + return DAG.getBitcast(XLenVT, V); }; SDValue TrueVInt = CastToInt(TrueV); @@ -9617,7 +9616,7 @@ SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const { // Emit integer SELECT (lowers to Zicond) SDValue ResultInt = - DAG.getNode(ISD::SELECT, DL, XLenIntVT, CondV, TrueVInt, FalseVInt); + DAG.getNode(ISD::SELECT, DL, XLenVT, CondV, TrueVInt, FalseVInt); // Convert back to floating VT if (VT == MVT::f32 && Subtarget.is64Bit())