From 2fa2870d150d23c3cae46804d65088284f9cdd43 Mon Sep 17 00:00:00 2001 From: Alex Bradbury Date: Fri, 8 Nov 2024 11:14:38 +0000 Subject: [PATCH] [LegalizeTypes] Support softening FMINIMUM/FMAXIMUM 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 and more recently added to LLVM's libc . --- llvm/include/llvm/IR/RuntimeLibcalls.def | 10 + .../SelectionDAG/LegalizeFloatTypes.cpp | 16 ++ llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h | 2 + .../CodeGen/RISCV/float-maximum-minimum.ll | 176 ++++++++++++++++++ 4 files changed, 204 insertions(+) diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.def b/llvm/include/llvm/IR/RuntimeLibcalls.def index 4aab658a86690..9c56cf098ff77 100644 --- a/llvm/include/llvm/IR/RuntimeLibcalls.def +++ b/llvm/include/llvm/IR/RuntimeLibcalls.def @@ -304,6 +304,16 @@ HANDLE_LIBCALL(FMAX_F64, "fmax") 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 fa2731ff7dbda..5abd7cb97bda5 100644 --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp @@ -76,6 +76,8 @@ void DAGTypeLegalizer::SoftenFloatResult(SDNode *N, unsigned ResNo) { 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 @@ SDValue DAGTypeLegalizer::SoftenFloatRes_FMAXIMUMNUM(SDNode *N) { 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 8d3458aaab9f8..a56cd5423e00b 100644 --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h @@ -572,6 +572,8 @@ class LLVM_LIBRARY_VISIBILITY DAGTypeLegalizer { 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 0e00dff0b6424..2e9f8cbf6d2ef 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 @@ define float @fminimum_f32(float %a, float %b) nounwind { ; 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 @@ define float @fminimum_f32(float %a, float %b) nounwind { ; 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 @@ define float @fmaximum_f32(float %a, float %b) nounwind { ; 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 @@ define float @fmaximum_f32(float %a, float %b) nounwind { ; 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 @@ define float @fminimum_nnan_f32(float %a, float %b) nounwind { ; 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 @@ define float @fminimum_nnan_f32(float %a, float %b) nounwind { ; 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 @@ define float @fmaximum_nnan_f32(float %a, float %b) nounwind { ; 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 @@ define float @fmaximum_nnan_f32(float %a, float %b) nounwind { ; 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 @@ define float @fminimum_nnan_attr_f32(float %a, float %b) nounwind "no-nans-fp-ma ; 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 @@ define float @fminimum_nnan_attr_f32(float %a, float %b) nounwind "no-nans-fp-ma ; 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 @@ define float @fminimum_nnan_op_f32(float %a, float %b) nounwind { ; 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 @@ define float @fminimum_nnan_op_f32(float %a, float %b) nounwind { ; 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 @@ define float @fmaximum_nnan_op_f32(float %a, float %b) nounwind { ; 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 @@ define float @fmaximum_nnan_op_f32(float %a, float %b) nounwind { ; 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)