[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