[RISCV] Implement lround*/llround*/lrint*/llrint* with fcvt instruction with -fno...
authorCraig Topper <craig.topper@sifive.com>
Tue, 6 Jul 2021 17:25:37 +0000 (10:25 -0700)
committerCraig Topper <craig.topper@sifive.com>
Tue, 6 Jul 2021 18:43:22 +0000 (11:43 -0700)
These are fp->int conversions using either RMM or dynamic rounding modes.

The lround and lrint opcodes have a return type of either i32 or
i64 depending on sizeof(long) in the frontend which should follow
xlen. llround/llrint should always return i64 so we'll need a libcall
for those on rv32.

The frontend will only emit the intrinsics if -fno-math-errno is in
effect otherwise a libcall will be emitted which will not use
these ISD opcodes.

gcc also does this optimization.

Reviewed By: arcbbb

Differential Revision: https://reviews.llvm.org/D105206

llvm/lib/Target/RISCV/RISCVISelLowering.cpp
llvm/lib/Target/RISCV/RISCVInstrInfoD.td
llvm/lib/Target/RISCV/RISCVInstrInfoF.td
llvm/lib/Target/RISCV/RISCVInstrInfoZfh.td
llvm/test/CodeGen/RISCV/double-intrinsics.ll
llvm/test/CodeGen/RISCV/float-intrinsics.ll
llvm/test/CodeGen/RISCV/half-intrinsics.ll

index 9d34a95..5a7fd47 100644 (file)
@@ -314,6 +314,10 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
   if (Subtarget.hasStdExtZfh()) {
     setOperationAction(ISD::FMINNUM, MVT::f16, Legal);
     setOperationAction(ISD::FMAXNUM, MVT::f16, Legal);
+    setOperationAction(ISD::LRINT, MVT::f16, Legal);
+    setOperationAction(ISD::LLRINT, MVT::f16, Legal);
+    setOperationAction(ISD::LROUND, MVT::f16, Legal);
+    setOperationAction(ISD::LLROUND, MVT::f16, Legal);
     for (auto CC : FPCCToExpand)
       setCondCodeAction(CC, MVT::f16, Expand);
     setOperationAction(ISD::SELECT_CC, MVT::f16, Expand);
@@ -326,6 +330,10 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
   if (Subtarget.hasStdExtF()) {
     setOperationAction(ISD::FMINNUM, MVT::f32, Legal);
     setOperationAction(ISD::FMAXNUM, MVT::f32, Legal);
+    setOperationAction(ISD::LRINT, MVT::f32, Legal);
+    setOperationAction(ISD::LLRINT, MVT::f32, Legal);
+    setOperationAction(ISD::LROUND, MVT::f32, Legal);
+    setOperationAction(ISD::LLROUND, MVT::f32, Legal);
     for (auto CC : FPCCToExpand)
       setCondCodeAction(CC, MVT::f32, Expand);
     setOperationAction(ISD::SELECT_CC, MVT::f32, Expand);
@@ -343,6 +351,10 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
   if (Subtarget.hasStdExtD()) {
     setOperationAction(ISD::FMINNUM, MVT::f64, Legal);
     setOperationAction(ISD::FMAXNUM, MVT::f64, Legal);
+    setOperationAction(ISD::LRINT, MVT::f64, Legal);
+    setOperationAction(ISD::LLRINT, MVT::f64, Legal);
+    setOperationAction(ISD::LROUND, MVT::f64, Legal);
+    setOperationAction(ISD::LLROUND, MVT::f64, Legal);
     for (auto CC : FPCCToExpand)
       setCondCodeAction(CC, MVT::f64, Expand);
     setOperationAction(ISD::SELECT_CC, MVT::f64, Expand);
index e22481f..8738d67 100644 (file)
@@ -331,6 +331,12 @@ def : Pat<(f64 (fpimm0)), (FCVT_D_W (i32 X0))>;
 def : Pat<(i32 (fp_to_sint FPR64:$rs1)), (FCVT_W_D FPR64:$rs1, 0b001)>;
 def : Pat<(i32 (fp_to_uint FPR64:$rs1)), (FCVT_WU_D FPR64:$rs1, 0b001)>;
 
+// float->int32 with current rounding mode.
+def : Pat<(i32 (lrint FPR64:$rs1)), (FCVT_W_D $rs1, 0b111)>;
+
+// float->int32 rounded to nearest with ties rounded away from zero.
+def : Pat<(i32 (lround FPR64:$rs1)), (FCVT_W_D $rs1, 0b100)>;
+
 // [u]int->double.
 def : Pat<(sint_to_fp (i32 GPR:$rs1)), (FCVT_D_W GPR:$rs1)>;
 def : Pat<(uint_to_fp (i32 GPR:$rs1)), (FCVT_D_WU GPR:$rs1)>;
@@ -356,9 +362,18 @@ def : Pat<(sext_inreg (assertzexti32 (fp_to_uint FPR64:$rs1)), i32),
 def : Pat<(sint_to_fp (i64 (sexti32 (i64 GPR:$rs1)))), (FCVT_D_W $rs1)>;
 def : Pat<(uint_to_fp (i64 (zexti32 (i64 GPR:$rs1)))), (FCVT_D_WU $rs1)>;
 
+// double->[u]int64. Round-to-zero must be used.
 def : Pat<(i64 (fp_to_sint FPR64:$rs1)), (FCVT_L_D FPR64:$rs1, 0b001)>;
 def : Pat<(i64 (fp_to_uint FPR64:$rs1)), (FCVT_LU_D FPR64:$rs1, 0b001)>;
 
+// double->int64 with current rounding mode.
+def : Pat<(i64 (lrint FPR64:$rs1)), (FCVT_L_D $rs1, 0b111)>;
+def : Pat<(i64 (llrint FPR64:$rs1)), (FCVT_L_D $rs1, 0b111)>;
+
+// double->int64 rounded to nearest with ties rounded away from zero.
+def : Pat<(i64 (lround FPR64:$rs1)), (FCVT_L_D $rs1, 0b100)>;
+def : Pat<(i64 (llround FPR64:$rs1)), (FCVT_L_D $rs1, 0b100)>;
+
 // [u]int64->fp. Match GCC and default to using dynamic rounding mode.
 def : Pat<(sint_to_fp (i64 GPR:$rs1)), (FCVT_D_L GPR:$rs1, 0b111)>;
 def : Pat<(uint_to_fp (i64 GPR:$rs1)), (FCVT_D_LU GPR:$rs1, 0b111)>;
index 64337a3..86038c8 100644 (file)
@@ -373,6 +373,12 @@ def : Pat<(i32 (bitconvert FPR32:$rs1)), (FMV_X_W FPR32:$rs1)>;
 def : Pat<(i32 (fp_to_sint FPR32:$rs1)), (FCVT_W_S $rs1, 0b001)>;
 def : Pat<(i32 (fp_to_uint FPR32:$rs1)), (FCVT_WU_S $rs1, 0b001)>;
 
+// float->int32 with current rounding mode.
+def : Pat<(i32 (lrint FPR32:$rs1)), (FCVT_W_S $rs1, 0b111)>;
+
+// float->int32 rounded to nearest with ties rounded away from zero.
+def : Pat<(i32 (lround FPR32:$rs1)), (FCVT_W_S $rs1, 0b100)>;
+
 // [u]int->float. Match GCC and default to using dynamic rounding mode.
 def : Pat<(sint_to_fp (i32 GPR:$rs1)), (FCVT_S_W $rs1, 0b111)>;
 def : Pat<(uint_to_fp (i32 GPR:$rs1)), (FCVT_S_WU $rs1, 0b111)>;
@@ -392,10 +398,18 @@ def : Pat<(sext_inreg (riscv_fmv_x_anyextw_rv64 FPR32:$src), i32),
 def : Pat<(sext_inreg (assertzexti32 (fp_to_uint FPR32:$rs1)), i32),
           (FCVT_WU_S $rs1, 0b001)>;
 
-// FP->[u]int64
+// float->[u]int64. Round-to-zero must be used.
 def : Pat<(i64 (fp_to_sint FPR32:$rs1)), (FCVT_L_S $rs1, 0b001)>;
 def : Pat<(i64 (fp_to_uint FPR32:$rs1)), (FCVT_LU_S $rs1, 0b001)>;
 
+// float->int64 with current rounding mode.
+def : Pat<(i64 (lrint FPR32:$rs1)), (FCVT_L_S $rs1, 0b111)>;
+def : Pat<(i64 (llrint FPR32:$rs1)), (FCVT_L_S $rs1, 0b111)>;
+
+// float->int64 rounded to neartest with ties rounded away from zero.
+def : Pat<(i64 (lround FPR32:$rs1)), (FCVT_L_S $rs1, 0b100)>;
+def : Pat<(i64 (llround FPR32:$rs1)), (FCVT_L_S $rs1, 0b100)>;
+
 // [u]int->fp. Match GCC and default to using dynamic rounding mode.
 def : Pat<(sint_to_fp (i64 (sexti32 (i64 GPR:$rs1)))), (FCVT_S_W $rs1, 0b111)>;
 def : Pat<(uint_to_fp (i64 (zexti32 (i64 GPR:$rs1)))), (FCVT_S_WU $rs1, 0b111)>;
index ebaf6e4..530e365 100644 (file)
@@ -334,11 +334,17 @@ def : Pat<(riscv_fmv_x_anyexth FPR16:$src), (FMV_X_H FPR16:$src)>;
 } // Predicates = [HasStdExtZfh]
 
 let Predicates = [HasStdExtZfh, IsRV32] in {
-// float->[u]int. Round-to-zero must be used.
+// half->[u]int. Round-to-zero must be used.
 def : Pat<(i32 (fp_to_sint FPR16:$rs1)), (FCVT_W_H $rs1, 0b001)>;
 def : Pat<(i32 (fp_to_uint FPR16:$rs1)), (FCVT_WU_H $rs1, 0b001)>;
 
-// [u]int->float. Match GCC and default to using dynamic rounding mode.
+// half->int32 with current rounding mode.
+def : Pat<(i32 (lrint FPR16:$rs1)), (FCVT_W_H $rs1, 0b111)>;
+
+// half->int32 rounded to nearest with ties rounded away from zero.
+def : Pat<(i32 (lround FPR16:$rs1)), (FCVT_W_H $rs1, 0b100)>;
+
+// [u]int->half. Match GCC and default to using dynamic rounding mode.
 def : Pat<(sint_to_fp (i32 GPR:$rs1)), (FCVT_H_W $rs1, 0b111)>;
 def : Pat<(uint_to_fp (i32 GPR:$rs1)), (FCVT_H_WU $rs1, 0b111)>;
 } // Predicates = [HasStdExtZfh, IsRV32]
@@ -351,10 +357,18 @@ let Predicates = [HasStdExtZfh, IsRV64] in {
 def : Pat<(sext_inreg (assertzexti32 (fp_to_uint FPR16:$rs1)), i32),
           (FCVT_WU_H $rs1, 0b001)>;
 
-// FP->[u]int64
+// half->[u]int64. Round-to-zero must be used.
 def : Pat<(i64 (fp_to_sint FPR16:$rs1)), (FCVT_L_H $rs1, 0b001)>;
 def : Pat<(i64 (fp_to_uint FPR16:$rs1)), (FCVT_LU_H $rs1, 0b001)>;
 
+// half->int64 with current rounding mode.
+def : Pat<(i64 (lrint FPR16:$rs1)), (FCVT_L_H $rs1, 0b111)>;
+def : Pat<(i64 (llrint FPR16:$rs1)), (FCVT_L_H $rs1, 0b111)>;
+
+// half->int64 rounded to nearest with ties rounded away from zero.
+def : Pat<(i64 (lround FPR16:$rs1)), (FCVT_L_H $rs1, 0b100)>;
+def : Pat<(i64 (llround FPR16:$rs1)), (FCVT_L_H $rs1, 0b100)>;
+
 // [u]int->fp. Match GCC and default to using dynamic rounding mode.
 def : Pat<(sint_to_fp (i64 (sexti32 (i64 GPR:$rs1)))), (FCVT_H_W $rs1, 0b111)>;
 def : Pat<(uint_to_fp (i64 (zexti32 (i64 GPR:$rs1)))), (FCVT_H_WU $rs1, 0b111)>;
index 79dcd37..7e77f69 100644 (file)
@@ -1,8 +1,8 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc -mtriple=riscv32 -mattr=+d -verify-machineinstrs < %s \
-; RUN:   | FileCheck -check-prefix=RV32IFD %s
-; RUN: llc -mtriple=riscv64 -mattr=+d -verify-machineinstrs < %s \
-; RUN:   | FileCheck -check-prefix=RV64IFD %s
+; RUN: sed 's/iXLen/i32/g' %s | llc -mtriple=riscv32 -mattr=+d \
+; RUN:   -verify-machineinstrs | FileCheck -check-prefix=RV32IFD %s
+; RUN: sed 's/iXLen/i64/g' %s | llc -mtriple=riscv64 -mattr=+d \
+; RUN:   -verify-machineinstrs | FileCheck -check-prefix=RV64IFD %s
 
 declare double @llvm.sqrt.f64(double)
 
@@ -642,3 +642,81 @@ define double @round_f64(double %a) nounwind {
   %1 = call double @llvm.round.f64(double %a)
   ret double %1
 }
+
+declare iXLen @llvm.lrint.iXLen.f64(float)
+
+define iXLen @lrint_f64(float %a) nounwind {
+; RV32IFD-LABEL: lrint_f64:
+; RV32IFD:       # %bb.0:
+; RV32IFD-NEXT:    fmv.w.x ft0, a0
+; RV32IFD-NEXT:    fcvt.w.s a0, ft0
+; RV32IFD-NEXT:    ret
+;
+; RV64IFD-LABEL: lrint_f64:
+; RV64IFD:       # %bb.0:
+; RV64IFD-NEXT:    fmv.w.x ft0, a0
+; RV64IFD-NEXT:    fcvt.l.s a0, ft0
+; RV64IFD-NEXT:    ret
+  %1 = call iXLen @llvm.lrint.iXLen.f64(float %a)
+  ret iXLen %1
+}
+
+declare iXLen @llvm.lround.iXLen.f64(float)
+
+define iXLen @lround_f64(float %a) nounwind {
+; RV32IFD-LABEL: lround_f64:
+; RV32IFD:       # %bb.0:
+; RV32IFD-NEXT:    fmv.w.x ft0, a0
+; RV32IFD-NEXT:    fcvt.w.s a0, ft0, rmm
+; RV32IFD-NEXT:    ret
+;
+; RV64IFD-LABEL: lround_f64:
+; RV64IFD:       # %bb.0:
+; RV64IFD-NEXT:    fmv.w.x ft0, a0
+; RV64IFD-NEXT:    fcvt.l.s a0, ft0, rmm
+; RV64IFD-NEXT:    ret
+  %1 = call iXLen @llvm.lround.iXLen.f64(float %a)
+  ret iXLen %1
+}
+
+declare i64 @llvm.llrint.i64.f64(float)
+
+define i64 @llrint_f64(float %a) nounwind {
+; RV32IFD-LABEL: llrint_f64:
+; RV32IFD:       # %bb.0:
+; RV32IFD-NEXT:    addi sp, sp, -16
+; RV32IFD-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32IFD-NEXT:    call llrintf@plt
+; RV32IFD-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32IFD-NEXT:    addi sp, sp, 16
+; RV32IFD-NEXT:    ret
+;
+; RV64IFD-LABEL: llrint_f64:
+; RV64IFD:       # %bb.0:
+; RV64IFD-NEXT:    fmv.w.x ft0, a0
+; RV64IFD-NEXT:    fcvt.l.s a0, ft0
+; RV64IFD-NEXT:    ret
+  %1 = call i64 @llvm.llrint.i64.f64(float %a)
+  ret i64 %1
+}
+
+declare i64 @llvm.llround.i64.f64(float)
+
+define i64 @llround_f64(float %a) nounwind {
+; RV32IFD-LABEL: llround_f64:
+; RV32IFD:       # %bb.0:
+; RV32IFD-NEXT:    addi sp, sp, -16
+; RV32IFD-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32IFD-NEXT:    call llroundf@plt
+; RV32IFD-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32IFD-NEXT:    addi sp, sp, 16
+; RV32IFD-NEXT:    ret
+;
+; RV64IFD-LABEL: llround_f64:
+; RV64IFD:       # %bb.0:
+; RV64IFD-NEXT:    fmv.w.x ft0, a0
+; RV64IFD-NEXT:    fcvt.l.s a0, ft0, rmm
+; RV64IFD-NEXT:    ret
+  %1 = call i64 @llvm.llround.i64.f64(float %a)
+  ret i64 %1
+}
index cda32b3..8423ce6 100644 (file)
@@ -1,12 +1,12 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc -mtriple=riscv32 -mattr=+f -verify-machineinstrs < %s \
-; RUN:   | FileCheck -check-prefix=RV32IF %s
-; RUN: llc -mtriple=riscv32 -mattr=+d -verify-machineinstrs < %s \
-; RUN:   | FileCheck -check-prefix=RV32IF %s
-; RUN: llc -mtriple=riscv64 -mattr=+f -verify-machineinstrs < %s \
-; RUN:   | FileCheck -check-prefix=RV64IF %s
-; RUN: llc -mtriple=riscv64 -mattr=+d -verify-machineinstrs < %s \
-; RUN:   | FileCheck -check-prefix=RV64IF %s
+; RUN: sed 's/iXLen/i32/g' %s | llc -mtriple=riscv32 -mattr=+f \
+; RUN:   -verify-machineinstrs | FileCheck -check-prefix=RV32IF %s
+; RUN: sed 's/iXLen/i32/g' %s | llc -mtriple=riscv32 -mattr=+d \
+; RUN:   -verify-machineinstrs | FileCheck -check-prefix=RV32IF %s
+; RUN: sed 's/iXLen/i64/g' %s | llc -mtriple=riscv64 -mattr=+f \
+; RUN:   -verify-machineinstrs | FileCheck -check-prefix=RV64IF %s
+; RUN: sed 's/iXLen/i64/g' %s | llc -mtriple=riscv64 -mattr=+d \
+; RUN:   -verify-machineinstrs | FileCheck -check-prefix=RV64IF %s
 
 declare float @llvm.sqrt.f32(float)
 
@@ -586,3 +586,81 @@ define float @round_f32(float %a) nounwind {
   %1 = call float @llvm.round.f32(float %a)
   ret float %1
 }
+
+declare iXLen @llvm.lrint.iXLen.f32(float)
+
+define iXLen @lrint_f32(float %a) nounwind {
+; RV32IF-LABEL: lrint_f32:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fmv.w.x ft0, a0
+; RV32IF-NEXT:    fcvt.w.s a0, ft0
+; RV32IF-NEXT:    ret
+;
+; RV64IF-LABEL: lrint_f32:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fmv.w.x ft0, a0
+; RV64IF-NEXT:    fcvt.l.s a0, ft0
+; RV64IF-NEXT:    ret
+  %1 = call iXLen @llvm.lrint.iXLen.f32(float %a)
+  ret iXLen %1
+}
+
+declare iXLen @llvm.lround.iXLen.f32(float)
+
+define iXLen @lround_f32(float %a) nounwind {
+; RV32IF-LABEL: lround_f32:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fmv.w.x ft0, a0
+; RV32IF-NEXT:    fcvt.w.s a0, ft0, rmm
+; RV32IF-NEXT:    ret
+;
+; RV64IF-LABEL: lround_f32:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fmv.w.x ft0, a0
+; RV64IF-NEXT:    fcvt.l.s a0, ft0, rmm
+; RV64IF-NEXT:    ret
+  %1 = call iXLen @llvm.lround.iXLen.f32(float %a)
+  ret iXLen %1
+}
+
+declare i64 @llvm.llrint.i64.f32(float)
+
+define i64 @llrint_f32(float %a) nounwind {
+; RV32IF-LABEL: llrint_f32:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    addi sp, sp, -16
+; RV32IF-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32IF-NEXT:    call llrintf@plt
+; RV32IF-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32IF-NEXT:    addi sp, sp, 16
+; RV32IF-NEXT:    ret
+;
+; RV64IF-LABEL: llrint_f32:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fmv.w.x ft0, a0
+; RV64IF-NEXT:    fcvt.l.s a0, ft0
+; RV64IF-NEXT:    ret
+  %1 = call i64 @llvm.llrint.i64.f32(float %a)
+  ret i64 %1
+}
+
+declare i64 @llvm.llround.i64.f32(float)
+
+define i64 @llround_f32(float %a) nounwind {
+; RV32IF-LABEL: llround_f32:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    addi sp, sp, -16
+; RV32IF-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32IF-NEXT:    call llroundf@plt
+; RV32IF-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32IF-NEXT:    addi sp, sp, 16
+; RV32IF-NEXT:    ret
+;
+; RV64IF-LABEL: llround_f32:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fmv.w.x ft0, a0
+; RV64IF-NEXT:    fcvt.l.s a0, ft0, rmm
+; RV64IF-NEXT:    ret
+  %1 = call i64 @llvm.llround.i64.f32(float %a)
+  ret i64 %1
+}
index c9a326e..32015ae 100644 (file)
@@ -1,12 +1,16 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc -mtriple=riscv32 -mattr=+experimental-zfh -verify-machineinstrs \
-; RUN:   -target-abi ilp32f < %s | FileCheck -check-prefix=RV32IZFH %s
-; RUN: llc -mtriple=riscv32 -mattr=+d -mattr=+experimental-zfh -verify-machineinstrs \
-; RUN:   -target-abi ilp32d < %s | FileCheck -check-prefix=RV32IDZFH %s
-; RUN: llc -mtriple=riscv64 -mattr=+experimental-zfh -verify-machineinstrs \
-; RUN:   -target-abi lp64f < %s | FileCheck -check-prefix=RV64IZFH %s
-; RUN: llc -mtriple=riscv64 -mattr=+d -mattr=+experimental-zfh -verify-machineinstrs \
-; RUN:   -target-abi lp64d < %s | FileCheck -check-prefix=RV64IDZFH %s
+; RUN: sed 's/iXLen/i32/g' %s | llc -mtriple=riscv32 -mattr=+experimental-zfh \
+; RUN:   -verify-machineinstrs -target-abi ilp32f | \
+; RUN:   FileCheck -check-prefix=RV32IZFH %s
+; RUN: sed 's/iXLen/i32/g' %s | llc -mtriple=riscv32 -mattr=+d \
+; RUN:   -mattr=+experimental-zfh -verify-machineinstrs -target-abi ilp32d | \
+; RUN:   FileCheck -check-prefix=RV32IDZFH %s
+; RUN: sed 's/iXLen/i64/g' %s | llc -mtriple=riscv64 -mattr=+experimental-zfh \
+; RUN:   -verify-machineinstrs -target-abi lp64f | \
+; RUN:   FileCheck -check-prefix=RV64IZFH %s
+; RUN: sed 's/iXLen/i64/g' %s | llc -mtriple=riscv64 -mattr=+d \
+; RUN:   -mattr=+experimental-zfh -verify-machineinstrs -target-abi lp64d | \
+; RUN:   FileCheck -check-prefix=RV64IDZFH %s
 
 declare half @llvm.sqrt.f16(half)
 
@@ -189,3 +193,123 @@ define half @copysign_f16(half %a, half %b) nounwind {
   %1 = call half @llvm.copysign.f16(half %a, half %b)
   ret half %1
 }
+
+declare iXLen @llvm.lrint.iXLen.f16(float)
+
+define iXLen @lrint_f16(float %a) nounwind {
+; RV32IZFH-LABEL: lrint_f16:
+; RV32IZFH:       # %bb.0:
+; RV32IZFH-NEXT:    fcvt.w.s a0, fa0
+; RV32IZFH-NEXT:    ret
+;
+; RV32IDZFH-LABEL: lrint_f16:
+; RV32IDZFH:       # %bb.0:
+; RV32IDZFH-NEXT:    fcvt.w.s a0, fa0
+; RV32IDZFH-NEXT:    ret
+;
+; RV64IZFH-LABEL: lrint_f16:
+; RV64IZFH:       # %bb.0:
+; RV64IZFH-NEXT:    fcvt.l.s a0, fa0
+; RV64IZFH-NEXT:    ret
+;
+; RV64IDZFH-LABEL: lrint_f16:
+; RV64IDZFH:       # %bb.0:
+; RV64IDZFH-NEXT:    fcvt.l.s a0, fa0
+; RV64IDZFH-NEXT:    ret
+  %1 = call iXLen @llvm.lrint.iXLen.f16(float %a)
+  ret iXLen %1
+}
+
+declare iXLen @llvm.lround.iXLen.f16(float)
+
+define iXLen @lround_f16(float %a) nounwind {
+; RV32IZFH-LABEL: lround_f16:
+; RV32IZFH:       # %bb.0:
+; RV32IZFH-NEXT:    fcvt.w.s a0, fa0, rmm
+; RV32IZFH-NEXT:    ret
+;
+; RV32IDZFH-LABEL: lround_f16:
+; RV32IDZFH:       # %bb.0:
+; RV32IDZFH-NEXT:    fcvt.w.s a0, fa0, rmm
+; RV32IDZFH-NEXT:    ret
+;
+; RV64IZFH-LABEL: lround_f16:
+; RV64IZFH:       # %bb.0:
+; RV64IZFH-NEXT:    fcvt.l.s a0, fa0, rmm
+; RV64IZFH-NEXT:    ret
+;
+; RV64IDZFH-LABEL: lround_f16:
+; RV64IDZFH:       # %bb.0:
+; RV64IDZFH-NEXT:    fcvt.l.s a0, fa0, rmm
+; RV64IDZFH-NEXT:    ret
+  %1 = call iXLen @llvm.lround.iXLen.f16(float %a)
+  ret iXLen %1
+}
+
+declare i64 @llvm.llrint.i64.f16(float)
+
+define i64 @llrint_f16(float %a) nounwind {
+; RV32IZFH-LABEL: llrint_f16:
+; RV32IZFH:       # %bb.0:
+; RV32IZFH-NEXT:    addi sp, sp, -16
+; RV32IZFH-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32IZFH-NEXT:    call llrintf@plt
+; RV32IZFH-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32IZFH-NEXT:    addi sp, sp, 16
+; RV32IZFH-NEXT:    ret
+;
+; RV32IDZFH-LABEL: llrint_f16:
+; RV32IDZFH:       # %bb.0:
+; RV32IDZFH-NEXT:    addi sp, sp, -16
+; RV32IDZFH-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32IDZFH-NEXT:    call llrintf@plt
+; RV32IDZFH-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32IDZFH-NEXT:    addi sp, sp, 16
+; RV32IDZFH-NEXT:    ret
+;
+; RV64IZFH-LABEL: llrint_f16:
+; RV64IZFH:       # %bb.0:
+; RV64IZFH-NEXT:    fcvt.l.s a0, fa0
+; RV64IZFH-NEXT:    ret
+;
+; RV64IDZFH-LABEL: llrint_f16:
+; RV64IDZFH:       # %bb.0:
+; RV64IDZFH-NEXT:    fcvt.l.s a0, fa0
+; RV64IDZFH-NEXT:    ret
+  %1 = call i64 @llvm.llrint.i64.f16(float %a)
+  ret i64 %1
+}
+
+declare i64 @llvm.llround.i64.f16(float)
+
+define i64 @llround_f16(float %a) nounwind {
+; RV32IZFH-LABEL: llround_f16:
+; RV32IZFH:       # %bb.0:
+; RV32IZFH-NEXT:    addi sp, sp, -16
+; RV32IZFH-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32IZFH-NEXT:    call llroundf@plt
+; RV32IZFH-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32IZFH-NEXT:    addi sp, sp, 16
+; RV32IZFH-NEXT:    ret
+;
+; RV32IDZFH-LABEL: llround_f16:
+; RV32IDZFH:       # %bb.0:
+; RV32IDZFH-NEXT:    addi sp, sp, -16
+; RV32IDZFH-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32IDZFH-NEXT:    call llroundf@plt
+; RV32IDZFH-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32IDZFH-NEXT:    addi sp, sp, 16
+; RV32IDZFH-NEXT:    ret
+;
+; RV64IZFH-LABEL: llround_f16:
+; RV64IZFH:       # %bb.0:
+; RV64IZFH-NEXT:    fcvt.l.s a0, fa0, rmm
+; RV64IZFH-NEXT:    ret
+;
+; RV64IDZFH-LABEL: llround_f16:
+; RV64IDZFH:       # %bb.0:
+; RV64IDZFH-NEXT:    fcvt.l.s a0, fa0, rmm
+; RV64IDZFH-NEXT:    ret
+  %1 = call i64 @llvm.llround.i64.f16(float %a)
+  ret i64 %1
+}