[LegalizeTypes] Support softening FMINIMUM/FMAXIMUM (#115463)

Without this, you get an error "Do not know how to soften the result of
this operator!" when compiling for a soft float target.

The libcall names match those defined in glibc
<https://www.gnu.org/software/libc/manual/html_node/Misc-FP-Arithmetic.html>
and more recently added to LLVM's libc
<https://github.com/llvm/llvm-project/pull/86016>.
diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.def b/llvm/include/llvm/IR/RuntimeLibcalls.def
index 4aab658..9c56cf0 100644
--- a/llvm/include/llvm/IR/RuntimeLibcalls.def
+++ b/llvm/include/llvm/IR/RuntimeLibcalls.def
@@ -304,6 +304,16 @@
 HANDLE_LIBCALL(FMAX_F80, "fmaxl")
 HANDLE_LIBCALL(FMAX_F128, "fmaxl")
 HANDLE_LIBCALL(FMAX_PPCF128, "fmaxl")
+HANDLE_LIBCALL(FMINIMUM_F32, "fminimumf")
+HANDLE_LIBCALL(FMINIMUM_F64, "fminimum")
+HANDLE_LIBCALL(FMINIMUM_F80, "fminimuml")
+HANDLE_LIBCALL(FMINIMUM_F128, "fminmuml")
+HANDLE_LIBCALL(FMINIMUM_PPCF128, "fminimuml")
+HANDLE_LIBCALL(FMAXIMUM_F32, "fmaximumf")
+HANDLE_LIBCALL(FMAXIMUM_F64, "fmaximum")
+HANDLE_LIBCALL(FMAXIMUM_F80, "fmaximuml")
+HANDLE_LIBCALL(FMAXIMUM_F128, "fmaxmuml")
+HANDLE_LIBCALL(FMAXIMUM_PPCF128, "fmaximum_numl")
 HANDLE_LIBCALL(FMINIMUMNUM_F32, "fminimum_numf")
 HANDLE_LIBCALL(FMINIMUMNUM_F64, "fminimum_num")
 HANDLE_LIBCALL(FMINIMUMNUM_F80, "fminimum_numl")
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
index fa2731f..5abd7cb9 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
@@ -76,6 +76,8 @@
     case ISD::FMAXNUM:     R = SoftenFloatRes_FMAXNUM(N); break;
     case ISD::FMINIMUMNUM:    R = SoftenFloatRes_FMINIMUMNUM(N); break;
     case ISD::FMAXIMUMNUM:    R = SoftenFloatRes_FMAXIMUMNUM(N); break;
+    case ISD::FMINIMUM:    R = SoftenFloatRes_FMINIMUM(N); break;
+    case ISD::FMAXIMUM:    R = SoftenFloatRes_FMAXIMUM(N); break;
     case ISD::STRICT_FADD:
     case ISD::FADD:        R = SoftenFloatRes_FADD(N); break;
     case ISD::STRICT_FACOS:
@@ -342,6 +344,20 @@
                       RTLIB::FMAXIMUMNUM_F128, RTLIB::FMAXIMUMNUM_PPCF128));
 }
 
+SDValue DAGTypeLegalizer::SoftenFloatRes_FMINIMUM(SDNode *N) {
+  return SoftenFloatRes_Binary(
+      N, GetFPLibCall(N->getValueType(0), RTLIB::FMINIMUM_F32,
+                      RTLIB::FMINIMUM_F64, RTLIB::FMINIMUM_F80,
+                      RTLIB::FMINIMUM_F128, RTLIB::FMINIMUM_PPCF128));
+}
+
+SDValue DAGTypeLegalizer::SoftenFloatRes_FMAXIMUM(SDNode *N) {
+  return SoftenFloatRes_Binary(
+      N, GetFPLibCall(N->getValueType(0), RTLIB::FMAXIMUM_F32,
+                      RTLIB::FMAXIMUM_F64, RTLIB::FMAXIMUM_F80,
+                      RTLIB::FMAXIMUM_F128, RTLIB::FMAXIMUM_PPCF128));
+}
+
 SDValue DAGTypeLegalizer::SoftenFloatRes_FADD(SDNode *N) {
   return SoftenFloatRes_Binary(N, GetFPLibCall(N->getValueType(0),
                                                RTLIB::ADD_F32,
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
index 8d3458a..a56cd54 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
@@ -572,6 +572,8 @@
   SDValue SoftenFloatRes_FMAXNUM(SDNode *N);
   SDValue SoftenFloatRes_FMINIMUMNUM(SDNode *N);
   SDValue SoftenFloatRes_FMAXIMUMNUM(SDNode *N);
+  SDValue SoftenFloatRes_FMINIMUM(SDNode *N);
+  SDValue SoftenFloatRes_FMAXIMUM(SDNode *N);
   SDValue SoftenFloatRes_FADD(SDNode *N);
   SDValue SoftenFloatRes_FCBRT(SDNode *N);
   SDValue SoftenFloatRes_FCEIL(SDNode *N);
diff --git a/llvm/test/CodeGen/RISCV/float-maximum-minimum.ll b/llvm/test/CodeGen/RISCV/float-maximum-minimum.ll
index 0e00dff..2e9f8cb 100644
--- a/llvm/test/CodeGen/RISCV/float-maximum-minimum.ll
+++ b/llvm/test/CodeGen/RISCV/float-maximum-minimum.ll
@@ -8,6 +8,9 @@
 ; RUN: sed 's/iXLen/i32/g' %s | llc -mtriple=riscv32 -mattr=+d \
 ; RUN:   -verify-machineinstrs -target-abi=ilp32f \
 ; RUN:   | FileCheck -check-prefix=RV32IF %s
+; RUN: sed 's/iXLen/i32/g' %s | llc -mtriple=riscv32 \
+; RUN:   -verify-machineinstrs -target-abi=ilp32 \
+; RUN:   | FileCheck -check-prefix=RV32I %s
 ; RUN: sed 's/iXLen/i64/g' %s | llc -mtriple=riscv64 -mattr=+f \
 ; RUN:   -verify-machineinstrs -target-abi=lp64f \
 ; RUN:   | FileCheck -check-prefix=RV64IF %s
@@ -17,6 +20,9 @@
 ; RUN: sed 's/iXLen/i64/g' %s | llc -mtriple=riscv64 -mattr=+d \
 ; RUN:   -verify-machineinstrs -target-abi=lp64d \
 ; RUN:   | FileCheck -check-prefix=RV64IF %s
+; RUN: sed 's/iXLen/i64/g' %s | llc -mtriple=riscv64 \
+; RUN:   -verify-machineinstrs -target-abi=lp64 \
+; RUN:   | FileCheck -check-prefix=RV64I %s
 
 declare float @llvm.minimum.f32(float, float)
 
@@ -59,6 +65,15 @@
 ; RV32IZFINX-NEXT:    fmin.s a0, a1, a2
 ; RV32IZFINX-NEXT:    ret
 ;
+; RV32I-LABEL: fminimum_f32:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    call fminimumf
+; RV32I-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    ret
+;
 ; RV64IF-LABEL: fminimum_f32:
 ; RV64IF:       # %bb.0:
 ; RV64IF-NEXT:    feq.s a0, fa0, fa0
@@ -96,6 +111,15 @@
 ; RV64IZFINX-NEXT:  .LBB0_4:
 ; RV64IZFINX-NEXT:    fmin.s a0, a1, a2
 ; RV64IZFINX-NEXT:    ret
+;
+; RV64I-LABEL: fminimum_f32:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    call fminimumf
+; RV64I-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    ret
   %1 = call float @llvm.minimum.f32(float %a, float %b)
   ret float %1
 }
@@ -141,6 +165,15 @@
 ; RV32IZFINX-NEXT:    fmax.s a0, a1, a2
 ; RV32IZFINX-NEXT:    ret
 ;
+; RV32I-LABEL: fmaximum_f32:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    call fmaximumf
+; RV32I-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    ret
+;
 ; RV64IF-LABEL: fmaximum_f32:
 ; RV64IF:       # %bb.0:
 ; RV64IF-NEXT:    feq.s a0, fa0, fa0
@@ -178,6 +211,15 @@
 ; RV64IZFINX-NEXT:  .LBB1_4:
 ; RV64IZFINX-NEXT:    fmax.s a0, a1, a2
 ; RV64IZFINX-NEXT:    ret
+;
+; RV64I-LABEL: fmaximum_f32:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    call fmaximumf
+; RV64I-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    ret
   %1 = call float @llvm.maximum.f32(float %a, float %b)
   ret float %1
 }
@@ -193,6 +235,15 @@
 ; RV32IZFINX-NEXT:    fmin.s a0, a0, a1
 ; RV32IZFINX-NEXT:    ret
 ;
+; RV32I-LABEL: fminimum_nnan_f32:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    call fminimumf
+; RV32I-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    ret
+;
 ; RV64IF-LABEL: fminimum_nnan_f32:
 ; RV64IF:       # %bb.0:
 ; RV64IF-NEXT:    fmin.s fa0, fa0, fa1
@@ -202,6 +253,15 @@
 ; RV64IZFINX:       # %bb.0:
 ; RV64IZFINX-NEXT:    fmin.s a0, a0, a1
 ; RV64IZFINX-NEXT:    ret
+;
+; RV64I-LABEL: fminimum_nnan_f32:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    call fminimumf
+; RV64I-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    ret
   %1 = call nnan float @llvm.minimum.f32(float %a, float %b)
   ret float %1
 }
@@ -217,6 +277,15 @@
 ; RV32IZFINX-NEXT:    fmax.s a0, a0, a1
 ; RV32IZFINX-NEXT:    ret
 ;
+; RV32I-LABEL: fmaximum_nnan_f32:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    call fmaximumf
+; RV32I-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    ret
+;
 ; RV64IF-LABEL: fmaximum_nnan_f32:
 ; RV64IF:       # %bb.0:
 ; RV64IF-NEXT:    fmax.s fa0, fa0, fa1
@@ -226,6 +295,15 @@
 ; RV64IZFINX:       # %bb.0:
 ; RV64IZFINX-NEXT:    fmax.s a0, a0, a1
 ; RV64IZFINX-NEXT:    ret
+;
+; RV64I-LABEL: fmaximum_nnan_f32:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    call fmaximumf
+; RV64I-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    ret
   %1 = call nnan float @llvm.maximum.f32(float %a, float %b)
   ret float %1
 }
@@ -241,6 +319,15 @@
 ; RV32IZFINX-NEXT:    fmin.s a0, a0, a1
 ; RV32IZFINX-NEXT:    ret
 ;
+; RV32I-LABEL: fminimum_nnan_attr_f32:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    call fminimumf
+; RV32I-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    ret
+;
 ; RV64IF-LABEL: fminimum_nnan_attr_f32:
 ; RV64IF:       # %bb.0:
 ; RV64IF-NEXT:    fmin.s fa0, fa0, fa1
@@ -250,6 +337,15 @@
 ; RV64IZFINX:       # %bb.0:
 ; RV64IZFINX-NEXT:    fmin.s a0, a0, a1
 ; RV64IZFINX-NEXT:    ret
+;
+; RV64I-LABEL: fminimum_nnan_attr_f32:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    call fminimumf
+; RV64I-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    ret
   %1 = call float @llvm.minimum.f32(float %a, float %b)
   ret float %1
 }
@@ -279,6 +375,22 @@
 ; RV32IZFINX-NEXT:    fmin.s a0, a0, a1
 ; RV32IZFINX-NEXT:    ret
 ;
+; RV32I-LABEL: fminimum_nnan_op_f32:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    sw s0, 8(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    mv s0, a0
+; RV32I-NEXT:    mv a1, a0
+; RV32I-NEXT:    call __addsf3
+; RV32I-NEXT:    mv a1, a0
+; RV32I-NEXT:    mv a0, s0
+; RV32I-NEXT:    call fminimumf
+; RV32I-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    lw s0, 8(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    ret
+;
 ; RV64IF-LABEL: fminimum_nnan_op_f32:
 ; RV64IF:       # %bb.0:
 ; RV64IF-NEXT:    feq.s a0, fa0, fa0
@@ -302,6 +414,22 @@
 ; RV64IZFINX-NEXT:    fadd.s a1, a0, a0
 ; RV64IZFINX-NEXT:    fmin.s a0, a0, a1
 ; RV64IZFINX-NEXT:    ret
+;
+; RV64I-LABEL: fminimum_nnan_op_f32:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    sd s0, 0(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    mv s0, a0
+; RV64I-NEXT:    mv a1, a0
+; RV64I-NEXT:    call __addsf3
+; RV64I-NEXT:    mv a1, a0
+; RV64I-NEXT:    mv a0, s0
+; RV64I-NEXT:    call fminimumf
+; RV64I-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    ld s0, 0(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    ret
   %c = fadd nnan float %a, %a
   %1 = call float @llvm.minimum.f32(float %a, float %c)
   ret float %1
@@ -322,6 +450,30 @@
 ; RV32IZFINX-NEXT:    fmax.s a0, a2, a0
 ; RV32IZFINX-NEXT:    ret
 ;
+; RV32I-LABEL: fmaximum_nnan_op_f32:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    sw s0, 8(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    sw s1, 4(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    sw s2, 0(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    mv s0, a1
+; RV32I-NEXT:    mv s1, a0
+; RV32I-NEXT:    call __addsf3
+; RV32I-NEXT:    mv s2, a0
+; RV32I-NEXT:    mv a0, s1
+; RV32I-NEXT:    mv a1, s0
+; RV32I-NEXT:    call __subsf3
+; RV32I-NEXT:    mv a1, a0
+; RV32I-NEXT:    mv a0, s2
+; RV32I-NEXT:    call fmaximumf
+; RV32I-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    lw s0, 8(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    lw s1, 4(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    lw s2, 0(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    ret
+;
 ; RV64IF-LABEL: fmaximum_nnan_op_f32:
 ; RV64IF:       # %bb.0:
 ; RV64IF-NEXT:    fadd.s fa5, fa0, fa1
@@ -335,6 +487,30 @@
 ; RV64IZFINX-NEXT:    fsub.s a0, a0, a1
 ; RV64IZFINX-NEXT:    fmax.s a0, a2, a0
 ; RV64IZFINX-NEXT:    ret
+;
+; RV64I-LABEL: fmaximum_nnan_op_f32:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -32
+; RV64I-NEXT:    sd ra, 24(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    sd s0, 16(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    sd s1, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    sd s2, 0(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    mv s0, a1
+; RV64I-NEXT:    mv s1, a0
+; RV64I-NEXT:    call __addsf3
+; RV64I-NEXT:    mv s2, a0
+; RV64I-NEXT:    mv a0, s1
+; RV64I-NEXT:    mv a1, s0
+; RV64I-NEXT:    call __subsf3
+; RV64I-NEXT:    mv a1, a0
+; RV64I-NEXT:    mv a0, s2
+; RV64I-NEXT:    call fmaximumf
+; RV64I-NEXT:    ld ra, 24(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    ld s0, 16(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    ld s1, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    ld s2, 0(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    addi sp, sp, 32
+; RV64I-NEXT:    ret
   %c = fadd nnan float %a, %b
   %d = fsub nnan float %a, %b
   %1 = call float @llvm.maximum.f32(float %c, float %d)