[llvm] [WebAssembly] support `f{min, max}imum.f128` and `f{min, max}imumnum.f128` (PR #207160)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 2 06:31:26 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-webassembly
Author: Folkert de Vries (folkertdev)
<details>
<summary>Changes</summary>
fixes https://github.com/llvm/llvm-project/issues/207100
As far as I can see `wasi-libc` does not currently export the dedicated libcalls, so I went with a custom expansion. It emits more code, but will work without any linker errors etc.
The `long double` type is actually `f128` for webassembly, so at least the libcalls should work.
---
Full diff: https://github.com/llvm/llvm-project/pull/207160.diff
3 Files Affected:
- (modified) llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp (+32)
- (modified) llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp (+6)
- (added) llvm/test/CodeGen/WebAssembly/f128-minmax.ll (+154)
``````````diff
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index f06ab5bd0942b..e03a25cd5fef8 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -197,6 +197,12 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering(
{ISD::FMINNUM, ISD::FMINIMUMNUM, ISD::FMAXNUM, ISD::FMAXIMUMNUM},
{MVT::v4f32, MVT::v2f64}, Custom);
}
+
+ // Combine expands these operations, because wasi-libc does not yet have the
+ // dedicated libcalls.
+ setTargetDAGCombine(
+ {ISD::FMINIMUM, ISD::FMAXIMUM, ISD::FMINIMUMNUM, ISD::FMAXIMUMNUM});
+
// SIMD-specific configuration
if (Subtarget->hasSIMD128()) {
@@ -3980,6 +3986,27 @@ static SDValue performShiftCombine(SDNode *N,
return DAG.getNode(ISD::CONCAT_VECTORS, DL, VT, MulLo, MulHi);
}
+static SDValue performMinMaxF128Combine(SDNode *N, SelectionDAG &DAG) {
+ if (N->getValueType(0) != MVT::f128)
+ return SDValue();
+
+ const TargetLowering &TLI = DAG.getTargetLoweringInfo();
+ switch (N->getOpcode()) {
+ // wasi-libc does not currently define fminimuml and fmaximuml.
+ case ISD::FMINIMUM:
+ case ISD::FMAXIMUM:
+ return TLI.expandFMINIMUM_FMAXIMUM(N, DAG);
+
+ // wasi-libc does not currently define fminimum_numl and fmaximum_numl.
+ case ISD::FMINIMUMNUM:
+ case ISD::FMAXIMUMNUM:
+ return TLI.expandFMINIMUMNUM_FMAXIMUMNUM(N, DAG);
+
+ default:
+ return SDValue();
+ }
+}
+
SDValue
WebAssemblyTargetLowering::PerformDAGCombine(SDNode *N,
DAGCombinerInfo &DCI) const {
@@ -4020,5 +4047,10 @@ WebAssemblyTargetLowering::PerformDAGCombine(SDNode *N,
return performMulCombine(N, DCI);
case ISD::SHL:
return performShiftCombine(N, DCI);
+ case ISD::FMINIMUM:
+ case ISD::FMAXIMUM:
+ case ISD::FMINIMUMNUM:
+ case ISD::FMAXIMUMNUM:
+ return performMinMaxF128Combine(N, DCI.DAG);
}
}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp
index f3c236ca8c9ce..ab22f18d23f85 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp
@@ -275,6 +275,12 @@ struct RuntimeLibcallSignatureTable {
Table[RTLIB::FMAX_F32] = f32_func_f32_f32;
Table[RTLIB::FMAX_F64] = f64_func_f64_f64;
Table[RTLIB::FMAX_F128] = i64_i64_func_i64_i64_i64_i64;
+ // wasi-libc does not yet have these symbols. We currently expand them
+ // in performMinMaxF128Combine.
+ // Table[RTLIB::FMINIMUM_F128] = i64_i64_func_i64_i64_i64_i64;
+ // Table[RTLIB::FMAXIMUM_F128] = i64_i64_func_i64_i64_i64_i64;
+ // Table[RTLIB::FMINIMUM_NUM_F128] = i64_i64_func_i64_i64_i64_i64;
+ // Table[RTLIB::FMAXIMUM_NUM_F128] = i64_i64_func_i64_i64_i64_i64;
Table[RTLIB::LDEXP_F32] = f32_func_f32_i32;
Table[RTLIB::LDEXP_F64] = f64_func_f64_i32;
Table[RTLIB::LDEXP_F128] = i64_i64_func_i64_i64_i32;
diff --git a/llvm/test/CodeGen/WebAssembly/f128-minmax.ll b/llvm/test/CodeGen/WebAssembly/f128-minmax.ll
new file mode 100644
index 0000000000000..4b68af9f07081
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/f128-minmax.ll
@@ -0,0 +1,154 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s
+
+; These intrinsics are currently expanded, because wasi-libc does not have the specific libcalls.
+
+target triple = "wasm32-unknown-wasi"
+
+define fp128 @minimum_f128(fp128 %x, fp128 %y) {
+; CHECK-LABEL: minimum_f128:
+; CHECK: .functype minimum_f128 (i32, i64, i64, i64, i64) -> ()
+; CHECK-NEXT: # %bb.0:
+; CHECK-NEXT: i64.const $push3=, 9223231299366420480
+; CHECK-NEXT: call $push1=, __lttf2, $1, $2, $3, $4
+; CHECK-NEXT: i32.const $push0=, 0
+; CHECK-NEXT: i32.lt_s $push22=, $pop1, $pop0
+; CHECK-NEXT: local.tee $push21=, $5=, $pop22
+; CHECK-NEXT: i64.select $push2=, $2, $4, $pop21
+; CHECK-NEXT: call $push20=, __unordtf2, $1, $2, $3, $4
+; CHECK-NEXT: local.tee $push19=, $6=, $pop20
+; CHECK-NEXT: i64.select $push4=, $pop3, $pop2, $pop19
+; CHECK-NEXT: call $push5=, __trunctfsf2, $1, $2
+; CHECK-NEXT: i32.reinterpret_f32 $push6=, $pop5
+; CHECK-NEXT: i32.const $push7=, -2147483648
+; CHECK-NEXT: i32.eq $push18=, $pop6, $pop7
+; CHECK-NEXT: local.tee $push17=, $7=, $pop18
+; CHECK-NEXT: i64.select $push8=, $2, $4, $pop17
+; CHECK-NEXT: call $push16=, __eqtf2, $1, $2, $3, $4
+; CHECK-NEXT: local.tee $push15=, $8=, $pop16
+; CHECK-NEXT: i64.select $push9=, $pop4, $pop8, $pop15
+; CHECK-NEXT: i64.store 8($0), $pop9
+; CHECK-NEXT: i64.const $push11=, 0
+; CHECK-NEXT: i64.select $push10=, $1, $3, $5
+; CHECK-NEXT: i64.select $push12=, $pop11, $pop10, $6
+; CHECK-NEXT: i64.select $push13=, $1, $3, $7
+; CHECK-NEXT: i64.select $push14=, $pop12, $pop13, $8
+; CHECK-NEXT: i64.store 0($0), $pop14
+; CHECK-NEXT: return
+ %r = call fp128 @llvm.minimum.f128(fp128 %x, fp128 %y)
+ ret fp128 %r
+}
+
+define fp128 @maximum_f128(fp128 %x, fp128 %y) {
+; CHECK-LABEL: maximum_f128:
+; CHECK: .functype maximum_f128 (i32, i64, i64, i64, i64) -> ()
+; CHECK-NEXT: # %bb.0:
+; CHECK-NEXT: i64.const $push3=, 9223231299366420480
+; CHECK-NEXT: call $push1=, __gttf2, $1, $2, $3, $4
+; CHECK-NEXT: i32.const $push0=, 0
+; CHECK-NEXT: i32.gt_s $push20=, $pop1, $pop0
+; CHECK-NEXT: local.tee $push19=, $5=, $pop20
+; CHECK-NEXT: i64.select $push2=, $2, $4, $pop19
+; CHECK-NEXT: call $push18=, __unordtf2, $1, $2, $3, $4
+; CHECK-NEXT: local.tee $push17=, $6=, $pop18
+; CHECK-NEXT: i64.select $push4=, $pop3, $pop2, $pop17
+; CHECK-NEXT: call $push5=, __trunctfsf2, $1, $2
+; CHECK-NEXT: i32.reinterpret_f32 $push16=, $pop5
+; CHECK-NEXT: local.tee $push15=, $7=, $pop16
+; CHECK-NEXT: i64.select $push6=, $4, $2, $pop15
+; CHECK-NEXT: call $push14=, __eqtf2, $1, $2, $3, $4
+; CHECK-NEXT: local.tee $push13=, $8=, $pop14
+; CHECK-NEXT: i64.select $push7=, $pop4, $pop6, $pop13
+; CHECK-NEXT: i64.store 8($0), $pop7
+; CHECK-NEXT: i64.const $push9=, 0
+; CHECK-NEXT: i64.select $push8=, $1, $3, $5
+; CHECK-NEXT: i64.select $push10=, $pop9, $pop8, $6
+; CHECK-NEXT: i64.select $push11=, $3, $1, $7
+; CHECK-NEXT: i64.select $push12=, $pop10, $pop11, $8
+; CHECK-NEXT: i64.store 0($0), $pop12
+; CHECK-NEXT: return
+ %r = call fp128 @llvm.maximum.f128(fp128 %x, fp128 %y)
+ ret fp128 %r
+}
+
+define fp128 @minimumnum_f128(fp128 %x, fp128 %y) {
+; CHECK-LABEL: minimumnum_f128:
+; CHECK: .functype minimumnum_f128 (i32, i64, i64, i64, i64) -> ()
+; CHECK-NEXT: # %bb.0:
+; CHECK-NEXT: call $push28=, __unordtf2, $1, $2, $1, $2
+; CHECK-NEXT: local.tee $push27=, $5=, $pop28
+; CHECK-NEXT: i64.select $push26=, $4, $2, $pop27
+; CHECK-NEXT: local.tee $push25=, $2=, $pop26
+; CHECK-NEXT: call $push24=, __unordtf2, $3, $4, $3, $4
+; CHECK-NEXT: local.tee $push23=, $6=, $pop24
+; CHECK-NEXT: i64.select $7=, $pop25, $4, $pop23
+; CHECK-NEXT: i64.select $push22=, $3, $1, $5
+; CHECK-NEXT: local.tee $push21=, $4=, $pop22
+; CHECK-NEXT: i64.select $push20=, $4, $3, $6
+; CHECK-NEXT: local.tee $push19=, $1=, $pop20
+; CHECK-NEXT: call $push1=, __lttf2, $pop21, $2, $pop19, $7
+; CHECK-NEXT: i32.const $push0=, 0
+; CHECK-NEXT: i32.lt_s $push18=, $pop1, $pop0
+; CHECK-NEXT: local.tee $push17=, $5=, $pop18
+; CHECK-NEXT: i64.select $3=, $2, $7, $pop17
+; CHECK-NEXT: call $push2=, __trunctfsf2, $4, $2
+; CHECK-NEXT: i32.reinterpret_f32 $push3=, $pop2
+; CHECK-NEXT: i32.const $push4=, -2147483648
+; CHECK-NEXT: i32.eq $push16=, $pop3, $pop4
+; CHECK-NEXT: local.tee $push15=, $6=, $pop16
+; CHECK-NEXT: i64.select $push5=, $2, $3, $pop15
+; CHECK-NEXT: i64.select $push14=, $4, $1, $5
+; CHECK-NEXT: local.tee $push13=, $2=, $pop14
+; CHECK-NEXT: i64.const $push6=, 0
+; CHECK-NEXT: i64.const $push12=, 0
+; CHECK-NEXT: call $push11=, __eqtf2, $pop13, $3, $pop6, $pop12
+; CHECK-NEXT: local.tee $push10=, $5=, $pop11
+; CHECK-NEXT: i64.select $push7=, $3, $pop5, $pop10
+; CHECK-NEXT: i64.store 8($0), $pop7
+; CHECK-NEXT: i64.select $push8=, $4, $2, $6
+; CHECK-NEXT: i64.select $push9=, $2, $pop8, $5
+; CHECK-NEXT: i64.store 0($0), $pop9
+; CHECK-NEXT: return
+ %r = call fp128 @llvm.minimumnum.f128(fp128 %x, fp128 %y)
+ ret fp128 %r
+}
+
+define fp128 @maximumnum_f128(fp128 %x, fp128 %y) {
+; CHECK-LABEL: maximumnum_f128:
+; CHECK: .functype maximumnum_f128 (i32, i64, i64, i64, i64) -> ()
+; CHECK-NEXT: # %bb.0:
+; CHECK-NEXT: call $push26=, __unordtf2, $1, $2, $1, $2
+; CHECK-NEXT: local.tee $push25=, $5=, $pop26
+; CHECK-NEXT: i64.select $push24=, $4, $2, $pop25
+; CHECK-NEXT: local.tee $push23=, $2=, $pop24
+; CHECK-NEXT: call $push22=, __unordtf2, $3, $4, $3, $4
+; CHECK-NEXT: local.tee $push21=, $6=, $pop22
+; CHECK-NEXT: i64.select $7=, $pop23, $4, $pop21
+; CHECK-NEXT: i64.select $push20=, $3, $1, $5
+; CHECK-NEXT: local.tee $push19=, $4=, $pop20
+; CHECK-NEXT: i64.select $push18=, $4, $3, $6
+; CHECK-NEXT: local.tee $push17=, $1=, $pop18
+; CHECK-NEXT: call $push1=, __gttf2, $pop19, $2, $pop17, $7
+; CHECK-NEXT: i32.const $push0=, 0
+; CHECK-NEXT: i32.gt_s $push16=, $pop1, $pop0
+; CHECK-NEXT: local.tee $push15=, $5=, $pop16
+; CHECK-NEXT: i64.select $3=, $2, $7, $pop15
+; CHECK-NEXT: call $push2=, __trunctfsf2, $4, $2
+; CHECK-NEXT: i32.reinterpret_f32 $push14=, $pop2
+; CHECK-NEXT: local.tee $push13=, $6=, $pop14
+; CHECK-NEXT: i64.select $push3=, $3, $2, $pop13
+; CHECK-NEXT: i64.select $push12=, $4, $1, $5
+; CHECK-NEXT: local.tee $push11=, $2=, $pop12
+; CHECK-NEXT: i64.const $push4=, 0
+; CHECK-NEXT: i64.const $push10=, 0
+; CHECK-NEXT: call $push9=, __eqtf2, $pop11, $3, $pop4, $pop10
+; CHECK-NEXT: local.tee $push8=, $5=, $pop9
+; CHECK-NEXT: i64.select $push5=, $3, $pop3, $pop8
+; CHECK-NEXT: i64.store 8($0), $pop5
+; CHECK-NEXT: i64.select $push6=, $2, $4, $6
+; CHECK-NEXT: i64.select $push7=, $2, $pop6, $5
+; CHECK-NEXT: i64.store 0($0), $pop7
+; CHECK-NEXT: return
+ %r = call fp128 @llvm.maximumnum.f128(fp128 %x, fp128 %y)
+ ret fp128 %r
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/207160
More information about the llvm-commits
mailing list