[llvm] 6b0c183 - [RISCV][GlobalISel] Support G_INTRINSIC_LRINT/G_INTRINSIC_LLRINT (#221992)

via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 9 18:09:48 PDT 2026


Author: Kane Wang
Date: 2026-09-10T09:09:43+08:00
New Revision: 6b0c1836d05ab22f55ec5b8e76bc797415de10f3

URL: https://github.com/llvm/llvm-project/commit/6b0c1836d05ab22f55ec5b8e76bc797415de10f3
DIFF: https://github.com/llvm/llvm-project/commit/6b0c1836d05ab22f55ec5b8e76bc797415de10f3.diff

LOG: [RISCV][GlobalISel] Support G_INTRINSIC_LRINT/G_INTRINSIC_LLRINT (#221992)

Add legalizer rules so llvm.lrint/llrint lower to fcvt.{w,l}.{h,s,d}
with FRM_DYN, mirroring the existing G_LROUND/G_LLROUND handling.

Added: 
    llvm/test/CodeGen/RISCV/GlobalISel/lrint-llrint-f16.ll
    llvm/test/CodeGen/RISCV/GlobalISel/lrint-llrint.ll

Modified: 
    llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
    llvm/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp
    llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
index d0ee9bb39cc8b..d587b7090c6b4 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
@@ -649,6 +649,19 @@ RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &ST)
                    {s32, s128},
                    {s64, s128}});
 
+  getActionDefinitionsBuilder({G_INTRINSIC_LRINT, G_INTRINSIC_LLRINT})
+      .legalFor(ST.hasStdExtF(), {{sXLen, s32}})
+      .legalFor(ST.hasStdExtD(), {{sXLen, s64}})
+      .legalFor(ST.hasStdExtZfh(), {{sXLen, s16}})
+      .minScalar(0, sXLen)
+      .widenScalarIf(typeIs(1, s16), LegalizeMutations::changeTo(1, s32))
+      .libcallFor({{s32, s32},
+                   {s64, s32},
+                   {s32, s64},
+                   {s64, s64},
+                   {s32, s128},
+                   {s64, s128}});
+
   getActionDefinitionsBuilder({G_SITOFP, G_UITOFP})
       .legalFor(ST.hasStdExtF(), {{s32, sXLen}})
       .legalFor(ST.hasStdExtD(), {{s64, sXLen}})

diff  --git a/llvm/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp
index 53399a13611d2..d6a9df281ceb1 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp
@@ -170,6 +170,8 @@ bool RISCVRegisterBankInfo::onlyUsesFP(const MachineInstr &MI,
   case TargetOpcode::G_FPTOUI:
   case TargetOpcode::G_LROUND:
   case TargetOpcode::G_LLROUND:
+  case TargetOpcode::G_INTRINSIC_LRINT:
+  case TargetOpcode::G_INTRINSIC_LLRINT:
   case TargetOpcode::G_FCMP:
     return true;
   default:
@@ -466,6 +468,8 @@ RISCVRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
   case TargetOpcode::G_FPTOUI:
   case TargetOpcode::G_LROUND:
   case TargetOpcode::G_LLROUND:
+  case TargetOpcode::G_INTRINSIC_LRINT:
+  case TargetOpcode::G_INTRINSIC_LLRINT:
   case RISCV::G_FCLASS: {
     LLT Ty = MRI.getType(MI.getOperand(1).getReg());
     OpdsMapping[0] = GPRValueMapping;

diff  --git a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir
index a9a045daaaa9f..8e350e59123ca 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir
@@ -187,11 +187,12 @@
 # DEBUG-NEXT: .. the first uncovered type index: 1, OK
 # DEBUG-NEXT: .. the first uncovered imm index: 0, OK
 # DEBUG-NEXT: G_INTRINSIC_LRINT (opcode {{[0-9]+}}): 2 type indices, 0 imm indices
-# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined
-# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined
+# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
+# DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
 # DEBUG-NEXT: G_INTRINSIC_LLRINT (opcode {{[0-9]+}}): 2 type indices, 0 imm indices
-# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined
-# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined
+# DEBUG-NEXT: .. opcode {{[0-9]+}} is aliased to {{[0-9]+}}
+# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
+# DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
 # DEBUG-NEXT: G_INTRINSIC_ROUNDEVEN (opcode {{[0-9]+}}): 1 type index, 0 imm indices
 # DEBUG-NEXT: .. opcode {{[0-9]+}} is aliased to {{[0-9]+}}
 # DEBUG-NEXT: .. the first uncovered type index: 1, OK

diff  --git a/llvm/test/CodeGen/RISCV/GlobalISel/lrint-llrint-f16.ll b/llvm/test/CodeGen/RISCV/GlobalISel/lrint-llrint-f16.ll
new file mode 100644
index 0000000000000..a60d1d0cccfa1
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/lrint-llrint-f16.ll
@@ -0,0 +1,41 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -global-isel -mattr=+f,+d,+zfh -target-abi=ilp32d < %s | FileCheck %s --check-prefixes=CHECK,RV32IFD
+; RUN: llc -mtriple=riscv64 -global-isel -mattr=+f,+d,+zfh -target-abi=lp64d < %s | FileCheck %s --check-prefixes=CHECK,RV64IFD
+
+define i32 @lrint_f16(half %a) nounwind {
+; RV32IFD-LABEL: lrint_f16:
+; RV32IFD:       # %bb.0:
+; RV32IFD-NEXT:    fcvt.w.h a0, fa0
+; RV32IFD-NEXT:    ret
+;
+; RV64IFD-LABEL: lrint_f16:
+; RV64IFD:       # %bb.0:
+; RV64IFD-NEXT:    fcvt.l.h a0, fa0
+; RV64IFD-NEXT:    ret
+  %r = call i32 @llvm.lrint.f16(half %a)
+  ret i32 %r
+}
+
+define i64 @llrint_f16(half %a) nounwind {
+; RV32IFD-LABEL: llrint_f16:
+; RV32IFD:       # %bb.0:
+; RV32IFD-NEXT:    addi sp, sp, -16
+; RV32IFD-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32IFD-NEXT:    fcvt.s.h fa0, fa0
+; RV32IFD-NEXT:    call llrintf
+; RV32IFD-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32IFD-NEXT:    addi sp, sp, 16
+; RV32IFD-NEXT:    ret
+;
+; RV64IFD-LABEL: llrint_f16:
+; RV64IFD:       # %bb.0:
+; RV64IFD-NEXT:    fcvt.l.h a0, fa0
+; RV64IFD-NEXT:    ret
+  %r = call i64 @llvm.llrint.f16(half %a)
+  ret i64 %r
+}
+
+declare i32 @llvm.lrint.f16(half)
+declare i64 @llvm.llrint.f16(half)
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK: {{.*}}

diff  --git a/llvm/test/CodeGen/RISCV/GlobalISel/lrint-llrint.ll b/llvm/test/CodeGen/RISCV/GlobalISel/lrint-llrint.ll
new file mode 100644
index 0000000000000..33a3e08d27960
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/lrint-llrint.ll
@@ -0,0 +1,268 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -global-isel -mattr=+f,+d -target-abi=ilp32d < %s | FileCheck %s --check-prefixes=CHECK,RV32IFD
+; RUN: llc -mtriple=riscv64 -global-isel -mattr=+f,+d -target-abi=lp64d < %s | FileCheck %s --check-prefixes=CHECK,RV64IFD
+; RUN: llc -mtriple=riscv32 -global-isel < %s | FileCheck %s --check-prefixes=CHECK,RV32I
+; RUN: llc -mtriple=riscv64 -global-isel < %s | FileCheck %s --check-prefixes=CHECK,RV64I
+
+define i32 @lrint_f32(float %a) nounwind {
+; RV32IFD-LABEL: lrint_f32:
+; RV32IFD:       # %bb.0:
+; RV32IFD-NEXT:    fcvt.w.s a0, fa0
+; RV32IFD-NEXT:    ret
+;
+; RV64IFD-LABEL: lrint_f32:
+; RV64IFD:       # %bb.0:
+; RV64IFD-NEXT:    fcvt.l.s a0, fa0
+; RV64IFD-NEXT:    ret
+;
+; RV32I-LABEL: lrint_f32:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    call lrintf
+; RV32I-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: lrint_f32:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    call lrintf
+; RV64I-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    ret
+  %r = call i32 @llvm.lrint.f32(float %a)
+  ret i32 %r
+}
+
+define i32 @lrint_f64(double %a) nounwind {
+; RV32IFD-LABEL: lrint_f64:
+; RV32IFD:       # %bb.0:
+; RV32IFD-NEXT:    fcvt.w.d a0, fa0
+; RV32IFD-NEXT:    ret
+;
+; RV64IFD-LABEL: lrint_f64:
+; RV64IFD:       # %bb.0:
+; RV64IFD-NEXT:    fcvt.l.d a0, fa0
+; RV64IFD-NEXT:    ret
+;
+; RV32I-LABEL: lrint_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_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
+  %r = call i32 @llvm.lrint.f64(double %a)
+  ret i32 %r
+}
+
+define i64 @llrint_f32(float %a) nounwind {
+; RV32IFD-LABEL: llrint_f32:
+; RV32IFD:       # %bb.0:
+; RV32IFD-NEXT:    addi sp, sp, -16
+; RV32IFD-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32IFD-NEXT:    call llrintf
+; RV32IFD-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32IFD-NEXT:    addi sp, sp, 16
+; RV32IFD-NEXT:    ret
+;
+; RV64IFD-LABEL: llrint_f32:
+; RV64IFD:       # %bb.0:
+; RV64IFD-NEXT:    fcvt.l.s a0, fa0
+; RV64IFD-NEXT:    ret
+;
+; RV32I-LABEL: llrint_f32:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    call llrintf
+; RV32I-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: llrint_f32:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    call llrintf
+; RV64I-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    ret
+  %r = call i64 @llvm.llrint.f32(float %a)
+  ret i64 %r
+}
+
+define i64 @llrint_f64(double %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 llrint
+; 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:    fcvt.l.d a0, fa0
+; RV64IFD-NEXT:    ret
+;
+; RV32I-LABEL: llrint_f64:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    call llrint
+; RV32I-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: llrint_f64:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    call llrint
+; RV64I-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    ret
+  %r = call i64 @llvm.llrint.f64(double %a)
+  ret i64 %r
+}
+
+; fp128 has no native path; it lowers to the lrintl/llrintl libcalls
+; (RISC-V long double == fp128).
+define i32 @lrint_f128(fp128 %a) nounwind {
+; RV32IFD-LABEL: lrint_f128:
+; RV32IFD:       # %bb.0:
+; RV32IFD-NEXT:    addi sp, sp, -144
+; RV32IFD-NEXT:    sw ra, 140(sp) # 4-byte Folded Spill
+; RV32IFD-NEXT:    lw a1, 0(a0)
+; RV32IFD-NEXT:    lw a2, 4(a0)
+; RV32IFD-NEXT:    lw a3, 8(a0)
+; RV32IFD-NEXT:    lw a4, 12(a0)
+; RV32IFD-NEXT:    mv a0, sp
+; RV32IFD-NEXT:    sw a1, 0(sp)
+; RV32IFD-NEXT:    sw a2, 4(sp)
+; RV32IFD-NEXT:    sw a3, 8(sp)
+; RV32IFD-NEXT:    sw a4, 12(sp)
+; RV32IFD-NEXT:    call lrintl
+; RV32IFD-NEXT:    lw ra, 140(sp) # 4-byte Folded Reload
+; RV32IFD-NEXT:    addi sp, sp, 144
+; RV32IFD-NEXT:    ret
+;
+; RV64IFD-LABEL: lrint_f128:
+; RV64IFD:       # %bb.0:
+; RV64IFD-NEXT:    addi sp, sp, -16
+; RV64IFD-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64IFD-NEXT:    call lrintl
+; RV64IFD-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64IFD-NEXT:    addi sp, sp, 16
+; RV64IFD-NEXT:    ret
+;
+; RV32I-LABEL: lrint_f128:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -144
+; RV32I-NEXT:    sw ra, 140(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    lw a1, 0(a0)
+; RV32I-NEXT:    lw a2, 4(a0)
+; RV32I-NEXT:    lw a3, 8(a0)
+; RV32I-NEXT:    lw a4, 12(a0)
+; RV32I-NEXT:    mv a0, sp
+; RV32I-NEXT:    sw a1, 0(sp)
+; RV32I-NEXT:    sw a2, 4(sp)
+; RV32I-NEXT:    sw a3, 8(sp)
+; RV32I-NEXT:    sw a4, 12(sp)
+; RV32I-NEXT:    call lrintl
+; RV32I-NEXT:    lw ra, 140(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    addi sp, sp, 144
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: lrint_f128:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    call lrintl
+; RV64I-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    ret
+  %r = call i32 @llvm.lrint.f128(fp128 %a)
+  ret i32 %r
+}
+
+define i64 @llrint_f128(fp128 %a) nounwind {
+; RV32IFD-LABEL: llrint_f128:
+; RV32IFD:       # %bb.0:
+; RV32IFD-NEXT:    addi sp, sp, -144
+; RV32IFD-NEXT:    sw ra, 140(sp) # 4-byte Folded Spill
+; RV32IFD-NEXT:    lw a1, 0(a0)
+; RV32IFD-NEXT:    lw a2, 4(a0)
+; RV32IFD-NEXT:    lw a3, 8(a0)
+; RV32IFD-NEXT:    lw a4, 12(a0)
+; RV32IFD-NEXT:    mv a0, sp
+; RV32IFD-NEXT:    sw a1, 0(sp)
+; RV32IFD-NEXT:    sw a2, 4(sp)
+; RV32IFD-NEXT:    sw a3, 8(sp)
+; RV32IFD-NEXT:    sw a4, 12(sp)
+; RV32IFD-NEXT:    call llrintl
+; RV32IFD-NEXT:    lw ra, 140(sp) # 4-byte Folded Reload
+; RV32IFD-NEXT:    addi sp, sp, 144
+; RV32IFD-NEXT:    ret
+;
+; RV64IFD-LABEL: llrint_f128:
+; RV64IFD:       # %bb.0:
+; RV64IFD-NEXT:    addi sp, sp, -16
+; RV64IFD-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64IFD-NEXT:    call llrintl
+; RV64IFD-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64IFD-NEXT:    addi sp, sp, 16
+; RV64IFD-NEXT:    ret
+;
+; RV32I-LABEL: llrint_f128:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -144
+; RV32I-NEXT:    sw ra, 140(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    lw a1, 0(a0)
+; RV32I-NEXT:    lw a2, 4(a0)
+; RV32I-NEXT:    lw a3, 8(a0)
+; RV32I-NEXT:    lw a4, 12(a0)
+; RV32I-NEXT:    mv a0, sp
+; RV32I-NEXT:    sw a1, 0(sp)
+; RV32I-NEXT:    sw a2, 4(sp)
+; RV32I-NEXT:    sw a3, 8(sp)
+; RV32I-NEXT:    sw a4, 12(sp)
+; RV32I-NEXT:    call llrintl
+; RV32I-NEXT:    lw ra, 140(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    addi sp, sp, 144
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: llrint_f128:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    call llrintl
+; RV64I-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    ret
+  %r = call i64 @llvm.llrint.f128(fp128 %a)
+  ret i64 %r
+}
+
+declare i32 @llvm.lrint.f32(float)
+declare i32 @llvm.lrint.f64(double)
+declare i64 @llvm.llrint.f32(float)
+declare i64 @llvm.llrint.f64(double)
+declare i32 @llvm.lrint.f128(fp128)
+declare i64 @llvm.llrint.f128(fp128)
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK: {{.*}}


        


More information about the llvm-commits mailing list