[llvm] [RISCV][GlobalISel] Fix fptosi/fptoui from half to i64 on RV32 (PR #222316)
Kane Wang via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 9 20:04:30 PDT 2026
https://github.com/ReVe1uv updated https://github.com/llvm/llvm-project/pull/222316
>From ff6664da43b64ea18c74c1002540fe8f2935f79b Mon Sep 17 00:00:00 2001
From: Kane Wang <wangqiang1 at kylinos.cn>
Date: Wed, 9 Sep 2026 20:46:05 +0800
Subject: [PATCH 1/2] [RISCV][GlobalISel] Fix fptosi/fptoui from half to i64 on
RV32
The fcvt.l[u].h patterns are RV64-only, so RV32 had no rule for
G_FPTOSI/G_FPTOUI with {s64, s16}. Fall back to the half-variant
conversion libcalls (__fixhfdi/__fixunshfdi), matching SelectionDAG.
---
.../Target/RISCV/GISel/RISCVLegalizerInfo.cpp | 3 +
.../CodeGen/RISCV/GlobalISel/half-convert.ll | 57 +++++++++++++++++++
2 files changed, 60 insertions(+)
create mode 100644 llvm/test/CodeGen/RISCV/GlobalISel/half-convert.ll
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
index d0ee9bb39cc8b..a8b60bfe9b926 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
@@ -631,6 +631,9 @@ RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &ST)
.widenScalarToNextPow2(0)
.minScalar(0, s32)
.libcallFor({{s32, s32}, {s64, s32}, {s32, s64}, {s64, s64}})
+ // The fcvt.l[u].h conversions are RV64-only, so RV32 falls back to the
+ // half-variant conversion libcalls.
+ .libcallFor({{s64, s16}})
.libcallFor(ST.is64Bit(), {{s32, s128}, {s64, s128}}) // FIXME RV32.
.libcallFor(ST.is64Bit(), {{s128, s32}, {s128, s64}, {s128, s128}});
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/half-convert.ll b/llvm/test/CodeGen/RISCV/GlobalISel/half-convert.ll
new file mode 100644
index 0000000000000..d7e18681e13c0
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/half-convert.ll
@@ -0,0 +1,57 @@
+; 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,RV32
+; RUN: llc -mtriple=riscv64 -global-isel -mattr=+f,+d,+zfh -target-abi=lp64d < %s | FileCheck %s --check-prefixes=CHECK,RV64
+
+define i64 @fptoui_i64_f16(half %x) nounwind {
+; RV32-LABEL: fptoui_i64_f16:
+; RV32: # %bb.0:
+; RV32-NEXT: addi sp, sp, -16
+; RV32-NEXT: sw ra, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT: call __fixunshfdi
+; RV32-NEXT: lw ra, 12(sp) # 4-byte Folded Reload
+; RV32-NEXT: addi sp, sp, 16
+; RV32-NEXT: ret
+;
+; RV64-LABEL: fptoui_i64_f16:
+; RV64: # %bb.0:
+; RV64-NEXT: fcvt.lu.h a0, fa0, rtz
+; RV64-NEXT: ret
+ %a = fptoui half %x to i64
+ ret i64 %a
+}
+
+define i64 @fptosi_i64_f16(half %x) nounwind {
+; RV32-LABEL: fptosi_i64_f16:
+; RV32: # %bb.0:
+; RV32-NEXT: addi sp, sp, -16
+; RV32-NEXT: sw ra, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT: call __fixhfdi
+; RV32-NEXT: lw ra, 12(sp) # 4-byte Folded Reload
+; RV32-NEXT: addi sp, sp, 16
+; RV32-NEXT: ret
+;
+; RV64-LABEL: fptosi_i64_f16:
+; RV64: # %bb.0:
+; RV64-NEXT: fcvt.l.h a0, fa0, rtz
+; RV64-NEXT: ret
+ %a = fptosi half %x to i64
+ ret i64 %a
+}
+
+define i32 @fptoui_i32_f16(half %x) nounwind {
+; CHECK-LABEL: fptoui_i32_f16:
+; CHECK: # %bb.0:
+; CHECK-NEXT: fcvt.wu.h a0, fa0, rtz
+; CHECK-NEXT: ret
+ %a = fptoui half %x to i32
+ ret i32 %a
+}
+
+define i32 @fptosi_i32_f16(half %x) nounwind {
+; CHECK-LABEL: fptosi_i32_f16:
+; CHECK: # %bb.0:
+; CHECK-NEXT: fcvt.w.h a0, fa0, rtz
+; CHECK-NEXT: ret
+ %a = fptosi half %x to i32
+ ret i32 %a
+}
>From 700f231a97897bc7ad2e1d2385b0fb26a25882a6 Mon Sep 17 00:00:00 2001
From: Kane Wang <wangqiang1 at kylinos.cn>
Date: Thu, 10 Sep 2026 10:59:54 +0800
Subject: [PATCH 2/2] [RISCV][GlobalISel] Use fcvt.w[u].h + extend for half to
i64 with Zfh
The magnitude of a half is at most 65504, so with Zfh the i32 result of
fcvt.w[u].h never overflows and can simply be extended to i64.
---
.../Target/RISCV/GISel/RISCVLegalizerInfo.cpp | 7 +-
.../CodeGen/RISCV/GlobalISel/half-convert.ll | 78 ++++++++++++-------
2 files changed, 52 insertions(+), 33 deletions(-)
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
index a8b60bfe9b926..09cbf455c5211 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
@@ -630,10 +630,11 @@ RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &ST)
.customFor(ST.is64Bit() && ST.hasStdExtZfh(), {{s32, s16}})
.widenScalarToNextPow2(0)
.minScalar(0, s32)
+ // The magnitude of a half is at most 65504, so with Zfh use fcvt.w[u].h
+ // and extend the i32 result. Without Zfh, use the libcalls.
+ .libcallFor(!ST.hasStdExtZfh(), {{s64, s16}})
+ .narrowScalarFor({{s64, s16}}, changeTo(0, s32))
.libcallFor({{s32, s32}, {s64, s32}, {s32, s64}, {s64, s64}})
- // The fcvt.l[u].h conversions are RV64-only, so RV32 falls back to the
- // half-variant conversion libcalls.
- .libcallFor({{s64, s16}})
.libcallFor(ST.is64Bit(), {{s32, s128}, {s64, s128}}) // FIXME RV32.
.libcallFor(ST.is64Bit(), {{s128, s32}, {s128, s64}, {s128, s128}});
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/half-convert.ll b/llvm/test/CodeGen/RISCV/GlobalISel/half-convert.ll
index d7e18681e13c0..3338be0aad09d 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/half-convert.ll
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/half-convert.ll
@@ -1,21 +1,42 @@
; 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,RV32
-; RUN: llc -mtriple=riscv64 -global-isel -mattr=+f,+d,+zfh -target-abi=lp64d < %s | FileCheck %s --check-prefixes=CHECK,RV64
+; RUN: llc -mtriple=riscv32 -global-isel -mattr=+f,+d,+zfh \
+; RUN: -target-abi=ilp32d < %s | FileCheck %s --check-prefixes=RV32
+; RUN: llc -mtriple=riscv64 -global-isel -mattr=+f,+d,+zfh \
+; RUN: -target-abi=lp64d < %s | FileCheck %s --check-prefixes=RV64
+; RUN: llc -mtriple=riscv32 -global-isel -mattr=+f,+d \
+; RUN: -target-abi=ilp32d < %s | FileCheck %s --check-prefixes=RV32NOZFH
+; RUN: llc -mtriple=riscv64 -global-isel -mattr=+f,+d \
+; RUN: -target-abi=lp64d < %s | FileCheck %s --check-prefixes=RV64NOZFH
define i64 @fptoui_i64_f16(half %x) nounwind {
; RV32-LABEL: fptoui_i64_f16:
; RV32: # %bb.0:
-; RV32-NEXT: addi sp, sp, -16
-; RV32-NEXT: sw ra, 12(sp) # 4-byte Folded Spill
-; RV32-NEXT: call __fixunshfdi
-; RV32-NEXT: lw ra, 12(sp) # 4-byte Folded Reload
-; RV32-NEXT: addi sp, sp, 16
+; RV32-NEXT: fcvt.wu.h a0, fa0, rtz
+; RV32-NEXT: li a1, 0
; RV32-NEXT: ret
;
; RV64-LABEL: fptoui_i64_f16:
; RV64: # %bb.0:
; RV64-NEXT: fcvt.lu.h a0, fa0, rtz
; RV64-NEXT: ret
+;
+; RV32NOZFH-LABEL: fptoui_i64_f16:
+; RV32NOZFH: # %bb.0:
+; RV32NOZFH-NEXT: addi sp, sp, -16
+; RV32NOZFH-NEXT: sw ra, 12(sp) # 4-byte Folded Spill
+; RV32NOZFH-NEXT: call __fixunshfdi
+; RV32NOZFH-NEXT: lw ra, 12(sp) # 4-byte Folded Reload
+; RV32NOZFH-NEXT: addi sp, sp, 16
+; RV32NOZFH-NEXT: ret
+;
+; RV64NOZFH-LABEL: fptoui_i64_f16:
+; RV64NOZFH: # %bb.0:
+; RV64NOZFH-NEXT: addi sp, sp, -16
+; RV64NOZFH-NEXT: sd ra, 8(sp) # 8-byte Folded Spill
+; RV64NOZFH-NEXT: call __fixunshfdi
+; RV64NOZFH-NEXT: ld ra, 8(sp) # 8-byte Folded Reload
+; RV64NOZFH-NEXT: addi sp, sp, 16
+; RV64NOZFH-NEXT: ret
%a = fptoui half %x to i64
ret i64 %a
}
@@ -23,35 +44,32 @@ define i64 @fptoui_i64_f16(half %x) nounwind {
define i64 @fptosi_i64_f16(half %x) nounwind {
; RV32-LABEL: fptosi_i64_f16:
; RV32: # %bb.0:
-; RV32-NEXT: addi sp, sp, -16
-; RV32-NEXT: sw ra, 12(sp) # 4-byte Folded Spill
-; RV32-NEXT: call __fixhfdi
-; RV32-NEXT: lw ra, 12(sp) # 4-byte Folded Reload
-; RV32-NEXT: addi sp, sp, 16
+; RV32-NEXT: fcvt.w.h a0, fa0, rtz
+; RV32-NEXT: srai a1, a0, 31
; RV32-NEXT: ret
;
; RV64-LABEL: fptosi_i64_f16:
; RV64: # %bb.0:
; RV64-NEXT: fcvt.l.h a0, fa0, rtz
; RV64-NEXT: ret
+;
+; RV32NOZFH-LABEL: fptosi_i64_f16:
+; RV32NOZFH: # %bb.0:
+; RV32NOZFH-NEXT: addi sp, sp, -16
+; RV32NOZFH-NEXT: sw ra, 12(sp) # 4-byte Folded Spill
+; RV32NOZFH-NEXT: call __fixhfdi
+; RV32NOZFH-NEXT: lw ra, 12(sp) # 4-byte Folded Reload
+; RV32NOZFH-NEXT: addi sp, sp, 16
+; RV32NOZFH-NEXT: ret
+;
+; RV64NOZFH-LABEL: fptosi_i64_f16:
+; RV64NOZFH: # %bb.0:
+; RV64NOZFH-NEXT: addi sp, sp, -16
+; RV64NOZFH-NEXT: sd ra, 8(sp) # 8-byte Folded Spill
+; RV64NOZFH-NEXT: call __fixhfdi
+; RV64NOZFH-NEXT: ld ra, 8(sp) # 8-byte Folded Reload
+; RV64NOZFH-NEXT: addi sp, sp, 16
+; RV64NOZFH-NEXT: ret
%a = fptosi half %x to i64
ret i64 %a
}
-
-define i32 @fptoui_i32_f16(half %x) nounwind {
-; CHECK-LABEL: fptoui_i32_f16:
-; CHECK: # %bb.0:
-; CHECK-NEXT: fcvt.wu.h a0, fa0, rtz
-; CHECK-NEXT: ret
- %a = fptoui half %x to i32
- ret i32 %a
-}
-
-define i32 @fptosi_i32_f16(half %x) nounwind {
-; CHECK-LABEL: fptosi_i32_f16:
-; CHECK: # %bb.0:
-; CHECK-NEXT: fcvt.w.h a0, fa0, rtz
-; CHECK-NEXT: ret
- %a = fptosi half %x to i32
- ret i32 %a
-}
More information about the llvm-commits
mailing list