[llvm] [WebAssembly] Improve FP16 load and store generation. (PR #191274)
Brendan Dahl via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 9 13:25:05 PDT 2026
https://github.com/brendandahl updated https://github.com/llvm/llvm-project/pull/191274
>From f176d0671dc69914d2f43cfef90998153f147ef5 Mon Sep 17 00:00:00 2001
From: Brendan Dahl <brendan.dahl at gmail.com>
Date: Thu, 9 Apr 2026 18:49:26 +0000
Subject: [PATCH] [WebAssembly] Improve FP16 load and store generation.
Previously, these LL instructions were expanded to software emulation
calls, causing performance overhead in benchmarks. By making these
operations legal and providing patterns, we can generate efficient code
using the new instructions.
---
.../WebAssembly/WebAssemblyISelLowering.cpp | 9 ++++--
.../WebAssembly/WebAssemblyInstrMemory.td | 2 ++
.../WebAssembly/WebAssemblyInstrSIMD.td | 17 +++++++++++
.../CodeGen/WebAssembly/f16-intrinsics.ll | 29 +++++++++++++++++++
4 files changed, 55 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index efa6a61f82e9d..5a1ce364cc2b6 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -158,8 +158,13 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering(
setOperationAction(ISD::FP16_TO_FP, T, Expand);
setOperationAction(ISD::FP_TO_FP16, T, Expand);
}
- setLoadExtAction(ISD::EXTLOAD, T, MVT::f16, Expand);
- setTruncStoreAction(T, MVT::f16, Expand);
+ if (Subtarget->hasFP16() && T == MVT::f32) {
+ setLoadExtAction(ISD::EXTLOAD, T, MVT::f16, Legal);
+ setTruncStoreAction(T, MVT::f16, Legal);
+ } else {
+ setLoadExtAction(ISD::EXTLOAD, T, MVT::f16, Expand);
+ setTruncStoreAction(T, MVT::f16, Expand);
+ }
}
// Expand unavailable integer operations.
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td
index 0cbe9d0c6a6a4..d9b4b6fd8aaa9 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td
@@ -115,6 +115,7 @@ defm : LoadPat<i64, extloadi8, "LOAD8_U_I64">;
defm : LoadPat<i64, extloadi16, "LOAD16_U_I64">;
defm : LoadPat<i64, extloadi32, "LOAD32_U_I64">;
+defm : LoadPat<f32, extloadf16, "LOAD_F16_F32">;
defm : LoadPat<f32, int_wasm_loadf16_f32, "LOAD_F16_F32">;
// Defines atomic and non-atomic stores, regular and truncating
@@ -182,6 +183,7 @@ defm : StorePat<i64, truncstorei8, "STORE8_I64">;
defm : StorePat<i64, truncstorei16, "STORE16_I64">;
defm : StorePat<i64, truncstorei32, "STORE32_I64">;
+defm : StorePat<f32, truncstoref16, "STORE_F16_F32">;
defm : StorePat<f32, int_wasm_storef16_f32, "STORE_F16_F32">;
multiclass MemoryOps<WebAssemblyRegClass rc, string B> {
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td
index c929ad5486b05..66f8a3af33646 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td
@@ -225,6 +225,23 @@ foreach vec = StdVecs in {
inst>;
}
+// Match f16x8.splat of a loaded f16 value and fold it into v128.load16_splat.
+def loadf16_any : PatFrags<(ops node:$addr), [
+ (extloadf16 node:$addr),
+ (int_wasm_loadf16_f32 node:$addr)
+]>;
+
+multiclass LoadSplatFP16Pat<SDPatternOperator kind> {
+ def : Pat<(v8f16 (int_wasm_splat_f16x8 (kind (AddrOps32 offset32_op:$offset, I32:$addr)))),
+ (LOAD16_SPLAT_A32 0, offset32_op:$offset, I32:$addr)>,
+ Requires<[HasAddr32]>;
+ def : Pat<(v8f16 (int_wasm_splat_f16x8 (kind (AddrOps64 offset64_op:$offset, I64:$addr)))),
+ (LOAD16_SPLAT_A64 0, offset64_op:$offset, I64:$addr)>,
+ Requires<[HasAddr64]>;
+}
+
+defm : LoadSplatFP16Pat<loadf16_any>;
+
// Load and extend
multiclass SIMDLoadExtend<Vec vec, string loadPat, bits<32> simdop> {
defvar signed = vec.prefix#".load"#loadPat#"_s";
diff --git a/llvm/test/CodeGen/WebAssembly/f16-intrinsics.ll b/llvm/test/CodeGen/WebAssembly/f16-intrinsics.ll
index 13ac38cb1c375..bb28f462ebd0f 100644
--- a/llvm/test/CodeGen/WebAssembly/f16-intrinsics.ll
+++ b/llvm/test/CodeGen/WebAssembly/f16-intrinsics.ll
@@ -13,6 +13,16 @@ define float @ldf16_32(ptr %p) {
ret float %v
}
+; CHECK-LABEL: load_f16_cast:
+; CHECK: f32.load_f16 $push0=, 0($0)
+; CHECK-NEXT: return $pop0
+define float @load_f16_cast(ptr %p) {
+ %1 = load half, ptr %p, align 2
+ %2 = fpext half %1 to float
+ ret float %2
+}
+
+
; CHECK-LABEL: stf16_32:
; CHECK: f32.store_f16 0($1), $0
; CHECK-NEXT: return
@@ -29,6 +39,25 @@ define <8 x half> @splat_v8f16(float %x) {
ret <8 x half> %v
}
+; CHECK-LABEL: load_splat_v8f16:
+; CHECK: v128.load16_splat $push0=, 0($0)
+; CHECK-NEXT: return $pop0
+define <8 x half> @load_splat_v8f16(ptr %p) {
+ %1 = load half, ptr %p, align 2
+ %2 = fpext half %1 to float
+ %3 = call <8 x half> @llvm.wasm.splat.f16x8(float %2)
+ ret <8 x half> %3
+}
+
+; CHECK-LABEL: store_trunc_f16:
+; CHECK: f32.store_f16 0($1), $0
+; CHECK-NEXT: return
+define void @store_trunc_f16(float %v, ptr %p) {
+ %1 = fptrunc float %v to half
+ store half %1, ptr %p, align 2
+ ret void
+}
+
; CHECK-LABEL: const_splat_v8f16:
; CHECK: v128.const $push0=, 20800, 0, 0, 0, 0, 0, 0, 20800
; CHECK-NEXT: return $pop0
More information about the llvm-commits
mailing list