[llvm] [RISCV][ISelLowering] Use Zicond for FP selects on Zfinx/Zdinx (PR #169299)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Nov 25 07:45:53 PST 2025
https://github.com/fennecJ updated https://github.com/llvm/llvm-project/pull/169299
>From 329a5d04d9ac50c02e2631adbec119a50425972e Mon Sep 17 00:00:00 2001
From: fennecJ <hwahwa649 at gmail.com>
Date: Mon, 24 Nov 2025 16:25:36 +0800
Subject: [PATCH 01/11] [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 19a16197272fe..9183a523226bc 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -9555,6 +9555,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 fe46a46e0c21dbc2e415d5bfd6d05027f61bfb60 Mon Sep 17 00:00:00 2001
From: fennecJ <hwahwa649 at gmail.com>
Date: Mon, 24 Nov 2025 16:38:11 +0800
Subject: [PATCH 02/11] 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 f49320f3744c1f5e985c76156d3c7f387b746e0f Mon Sep 17 00:00:00 2001
From: fennecJ <hwahwa649 at gmail.com>
Date: Tue, 25 Nov 2025 01:23:21 +0800
Subject: [PATCH 03/11] 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 9183a523226bc..0ce5c493c88b6 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -9557,7 +9557,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 e235ce434649971e3b9526638ccc9eda0beeba9f Mon Sep 17 00:00:00 2001
From: fennecJ <hwahwa649 at gmail.com>
Date: Tue, 25 Nov 2025 02:53:01 +0800
Subject: [PATCH 04/11] 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 0ce5c493c88b6..a66ba8ff61ce2 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -9565,6 +9565,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);
}
@@ -9582,6 +9585,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 905e17ba1cd32f3aae74ac568c61a7777b0e6d7b Mon Sep 17 00:00:00 2001
From: fennecJ <hwahwa649 at gmail.com>
Date: Tue, 25 Nov 2025 03:06:56 +0800
Subject: [PATCH 05/11] 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 68e26c4e217df2a5eec8d28a9cc24e9f525fc7f3 Mon Sep 17 00:00:00 2001
From: fennecJ <hwahwa649 at gmail.com>
Date: Tue, 25 Nov 2025 03:42:47 +0800
Subject: [PATCH 06/11] 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 a66ba8ff61ce2..6436a7e9340fa 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -9558,19 +9558,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);
};
@@ -9582,12 +9582,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 bf53e56c2ce72ccbaba305b1ecace55431d7994d Mon Sep 17 00:00:00 2001
From: fennecJ <hwahwa649 at gmail.com>
Date: Tue, 25 Nov 2025 04:03:03 +0800
Subject: [PATCH 07/11] 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 3718939d372fc77bc64887fbe1348a6e52d302ec Mon Sep 17 00:00:00 2001
From: fennecJ <hwahwa649 at gmail.com>
Date: Tue, 25 Nov 2025 11:33:02 +0800
Subject: [PATCH 08/11] 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 6436a7e9340fa..065db2bcfc2a7 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -9564,6 +9564,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 6e40a9d0e8f73bcab223f56ff4a98e4280c99128 Mon Sep 17 00:00:00 2001
From: fennecJ <hwahwa649 at gmail.com>
Date: Tue, 25 Nov 2025 11:43:26 +0800
Subject: [PATCH 09/11] 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 e1009faf1e969cdd4bea441d517bc4599477d23f Mon Sep 17 00:00:00 2001
From: fennecJ <hwahwa649 at gmail.com>
Date: Tue, 25 Nov 2025 16:35:18 +0800
Subject: [PATCH 10/11] 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 065db2bcfc2a7..e02212179efab 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -9558,31 +9558,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 5778116b3db93b4fd848b3f7aca0cb41a609e106 Mon Sep 17 00:00:00 2001
From: fennecJ <hwahwa649 at gmail.com>
Date: Tue, 25 Nov 2025 23:18:00 +0800
Subject: [PATCH 11/11] 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 e02212179efab..c09237b39b13f 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -9570,6 +9570,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<ConstantFPSDNode>(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
+}
More information about the llvm-commits
mailing list