[llvm] [RISCV][ISel] Use saturating fcvt.w.dyn for lrint i32 on rv64 (PR #214678)
Kane Wang via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 7 02:33:58 PDT 2026
https://github.com/ReVe1uv created https://github.com/llvm/llvm-project/pull/214678
LRINT i32 on rv64 was promoted to i64 and selected fcvt.l.* + trunc, producing unspecified garbage for out-of-range input. LROUND i32 on rv64 already custom-lowers to saturating fcvt.w.* with RMM. Do the same for LRINT, but with FRM_DYN since lrint uses the current rounding mode rather than ties-away. Out-of-range results saturate to INT32_MAX/INT32_MIN (NaN -> 0).
>From b38f4461cec685066c6943671cbeb13fbfaa0ded Mon Sep 17 00:00:00 2001
From: Kane Wang <wangqiang1 at kylinos.cn>
Date: Fri, 7 Aug 2026 17:30:11 +0800
Subject: [PATCH] [RISCV][ISel] Use saturating fcvt.w.dyn for lrint i32 on rv64
LRINT i32 on rv64 was promoted to i64 and selected fcvt.l.* + trunc,
producing unspecified garbage for out-of-range input. LROUND i32 on
rv64 already custom-lowers to saturating fcvt.w.* with RMM. Do the
same for LRINT, but with FRM_DYN since lrint uses the current
rounding mode rather than ties-away. Out-of-range results saturate
to INT32_MAX/INT32_MIN (NaN -> 0).
---
llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 22 +++++++----
llvm/test/CodeGen/RISCV/double-intrinsics.ll | 41 +++++++++++++++++++-
2 files changed, 54 insertions(+), 9 deletions(-)
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index ea4803e59ebc1..8b5d0bed1e718 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -904,6 +904,7 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
ISD::STRICT_FP_TO_UINT, ISD::STRICT_FP_TO_SINT},
MVT::i32, Custom);
setOperationAction(ISD::LROUND, MVT::i32, Custom);
+ setOperationAction(ISD::LRINT, MVT::i32, Custom);
}
if (Subtarget.hasStdExtFOrZfinx()) {
@@ -15741,7 +15742,9 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N,
Results.push_back(Chain);
break;
}
- case ISD::LROUND: {
+ case ISD::LROUND:
+ case ISD::LRINT: {
+ bool IsLRound = N->getOpcode() == ISD::LROUND;
SDValue Op0 = N->getOperand(0);
EVT Op0VT = Op0.getValueType();
if (getTypeAction(*DAG.getContext(), Op0.getValueType()) !=
@@ -15753,17 +15756,22 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N,
if (Op0.getValueType() == MVT::f16 && !Subtarget.hasStdExtZfhOrZhinx())
Op0 = DAG.getNode(ISD::FP_EXTEND, DL, MVT::f32, Op0);
+ // LROUND rounds ties away from zero (RMM); LRINT uses the current
+ // rounding mode (DYN). fcvt.w saturates out-of-range results to
+ // INT32_MAX/INT32_MIN (NaN -> 0).
+ auto RndMode = IsLRound ? RISCVFPRndMode::RMM : RISCVFPRndMode::DYN;
SDValue Res =
DAG.getNode(RISCVISD::FCVT_W_RV64, DL, MVT::i64, Op0,
- DAG.getTargetConstant(RISCVFPRndMode::RMM, DL, MVT::i64));
+ DAG.getTargetConstant(RndMode, DL, MVT::i64));
Results.push_back(DAG.getNode(ISD::TRUNCATE, DL, MVT::i32, Res));
return;
}
- // If the FP type needs to be softened, emit a library call to lround. We'll
- // need to truncate the result. We assume any value that doesn't fit in i32
- // is allowed to return an unspecified value.
- RTLIB::Libcall LC = RTLIB::getLROUND(Op0.getValueType());
- assert(LC != RTLIB::UNKNOWN_LIBCALL && "Unexpected FP type for LROUND!");
+ // If the FP type needs to be softened, emit a library call to lround/lrint.
+ // We'll need to truncate the result. We assume any value that doesn't fit
+ // in i32 is allowed to return an unspecified value.
+ RTLIB::Libcall LC = IsLRound ? RTLIB::getLROUND(Op0.getValueType())
+ : RTLIB::getLRINT(Op0.getValueType());
+ assert(LC != RTLIB::UNKNOWN_LIBCALL && "Unexpected FP type!");
MakeLibCallOptions CallOptions;
EVT OpVT = Op0.getValueType();
CallOptions.setTypeListBeforeSoften(OpVT, MVT::i64);
diff --git a/llvm/test/CodeGen/RISCV/double-intrinsics.ll b/llvm/test/CodeGen/RISCV/double-intrinsics.ll
index 795c0dfc23b04..54df3578912c1 100644
--- a/llvm/test/CodeGen/RISCV/double-intrinsics.ll
+++ b/llvm/test/CodeGen/RISCV/double-intrinsics.ll
@@ -1368,6 +1368,43 @@ define i32 @lround_i32_f64(double %a) nounwind {
ret i32 %1
}
+define i32 @lrint_i32_f64(double %a) nounwind {
+; CHECKIFD-LABEL: lrint_i32_f64:
+; CHECKIFD: # %bb.0:
+; CHECKIFD-NEXT: fcvt.w.d a0, fa0
+; CHECKIFD-NEXT: ret
+;
+; RV32IZFINXZDINX-LABEL: lrint_i32_f64:
+; RV32IZFINXZDINX: # %bb.0:
+; RV32IZFINXZDINX-NEXT: fcvt.w.d a0, a0
+; RV32IZFINXZDINX-NEXT: ret
+;
+; RV64IZFINXZDINX-LABEL: lrint_i32_f64:
+; RV64IZFINXZDINX: # %bb.0:
+; RV64IZFINXZDINX-NEXT: fcvt.w.d a0, a0
+; RV64IZFINXZDINX-NEXT: ret
+;
+; RV32I-LABEL: lrint_i32_f64:
+; RV32I: # %bb.0:
+; RV32I-NEXT: addi sp, sp, -16
+; RV32I-NEXT: sw ra, 12(sp) # 4-byte Folded Spill
+; RV32I-NEXT: call lrint
+; RV32I-NEXT: lw ra, 12(sp) # 4-byte Folded Reload
+; RV32I-NEXT: addi sp, sp, 16
+; RV32I-NEXT: ret
+;
+; RV64I-LABEL: lrint_i32_f64:
+; RV64I: # %bb.0:
+; RV64I-NEXT: addi sp, sp, -16
+; RV64I-NEXT: sd ra, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT: call lrint
+; RV64I-NEXT: ld ra, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT: addi sp, sp, 16
+; RV64I-NEXT: ret
+ %1 = call i32 @llvm.lrint.i32.f64(double %a)
+ ret i32 %1
+}
+
define i64 @llrint_f64(double %a) nounwind {
; RV32IFD-LABEL: llrint_f64:
; RV32IFD: # %bb.0:
@@ -1495,11 +1532,11 @@ define i1 @isnan_d_fpclass(double %x) {
; RV32I-NEXT: lui a2, 524032
; RV32I-NEXT: slli a1, a1, 1
; RV32I-NEXT: srli a1, a1, 1
-; RV32I-NEXT: beq a1, a2, .LBB30_2
+; RV32I-NEXT: beq a1, a2, .LBB31_2
; RV32I-NEXT: # %bb.1:
; RV32I-NEXT: slt a0, a2, a1
; RV32I-NEXT: ret
-; RV32I-NEXT: .LBB30_2:
+; RV32I-NEXT: .LBB31_2:
; RV32I-NEXT: snez a0, a0
; RV32I-NEXT: ret
;
More information about the llvm-commits
mailing list