[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