[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