[llvm] [RISCV][ISelLowering] Use Zicond for FP selects on Zfinx/Zdinx (PR #169299)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Nov 26 01:34:00 PST 2025
================
@@ -9555,6 +9555,54 @@ 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();
+
+ // We can handle FGPR without spliting into hi/lo parts
+ bool FitsInGPR = TypeSize::isKnownLE(VT.getSizeInBits(),
+ Subtarget.getXLenVT().getSizeInBits());
+
+ bool UseZicondForFPSel = Subtarget.hasStdExtZicond() && FPinGPR &&
+ VT.isFloatingPoint() && FitsInGPR;
+
+ if (UseZicondForFPSel) {
+ 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);
+ }
----------------
fennecJ wrote:
I tried your select_i1_half_0_add case, and the result is correctly Nan-boxed at the return, I'll append the test result to newest commit.
```asm
select_i1_half_0_add:
# %bb.0: # %entry
addi sp, sp, -16
sd ra, 8(sp) # 8-byte Folded Spill
# kill: def $x11_w killed $x11_w def $x11
andi a0, a0, 1
czero.eqz a0, a1, a0
# kill: def $x10_w killed $x10_w killed $x10
call __extendhfsf2
lui a1, 260096
fadd.s a0, a0, a1
call __truncsfhf2
# kill: def $x10_w killed $x10_w def $x10
lui a1, 1048560 # a1= 0xFFFF0000
or a0, a0, a1 # nan-boxing !!
# kill: def $x10_w killed $x10_w killed $x10
ld ra, 8(sp) # 8-byte Folded Reload
addi sp, sp, 16
ret
```
Regarding your question about whether this optimization results in incorrect Nan-boxing:
Actually, I initially shared the same assumption that explicit Nan-boxing was required here. However, upon double-checking the Zfinx/Zdinx/Zhinx specification[1], I discovered that Nan-boxing is not required for operands in these extensions. Instead, they rely on Sign-Extension.
According to **Section 1 (Processing of Narrower Values)**:
> "Floating-point operands of width w < XLEN bits occupy bits w-1:0 of an x register. Floating-point operations on w-bit operands **ignore operand bits XLEN-1: w**."
> "Floating-point operations that produce w < XLEN-bit results **fill bits XLEN-1: w with copies of bit w-1 (the sign bit)**."
Since my optimization targets +0.0 (sign bit is 0), the resulting all-zero integer effectively implements the required Sign-Extension. It is safe for input operands (as high bits are ignored) and valid as a result.
As for the ABI[2], the clause regarding Nan-boxing specifies:
> "When a floating-point argument narrower than FLEN bits is passed in a **floating-point register**, it is 1-extended (NaN-boxed) to FLEN bits."
Since Zfinx has no floating-point registers (it uses `x` registers), and the Unpriv spec mandates sign-extension for `x` registers, providing a zero-extended `0` is safe. Even if specific calling conventions require boxing at boundaries, the backend's `splitValueIntoRegisterParts` handles that explicitly.
Therefore, I believe keeping the `+0.0` optimization at the current location is safe and yields the best code generation (single `czero`).
[1] - [riscv unpriv zfinx](https://docs.riscv.org/reference/isa/unpriv/zfinx.html)
[2] - [riscv-elf-psabi-doc](https://github.com/riscv-non-isa/riscv-elf-psabi-doc/releases/tag/v1.0)
https://github.com/llvm/llvm-project/pull/169299
More information about the llvm-commits
mailing list