[llvm] [RISCV][GlobalISel] Support G_INTRINSIC_LRINT/G_INTRINSIC_LLRINT (PR #221992)
Kane Wang via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 8 06:11:14 PDT 2026
https://github.com/ReVe1uv created https://github.com/llvm/llvm-project/pull/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.
>From 3e9651207bf8ff2446d73b6b6ef0481f9bca79a6 Mon Sep 17 00:00:00 2001
From: Kane Wang <wangqiang1 at kylinos.cn>
Date: Tue, 8 Sep 2026 17:17:19 +0800
Subject: [PATCH] [RISCV][GlobalISel] Support
G_INTRINSIC_LRINT/G_INTRINSIC_LLRINT
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.
---
.../Target/RISCV/GISel/RISCVLegalizerInfo.cpp | 18 ++
.../RISCV/GISel/RISCVRegisterBankInfo.cpp | 4 +
.../GlobalISel/legalizer-info-validation.mir | 9 +-
.../RISCV/GlobalISel/lrint-llrint-f16.ll | 41 +++
.../CodeGen/RISCV/GlobalISel/lrint-llrint.ll | 268 ++++++++++++++++++
5 files changed, 336 insertions(+), 4 deletions(-)
create mode 100644 llvm/test/CodeGen/RISCV/GlobalISel/lrint-llrint-f16.ll
create mode 100644 llvm/test/CodeGen/RISCV/GlobalISel/lrint-llrint.ll
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
index d0ee9bb39cc8b..d4dad30f720fa 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
@@ -649,6 +649,24 @@ 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}})
+ .widenScalarIf(
+ [=, &ST](const LegalityQuery &Query) {
+ return ST.is64Bit() && Query.Types[0].isScalar() &&
+ Query.Types[0].getSizeInBits() == 32;
+ },
+ LegalizeMutations::changeTo(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