[llvm] [SystemZ] Mark fminimumnum/fmaximumnum as legal (PR #184595)

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Wed Mar 4 03:51:01 PST 2026


https://github.com/nikic created https://github.com/llvm/llvm-project/pull/184595

Per my reading of the s390x PoO, in M=4 mode these instructions return the other operand for sNaN (rather than returning qNaN, as for M=0), which matches the semantics of fminimumnum/fmaximumnum.

I'd appreciate if an s390x expert can confirm that.

(I think this also means that the current lowering for strict_fminnum/strict_fmaxnum is incorrect and should use M=0 instead. Unlike the non-strict variants, these are required to have IEEE sNaN handling.)

>From 4ec0f3eb6bbb6a96841609e8931c4e914f1bb768 Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Wed, 4 Mar 2026 12:47:12 +0100
Subject: [PATCH] [SystemZ] Mark fminimumnum/fmaximumnum as legal

Per my reading of the s390x PoO, in M=4 mode these instructions
return the other operand for sNaN (rather than returning qNaN, as
for M=0), which matches the semantics of fminimumnum/fmaximumnum.
I'd appreciate if an s390x expert can confirm that.
---
 .../Target/SystemZ/SystemZISelLowering.cpp    |  32 +---
 llvm/lib/Target/SystemZ/SystemZInstrVector.td |   2 +
 .../SystemZ/fminimumnum-fmaximumnum.ll        | 164 ++++++++++++++++++
 3 files changed, 174 insertions(+), 24 deletions(-)
 create mode 100644 llvm/test/CodeGen/SystemZ/fminimumnum-fmaximumnum.ll

diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
index eacaaddc5e4d4..2a9cb903f3921 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
@@ -694,30 +694,14 @@ SystemZTargetLowering::SystemZTargetLowering(const TargetMachine &TM,
     setOperationAction(ISD::FROUND, MVT::v4f32, Legal);
     setOperationAction(ISD::FROUNDEVEN, MVT::v4f32, Legal);
 
-    setOperationAction(ISD::FMAXNUM, MVT::f64, Legal);
-    setOperationAction(ISD::FMAXIMUM, MVT::f64, Legal);
-    setOperationAction(ISD::FMINNUM, MVT::f64, Legal);
-    setOperationAction(ISD::FMINIMUM, MVT::f64, Legal);
-
-    setOperationAction(ISD::FMAXNUM, MVT::v2f64, Legal);
-    setOperationAction(ISD::FMAXIMUM, MVT::v2f64, Legal);
-    setOperationAction(ISD::FMINNUM, MVT::v2f64, Legal);
-    setOperationAction(ISD::FMINIMUM, MVT::v2f64, Legal);
-
-    setOperationAction(ISD::FMAXNUM, MVT::f32, Legal);
-    setOperationAction(ISD::FMAXIMUM, MVT::f32, Legal);
-    setOperationAction(ISD::FMINNUM, MVT::f32, Legal);
-    setOperationAction(ISD::FMINIMUM, MVT::f32, Legal);
-
-    setOperationAction(ISD::FMAXNUM, MVT::v4f32, Legal);
-    setOperationAction(ISD::FMAXIMUM, MVT::v4f32, Legal);
-    setOperationAction(ISD::FMINNUM, MVT::v4f32, Legal);
-    setOperationAction(ISD::FMINIMUM, MVT::v4f32, Legal);
-
-    setOperationAction(ISD::FMAXNUM, MVT::f128, Legal);
-    setOperationAction(ISD::FMAXIMUM, MVT::f128, Legal);
-    setOperationAction(ISD::FMINNUM, MVT::f128, Legal);
-    setOperationAction(ISD::FMINIMUM, MVT::f128, Legal);
+    for (MVT Type : {MVT::f64, MVT::v2f64, MVT::f32, MVT::v4f32, MVT::f128}) {
+      setOperationAction(ISD::FMAXNUM, Type, Legal);
+      setOperationAction(ISD::FMAXIMUM, Type, Legal);
+      setOperationAction(ISD::FMAXIMUMNUM, Type, Legal);
+      setOperationAction(ISD::FMINNUM, Type, Legal);
+      setOperationAction(ISD::FMINIMUM, Type, Legal);
+      setOperationAction(ISD::FMINIMUMNUM, Type, Legal);
+    }
 
     // Handle constrained floating-point operations.
     setOperationAction(ISD::STRICT_FADD, MVT::v4f32, Legal);
diff --git a/llvm/lib/Target/SystemZ/SystemZInstrVector.td b/llvm/lib/Target/SystemZ/SystemZInstrVector.td
index 0bbca28ca7be0..fe26bdc72799b 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrVector.td
+++ b/llvm/lib/Target/SystemZ/SystemZInstrVector.td
@@ -1599,6 +1599,7 @@ let Predicates = [FeatureVector] in {
   // Maximum.
   multiclass VectorMax<Instruction insn, TypedReg tr> {
     def : FPMinMax<insn, any_fmaxnum, tr, 4>;
+    def : FPMinMax<insn, fmaximumnum, tr, 4>;
     def : FPMinMax<insn, any_fmaximum, tr, 1>;
   }
   let Predicates = [FeatureVectorEnhancements1] in {
@@ -1625,6 +1626,7 @@ let Predicates = [FeatureVector] in {
   // Minimum.
   multiclass VectorMin<Instruction insn, TypedReg tr> {
     def : FPMinMax<insn, any_fminnum, tr, 4>;
+    def : FPMinMax<insn, fminimumnum, tr, 4>;
     def : FPMinMax<insn, any_fminimum, tr, 1>;
   }
   let Predicates = [FeatureVectorEnhancements1] in {
diff --git a/llvm/test/CodeGen/SystemZ/fminimumnum-fmaximumnum.ll b/llvm/test/CodeGen/SystemZ/fminimumnum-fmaximumnum.ll
new file mode 100644
index 0000000000000..b447e9119086f
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/fminimumnum-fmaximumnum.ll
@@ -0,0 +1,164 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=s390x-unknown-linux-gnu -mcpu=z14 < %s | FileCheck %s
+
+define half @fminimumnum_f16(half %a, half %b) nounwind {
+; CHECK-LABEL: fminimumnum_f16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    stmg %r14, %r15, 112(%r15)
+; CHECK-NEXT:    aghi %r15, -176
+; CHECK-NEXT:    std %f8, 168(%r15) # 8-byte Spill
+; CHECK-NEXT:    std %f9, 160(%r15) # 8-byte Spill
+; CHECK-NEXT:    ldr %f8, %f0
+; CHECK-NEXT:    ldr %f0, %f2
+; CHECK-NEXT:    brasl %r14, __extendhfsf2 at PLT
+; CHECK-NEXT:    ldr %f9, %f0
+; CHECK-NEXT:    ldr %f0, %f8
+; CHECK-NEXT:    brasl %r14, __extendhfsf2 at PLT
+; CHECK-NEXT:    wfminsb %f0, %f0, %f9, 4
+; CHECK-NEXT:    brasl %r14, __truncsfhf2 at PLT
+; CHECK-NEXT:    ld %f8, 168(%r15) # 8-byte Reload
+; CHECK-NEXT:    ld %f9, 160(%r15) # 8-byte Reload
+; CHECK-NEXT:    lmg %r14, %r15, 288(%r15)
+; CHECK-NEXT:    br %r14
+  %res = call half @llvm.minimumnum(half %a, half %b)
+  ret half %res
+}
+
+define float @fminimumnum_f32(float %a, float %b) {
+; CHECK-LABEL: fminimumnum_f32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    wfminsb %f0, %f0, %f2, 4
+; CHECK-NEXT:    br %r14
+  %res = call float @llvm.minimumnum(float %a, float %b)
+  ret float %res
+}
+
+define double @fminimumnum_f64(double %a, double %b) {
+; CHECK-LABEL: fminimumnum_f64:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    wfmindb %f0, %f0, %f2, 4
+; CHECK-NEXT:    br %r14
+  %res = call double @llvm.minimumnum(double %a, double %b)
+  ret double %res
+}
+
+define fp128 @fminimumnum_f128(fp128 %a, fp128 %b) {
+; CHECK-LABEL: fminimumnum_f128:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vl %v0, 0(%r4), 3
+; CHECK-NEXT:    vl %v1, 0(%r3), 3
+; CHECK-NEXT:    wfminxb %v0, %v1, %v0, 4
+; CHECK-NEXT:    vst %v0, 0(%r2), 3
+; CHECK-NEXT:    br %r14
+  %res = call fp128 @llvm.minimumnum(fp128 %a, fp128 %b)
+  ret fp128 %res
+}
+
+define <4 x float> @fminimumnum_v4f32(<4 x float> %a, <4 x float> %b) {
+; CHECK-LABEL: fminimumnum_v4f32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vfminsb %v24, %v24, %v26, 4
+; CHECK-NEXT:    br %r14
+  %res = call <4 x float> @llvm.minimumnum(<4 x float> %a, <4 x float> %b)
+  ret <4 x float> %res
+}
+
+define <2 x double> @fminimumnum_v2f64(<2 x double> %a, <2 x double> %b) {
+; CHECK-LABEL: fminimumnum_v2f64:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vfmindb %v24, %v24, %v26, 4
+; CHECK-NEXT:    br %r14
+  %res = call <2 x double> @llvm.minimumnum(<2 x double> %a, <2 x double> %b)
+  ret <2 x double> %res
+}
+
+define <4 x double> @fminimumnum_v4f64(<4 x double> %a, <4 x double> %b) {
+; CHECK-LABEL: fminimumnum_v4f64:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vfmindb %v24, %v24, %v28, 4
+; CHECK-NEXT:    vfmindb %v26, %v26, %v30, 4
+; CHECK-NEXT:    br %r14
+  %res = call <4 x double> @llvm.minimumnum(<4 x double> %a, <4 x double> %b)
+  ret <4 x double> %res
+}
+
+define half @fmaximumnum_f16(half %a, half %b) nounwind {
+; CHECK-LABEL: fmaximumnum_f16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    stmg %r14, %r15, 112(%r15)
+; CHECK-NEXT:    aghi %r15, -176
+; CHECK-NEXT:    std %f8, 168(%r15) # 8-byte Spill
+; CHECK-NEXT:    std %f9, 160(%r15) # 8-byte Spill
+; CHECK-NEXT:    ldr %f8, %f0
+; CHECK-NEXT:    ldr %f0, %f2
+; CHECK-NEXT:    brasl %r14, __extendhfsf2 at PLT
+; CHECK-NEXT:    ldr %f9, %f0
+; CHECK-NEXT:    ldr %f0, %f8
+; CHECK-NEXT:    brasl %r14, __extendhfsf2 at PLT
+; CHECK-NEXT:    wfmaxsb %f0, %f0, %f9, 4
+; CHECK-NEXT:    brasl %r14, __truncsfhf2 at PLT
+; CHECK-NEXT:    ld %f8, 168(%r15) # 8-byte Reload
+; CHECK-NEXT:    ld %f9, 160(%r15) # 8-byte Reload
+; CHECK-NEXT:    lmg %r14, %r15, 288(%r15)
+; CHECK-NEXT:    br %r14
+  %res = call half @llvm.maximumnum(half %a, half %b)
+  ret half %res
+}
+
+define float @fmaximumnum_f32(float %a, float %b) {
+; CHECK-LABEL: fmaximumnum_f32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    wfmaxsb %f0, %f0, %f2, 4
+; CHECK-NEXT:    br %r14
+  %res = call float @llvm.maximumnum(float %a, float %b)
+  ret float %res
+}
+
+define double @fmaximumnum_f64(double %a, double %b) {
+; CHECK-LABEL: fmaximumnum_f64:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    wfmaxdb %f0, %f0, %f2, 4
+; CHECK-NEXT:    br %r14
+  %res = call double @llvm.maximumnum(double %a, double %b)
+  ret double %res
+}
+
+define fp128 @fmaximumnum_f128(fp128 %a, fp128 %b) {
+; CHECK-LABEL: fmaximumnum_f128:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vl %v0, 0(%r4), 3
+; CHECK-NEXT:    vl %v1, 0(%r3), 3
+; CHECK-NEXT:    wfmaxxb %v0, %v1, %v0, 4
+; CHECK-NEXT:    vst %v0, 0(%r2), 3
+; CHECK-NEXT:    br %r14
+  %res = call fp128 @llvm.maximumnum(fp128 %a, fp128 %b)
+  ret fp128 %res
+}
+
+define <4 x float> @fmaximumnum_v4f32(<4 x float> %a, <4 x float> %b) {
+; CHECK-LABEL: fmaximumnum_v4f32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vfmaxsb %v24, %v24, %v26, 4
+; CHECK-NEXT:    br %r14
+  %res = call <4 x float> @llvm.maximumnum(<4 x float> %a, <4 x float> %b)
+  ret <4 x float> %res
+}
+
+define <2 x double> @fmaximumnum_v2f64(<2 x double> %a, <2 x double> %b) {
+; CHECK-LABEL: fmaximumnum_v2f64:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vfmaxdb %v24, %v24, %v26, 4
+; CHECK-NEXT:    br %r14
+  %res = call <2 x double> @llvm.maximumnum(<2 x double> %a, <2 x double> %b)
+  ret <2 x double> %res
+}
+
+define <4 x double> @fmaximumnum_v4f64(<4 x double> %a, <4 x double> %b) {
+; CHECK-LABEL: fmaximumnum_v4f64:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vfmaxdb %v24, %v24, %v28, 4
+; CHECK-NEXT:    vfmaxdb %v26, %v26, %v30, 4
+; CHECK-NEXT:    br %r14
+  %res = call <4 x double> @llvm.maximumnum(<4 x double> %a, <4 x double> %b)
+  ret <4 x double> %res
+}



More information about the llvm-commits mailing list