[llvm] [RISCV] Lower scalar FP (freeze poison) as a fmv/fcvt from x0. (PR #225549)
Craig Topper via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 22 15:56:31 PDT 2026
https://github.com/topperc created https://github.com/llvm/llvm-project/pull/225549
We need to ensure the result frozen result is nan-boxed. If it is not nan-boxed, it will be treated as a nan in FP contexts regardless of what the lower bits are. If the frozen value is also casted to an integer type, the lack of nan-boxing will not be noticed and the integer will be taken from the lower bits of the FP register. This may cause the freeze to appear to be 2 different values.
This patch lowers the (freeze poison) the same as a constantfp 0.0. This ensures the frozen value will be produced by an instruction that will do the nan-boxing.
Fixes #225455.
>From c153c999af534f7655c77681f4639cb75cc8a065 Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Tue, 22 Sep 2026 15:24:14 -0700
Subject: [PATCH] [RISCV] Lower scalar FP (freeze poison) as a fmv/fcvt from
x0.
We need to ensure the result frozen result is nan-boxed. If it is
not nan-boxed, it may be a nan in FP contexts regardless of what
the lower bits are. If the frozen value is also casted to an integer
type, the lack of nan-boxing will not be noticed and the integer
will be just the lower bits of the FP value. This will cause the
frozen to appear to be 2 different values.
This patch lowers the (freeze poison) the same as a constantfp 0.0.
This ensures the frozen value will be produced by an instruction
that will do the nan-boxing.
Fixes #225455.
---
llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp | 146 +++++++++++---------
llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h | 2 +
llvm/test/CodeGen/RISCV/bfloat-imm.ll | 9 ++
llvm/test/CodeGen/RISCV/double-imm.ll | 22 +++
llvm/test/CodeGen/RISCV/float-imm.ll | 13 ++
llvm/test/CodeGen/RISCV/half-imm.ll | 22 +++
6 files changed, 147 insertions(+), 67 deletions(-)
diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
index 146aef612f3876..cf32ab8befd5ee 100644
--- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
@@ -1171,6 +1171,74 @@ static bool isApplicableToPLIOrPLUI(int Val) {
Bit15To8 == Bit7To0;
}
+void RISCVDAGToDAGISel::selectFPConstant(SDNode *Node, const SDLoc &DL, MVT VT,
+ const APFloat &APF) {
+ bool Is64Bit = Subtarget->is64Bit();
+ bool HasZdinx = Subtarget->hasStdExtZdinx();
+ MVT XLenVT = Subtarget->getXLenVT();
+
+ bool NegZeroF64 = APF.isNegZero() && VT == MVT::f64;
+ SDValue Imm;
+ // For +0.0 or f64 -0.0 we need to start from X0. For all others, we will
+ // create an integer immediate.
+ if (APF.isPosZero() || NegZeroF64) {
+ if (VT == MVT::f64 && HasZdinx && !Is64Bit)
+ Imm = CurDAG->getRegister(RISCV::X0_Pair, MVT::f64);
+ else
+ Imm = CurDAG->getRegister(RISCV::X0, XLenVT);
+ } else {
+ Imm = selectImm(CurDAG, DL, XLenVT, APF.bitcastToAPInt().getSExtValue(),
+ *Subtarget);
+ }
+
+ unsigned Opc;
+ switch (VT.SimpleTy) {
+ default:
+ llvm_unreachable("Unexpected size");
+ case MVT::bf16:
+ assert(Subtarget->hasStdExtZfbfmin());
+ Opc = RISCV::FMV_H_X;
+ break;
+ case MVT::f16:
+ Opc = Subtarget->hasStdExtZhinxmin() ? RISCV::COPY : RISCV::FMV_H_X;
+ break;
+ case MVT::f32:
+ Opc = Subtarget->hasStdExtZfinx() ? RISCV::COPY : RISCV::FMV_W_X;
+ break;
+ case MVT::f64:
+ // For RV32, we can't move from a GPR, we need to convert instead. This
+ // should only happen for +0.0 and -0.0.
+ assert((Subtarget->is64Bit() || APF.isZero()) && "Unexpected constant");
+ if (HasZdinx)
+ Opc = RISCV::COPY;
+ else
+ Opc = Is64Bit ? RISCV::FMV_D_X : RISCV::FCVT_D_W;
+ break;
+ }
+
+ SDNode *Res;
+ if (VT.SimpleTy == MVT::f16 && Opc == RISCV::COPY)
+ Res = CurDAG->getTargetExtractSubreg(RISCV::sub_16, DL, VT, Imm).getNode();
+ else if (VT.SimpleTy == MVT::f32 && Opc == RISCV::COPY)
+ Res = CurDAG->getTargetExtractSubreg(RISCV::sub_32, DL, VT, Imm).getNode();
+ else if (Opc == RISCV::FCVT_D_W_IN32X || Opc == RISCV::FCVT_D_W)
+ Res = CurDAG->getMachineNode(
+ Opc, DL, VT, Imm,
+ CurDAG->getTargetConstant(RISCVFPRndMode::RNE, DL, XLenVT));
+ else
+ Res = CurDAG->getMachineNode(Opc, DL, VT, Imm);
+
+ // For f64 -0.0, we need to insert a fneg.d idiom.
+ if (NegZeroF64) {
+ Opc = RISCV::FSGNJN_D;
+ if (HasZdinx)
+ Opc = Is64Bit ? RISCV::FSGNJN_D_INX : RISCV::FSGNJN_D_IN32X;
+ Res = CurDAG->getMachineNode(Opc, DL, VT, SDValue(Res, 0), SDValue(Res, 0));
+ }
+
+ ReplaceNode(Node, Res);
+}
+
void RISCVDAGToDAGISel::Select(SDNode *Node) {
// If we have a custom node, we have already selected.
if (Node->isMachineOpcode()) {
@@ -1243,75 +1311,19 @@ void RISCVDAGToDAGISel::Select(SDNode *Node) {
}
case ISD::ConstantFP: {
const APFloat &APF = cast<ConstantFPSDNode>(Node)->getValueAPF();
-
- bool Is64Bit = Subtarget->is64Bit();
- bool HasZdinx = Subtarget->hasStdExtZdinx();
-
- bool NegZeroF64 = APF.isNegZero() && VT == MVT::f64;
- SDValue Imm;
- // For +0.0 or f64 -0.0 we need to start from X0. For all others, we will
- // create an integer immediate.
- if (APF.isPosZero() || NegZeroF64) {
- if (VT == MVT::f64 && HasZdinx && !Is64Bit)
- Imm = CurDAG->getRegister(RISCV::X0_Pair, MVT::f64);
- else
- Imm = CurDAG->getRegister(RISCV::X0, XLenVT);
- } else {
- Imm = selectImm(CurDAG, DL, XLenVT, APF.bitcastToAPInt().getSExtValue(),
- *Subtarget);
- }
-
- unsigned Opc;
- switch (VT.SimpleTy) {
- default:
- llvm_unreachable("Unexpected size");
- case MVT::bf16:
- assert(Subtarget->hasStdExtZfbfmin());
- Opc = RISCV::FMV_H_X;
- break;
- case MVT::f16:
- Opc = Subtarget->hasStdExtZhinxmin() ? RISCV::COPY : RISCV::FMV_H_X;
- break;
- case MVT::f32:
- Opc = Subtarget->hasStdExtZfinx() ? RISCV::COPY : RISCV::FMV_W_X;
- break;
- case MVT::f64:
- // For RV32, we can't move from a GPR, we need to convert instead. This
- // should only happen for +0.0 and -0.0.
- assert((Subtarget->is64Bit() || APF.isZero()) && "Unexpected constant");
- if (HasZdinx)
- Opc = RISCV::COPY;
- else
- Opc = Is64Bit ? RISCV::FMV_D_X : RISCV::FCVT_D_W;
- break;
- }
-
- SDNode *Res;
- if (VT.SimpleTy == MVT::f16 && Opc == RISCV::COPY) {
- Res =
- CurDAG->getTargetExtractSubreg(RISCV::sub_16, DL, VT, Imm).getNode();
- } else if (VT.SimpleTy == MVT::f32 && Opc == RISCV::COPY) {
- Res =
- CurDAG->getTargetExtractSubreg(RISCV::sub_32, DL, VT, Imm).getNode();
- } else if (Opc == RISCV::FCVT_D_W_IN32X || Opc == RISCV::FCVT_D_W)
- Res = CurDAG->getMachineNode(
- Opc, DL, VT, Imm,
- CurDAG->getTargetConstant(RISCVFPRndMode::RNE, DL, XLenVT));
- else
- Res = CurDAG->getMachineNode(Opc, DL, VT, Imm);
-
- // For f64 -0.0, we need to insert a fneg.d idiom.
- if (NegZeroF64) {
- Opc = RISCV::FSGNJN_D;
- if (HasZdinx)
- Opc = Is64Bit ? RISCV::FSGNJN_D_INX : RISCV::FSGNJN_D_IN32X;
- Res =
- CurDAG->getMachineNode(Opc, DL, VT, SDValue(Res, 0), SDValue(Res, 0));
- }
-
- ReplaceNode(Node, Res);
+ selectFPConstant(Node, DL, VT, APF);
return;
}
+ case ISD::FREEZE:
+ // Lower (freeze poison) to a zero producing instruction so we properly
+ // nan-box to FLEN.
+ if (VT.isFloatingPoint() && !VT.isVector() && Subtarget->hasStdExtF() &&
+ Node->getOperand(0).getOpcode() == ISD::POISON) {
+ APFloat Zero = APFloat::getZero(VT.getFltSemantics());
+ selectFPConstant(Node, DL, VT, Zero);
+ return;
+ }
+ break;
case RISCVISD::BuildGPRPair:
case RISCVISD::BuildPairF64:
case RISCVISD::BuildPairGPRVec: {
diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h
index da08a6032d94ff..5012f746d95530 100644
--- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h
+++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h
@@ -108,6 +108,8 @@ class RISCVDAGToDAGISel : public SelectionDAGISel {
SDValue X, unsigned Msb, unsigned Lsb);
bool tryIndexedLoad(SDNode *Node);
bool tryWideningMulAcc(SDNode *Node, const SDLoc &DL);
+ void selectFPConstant(SDNode *Node, const SDLoc &DL, MVT VT,
+ const APFloat &APF);
bool selectShiftMask(SDValue N, unsigned ShiftWidth, SDValue &ShAmt);
bool selectShiftMaskXLen(SDValue N, SDValue &ShAmt) {
diff --git a/llvm/test/CodeGen/RISCV/bfloat-imm.ll b/llvm/test/CodeGen/RISCV/bfloat-imm.ll
index 3c94b7bd70ea3f..d1590ba8e40cb0 100644
--- a/llvm/test/CodeGen/RISCV/bfloat-imm.ll
+++ b/llvm/test/CodeGen/RISCV/bfloat-imm.ll
@@ -43,3 +43,12 @@ define bfloat @bfloat_negative_zero() nounwind {
; CHECK-NEXT: ret
ret bfloat -0.0
}
+
+define bfloat @freeze_poison() nounwind {
+; CHECK-LABEL: freeze_poison:
+; CHECK: # %bb.0:
+; CHECK-NEXT: fmv.h.x fa0, zero
+; CHECK-NEXT: ret
+ %a = freeze bfloat poison
+ ret bfloat %a
+}
diff --git a/llvm/test/CodeGen/RISCV/double-imm.ll b/llvm/test/CodeGen/RISCV/double-imm.ll
index 9f31c95f64dc36..b8719fb5b9109a 100644
--- a/llvm/test/CodeGen/RISCV/double-imm.ll
+++ b/llvm/test/CodeGen/RISCV/double-imm.ll
@@ -170,3 +170,25 @@ entry:
%d. = select i1 %tobool.not, double %d, double -0.000000e+00
ret double %d.
}
+
+define double @freeze_poison() nounwind {
+; CHECK32D-LABEL: freeze_poison:
+; CHECK32D: # %bb.0:
+; CHECK32D-NEXT: fcvt.d.w fa0, zero
+; CHECK32D-NEXT: ret
+;
+; CHECK64D-LABEL: freeze_poison:
+; CHECK64D: # %bb.0:
+; CHECK64D-NEXT: fmv.d.x fa0, zero
+; CHECK64D-NEXT: ret
+;
+; CHECKRV32ZDINX-LABEL: freeze_poison:
+; CHECKRV32ZDINX: # %bb.0:
+; CHECKRV32ZDINX-NEXT: ret
+;
+; CHECKRV64ZDINX-LABEL: freeze_poison:
+; CHECKRV64ZDINX: # %bb.0:
+; CHECKRV64ZDINX-NEXT: ret
+ %a = freeze double poison
+ ret double %a
+}
diff --git a/llvm/test/CodeGen/RISCV/float-imm.ll b/llvm/test/CodeGen/RISCV/float-imm.ll
index 610c72b5f932ef..0742d2d50bc373 100644
--- a/llvm/test/CodeGen/RISCV/float-imm.ll
+++ b/llvm/test/CodeGen/RISCV/float-imm.ll
@@ -68,3 +68,16 @@ define float @float_negative_zero(ptr %pf) nounwind {
; CHECKZFINX-NEXT: ret
ret float -0.0
}
+
+define float @freeze_poison() nounwind {
+; CHECK-LABEL: freeze_poison:
+; CHECK: # %bb.0:
+; CHECK-NEXT: fmv.w.x fa0, zero
+; CHECK-NEXT: ret
+;
+; CHECKZFINX-LABEL: freeze_poison:
+; CHECKZFINX: # %bb.0:
+; CHECKZFINX-NEXT: ret
+ %a = freeze float poison
+ ret float %a
+}
diff --git a/llvm/test/CodeGen/RISCV/half-imm.ll b/llvm/test/CodeGen/RISCV/half-imm.ll
index 8fbaca5c8adc0d..0ed1a095796bf4 100644
--- a/llvm/test/CodeGen/RISCV/half-imm.ll
+++ b/llvm/test/CodeGen/RISCV/half-imm.ll
@@ -134,3 +134,25 @@ define half @half_negative_zero(ptr %pf) nounwind {
; CHECKIZHINXMIN-NEXT: ret
ret half -0.0
}
+
+define half @freeze_poison() nounwind {
+; CHECK-LABEL: freeze_poison:
+; CHECK: # %bb.0:
+; CHECK-NEXT: fmv.h.x fa0, zero
+; CHECK-NEXT: ret
+;
+; CHECKIZHINX-LABEL: freeze_poison:
+; CHECKIZHINX: # %bb.0:
+; CHECKIZHINX-NEXT: ret
+;
+; CHECKIZFHMIN-LABEL: freeze_poison:
+; CHECKIZFHMIN: # %bb.0:
+; CHECKIZFHMIN-NEXT: fmv.h.x fa0, zero
+; CHECKIZFHMIN-NEXT: ret
+;
+; CHECKIZHINXMIN-LABEL: freeze_poison:
+; CHECKIZHINXMIN: # %bb.0:
+; CHECKIZHINXMIN-NEXT: ret
+ %a = freeze half poison
+ ret half %a
+}
More information about the llvm-commits
mailing list