Index: clang/lib/Basic/Targets/SystemZ.h =================================================================== --- clang/lib/Basic/Targets/SystemZ.h +++ clang/lib/Basic/Targets/SystemZ.h @@ -29,11 +29,12 @@ int ISARevision; bool HasTransactionalExecution; bool HasVector; + bool SoftFloat; public: SystemZTargetInfo(const llvm::Triple &Triple, const TargetOptions &) : TargetInfo(Triple), CPU("z10"), ISARevision(8), - HasTransactionalExecution(false), HasVector(false) { + HasTransactionalExecution(false), HasVector(false), SoftFloat(false) { IntMaxType = SignedLong; Int64Type = SignedLong; TLSSupported = true; @@ -114,6 +115,8 @@ HasTransactionalExecution = true; else if (Feature == "+vector") HasVector = true; + else if (Feature == "+soft-float") + SoftFloat = true; } // If we use the vector ABI, vector types are 64-bit aligned. if (HasVector) { Index: clang/lib/Basic/Targets/SystemZ.cpp =================================================================== --- clang/lib/Basic/Targets/SystemZ.cpp +++ clang/lib/Basic/Targets/SystemZ.cpp @@ -122,6 +122,7 @@ .Case("arch13", ISARevision >= 13) .Case("htm", HasTransactionalExecution) .Case("vx", HasVector) + .Case("softfloat", SoftFloat) .Default(false); } @@ -145,6 +146,8 @@ Builder.defineMacro("__VX__"); if (Opts.ZVector) Builder.defineMacro("__VEC__", "10303"); + if (SoftFloat) + Builder.defineMacro("SOFT_FLOAT", "1"); } ArrayRef SystemZTargetInfo::getTargetBuiltins() const { Index: clang/lib/Driver/ToolChains/Arch/SystemZ.h =================================================================== --- clang/lib/Driver/ToolChains/Arch/SystemZ.h +++ clang/lib/Driver/ToolChains/Arch/SystemZ.h @@ -9,6 +9,7 @@ #ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_ARCH_SYSTEMZ_H #define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_ARCH_SYSTEMZ_H +#include "clang/Driver/Driver.h" #include "llvm/ADT/StringRef.h" #include "llvm/Option/Option.h" #include @@ -19,9 +20,17 @@ namespace tools { namespace systemz { +enum class FloatABI { + Invalid, + Soft, + Hard, +}; + +FloatABI getSystemZFloatABI(const Driver &D, const llvm::opt::ArgList &Args); + std::string getSystemZTargetCPU(const llvm::opt::ArgList &Args); -void getSystemZTargetFeatures(const llvm::opt::ArgList &Args, +void getSystemZTargetFeatures(const Driver &D, const llvm::opt::ArgList &Args, std::vector &Features); } // end namespace systemz Index: clang/lib/Driver/ToolChains/Arch/SystemZ.cpp =================================================================== --- clang/lib/Driver/ToolChains/Arch/SystemZ.cpp +++ clang/lib/Driver/ToolChains/Arch/SystemZ.cpp @@ -7,7 +7,9 @@ //===----------------------------------------------------------------------===// #include "SystemZ.h" +#include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/Options.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/Host.h" @@ -16,6 +18,36 @@ using namespace clang; using namespace llvm::opt; +systemz::FloatABI systemz::getSystemZFloatABI(const Driver &D, + const ArgList &Args) { + systemz::FloatABI ABI = systemz::FloatABI::Invalid; + if (Arg *A = Args.getLastArg(clang::driver::options::OPT_msoft_float, + options::OPT_mhard_float, + options::OPT_mfloat_abi_EQ)) { + if (A->getOption().matches(clang::driver::options::OPT_msoft_float)) + ABI = systemz::FloatABI::Soft; + else if (A->getOption().matches(options::OPT_mhard_float)) + ABI = systemz::FloatABI::Hard; + else { + ABI = llvm::StringSwitch(A->getValue()) + .Case("soft", systemz::FloatABI::Soft) + .Case("hard", systemz::FloatABI::Hard) + .Default(systemz::FloatABI::Invalid); + if (ABI == systemz::FloatABI::Invalid && + !StringRef(A->getValue()).empty()) { + D.Diag(clang::diag::err_drv_invalid_mfloat_abi) << A->getAsString(Args); + ABI = systemz::FloatABI::Hard; + } + } + } + + // If unspecified, hard-float is the default. + if (ABI == systemz::FloatABI::Invalid) + ABI = systemz::FloatABI::Hard; + + return ABI; +} + std::string systemz::getSystemZTargetCPU(const ArgList &Args) { if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ)) { llvm::StringRef CPUName = A->getValue(); @@ -33,7 +65,7 @@ return "z10"; } -void systemz::getSystemZTargetFeatures(const ArgList &Args, +void systemz::getSystemZTargetFeatures(const Driver &D, const ArgList &Args, std::vector &Features) { // -m(no-)htm overrides use of the transactional-execution facility. if (Arg *A = Args.getLastArg(options::OPT_mhtm, options::OPT_mno_htm)) { @@ -49,4 +81,8 @@ else Features.push_back("-vector"); } + + systemz::FloatABI FloatABI = systemz::getSystemZFloatABI(D, Args); + if (FloatABI == systemz::FloatABI::Soft) + Features.push_back("+soft-float"); } Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -339,7 +339,7 @@ riscv::getRISCVTargetFeatures(D, Triple, Args, Features); break; case llvm::Triple::systemz: - systemz::getSystemZTargetFeatures(Args, Features); + systemz::getSystemZTargetFeatures(D, Args, Features); break; case llvm::Triple::aarch64: case llvm::Triple::aarch64_32: @@ -2005,6 +2005,21 @@ CmdArgs.push_back("-mbackchain"); if (HasPackedStack) CmdArgs.push_back("-mpacked-stack"); + + systemz::FloatABI FloatABI = + systemz::getSystemZFloatABI(getToolChain().getDriver(), Args); + + if (FloatABI == systemz::FloatABI::Soft) { + // Floating point operations and argument passing are soft. + CmdArgs.push_back("-msoft-float"); + CmdArgs.push_back("-mfloat-abi"); + CmdArgs.push_back("soft"); + } else { + // Floating point operations and argument passing are hard. + assert(FloatABI == systemz::FloatABI::Hard && "Invalid float abi!"); + CmdArgs.push_back("-mfloat-abi"); + CmdArgs.push_back("hard"); + } } void Clang::AddX86TargetArgs(const ArgList &Args, Index: clang/test/CodeGen/function-target-features.c =================================================================== --- clang/test/CodeGen/function-target-features.c +++ clang/test/CodeGen/function-target-features.c @@ -12,6 +12,7 @@ // RUN: %clang_cc1 -triple sparc-unknown-unknown -emit-llvm -o - %s -target-feature +soft-float | FileCheck %s -check-prefix=SOFT-FLOAT // RUN: %clang_cc1 -triple arm-unknown-unknown -emit-llvm -o - %s -target-feature +soft-float | FileCheck %s -check-prefix=SOFT-FLOAT // RUN: %clang_cc1 -triple mips-unknown-unknown -emit-llvm -o - %s -target-feature +soft-float | FileCheck %s -check-prefix=SOFT-FLOAT +// RUN: %clang_cc1 -triple systemz-unknown-unknown -emit-llvm -o - %s -target-feature +soft-float | FileCheck %s -check-prefix=SOFT-FLOAT void foo() {} Index: clang/test/Driver/systemz-float.c =================================================================== --- /dev/null +++ clang/test/Driver/systemz-float.c @@ -0,0 +1,39 @@ +// Check handling -mhard-float / -msoft-float options +// when build for SystemZ platforms. +// +// Default +// RUN: %clang -c %s -### -o %t.o 2>&1 \ +// RUN: -target s390x-linux-gnu \ +// RUN: | FileCheck --check-prefix=CHECK-DEF %s +// CHECK-DEF-NOT: "-target-feature" "+soft-float" +// CHECK-DEF-NOT: "-msoft-float" +// +// -mhard-float +// RUN: %clang -c %s -### -o %t.o 2>&1 \ +// RUN: -target s390x-linux-gnu -mhard-float \ +// RUN: | FileCheck --check-prefix=CHECK-HARD %s +// CHECK-HARD-NOT: "-msoft-float" +// +// -msoft-float +// RUN: %clang -c %s -### -o %t.o 2>&1 \ +// RUN: -target s390x-linux-gnu -msoft-float \ +// RUN: | FileCheck --check-prefix=CHECK-SOFT %s +// CHECK-SOFT: "-target-feature" "+soft-float" +// +// -mfloat-abi=soft +// RUN: %clang -c %s -### -o %t.o 2>&1 \ +// RUN: -target s390x-linux-gnu -mfloat-abi=soft \ +// RUN: | FileCheck --check-prefix=CHECK-FLOATABISOFT %s +// CHECK-FLOATABISOFT: "-target-feature" "+soft-float" +// +// -mfloat-abi=hard +// RUN: %clang -c %s -### -o %t.o 2>&1 \ +// RUN: -target s390x-linux-gnu -mfloat-abi=hard \ +// RUN: | FileCheck --check-prefix=CHECK-FLOATABIHARD %s +// CHECK-FLOATABIHARD-NOT: "-target-feature" "+soft-float" +// +// check invalid -mfloat-abi +// RUN: %clang -c %s -### -o %t.o 2>&1 \ +// RUN: -target s390x-linux-gnu -mfloat-abi=x \ +// RUN: | FileCheck --check-prefix=CHECK-ERRMSG %s +// CHECK-ERRMSG: error: invalid float ABI '-mfloat-abi=x' Index: llvm/lib/Target/SystemZ/SystemZFeatures.td =================================================================== --- llvm/lib/Target/SystemZ/SystemZFeatures.td +++ llvm/lib/Target/SystemZ/SystemZFeatures.td @@ -25,6 +25,14 @@ class SystemZFeatureAdd x, list y> : SystemZFeatureList; + +// This feature is added as a subtarget feature whenever the function has the +// "use-soft-float" attribute set on the function. +def FeatureSoftFloat : SystemZFeature< + "soft-float", "SoftFloat", + "Use software emulation for floating point" +>; + //===----------------------------------------------------------------------===// // // New features added in the Ninth Edition of the z/Architecture Index: llvm/lib/Target/SystemZ/SystemZISelLowering.h =================================================================== --- llvm/lib/Target/SystemZ/SystemZISelLowering.h +++ llvm/lib/Target/SystemZ/SystemZISelLowering.h @@ -393,6 +393,8 @@ explicit SystemZTargetLowering(const TargetMachine &TM, const SystemZSubtarget &STI); + bool useSoftFloat() const override; + // Override TargetLowering. MVT getScalarShiftAmountTy(const DataLayout &, EVT) const override { return MVT::i32; Index: llvm/lib/Target/SystemZ/SystemZISelLowering.cpp =================================================================== --- llvm/lib/Target/SystemZ/SystemZISelLowering.cpp +++ llvm/lib/Target/SystemZ/SystemZISelLowering.cpp @@ -88,17 +88,19 @@ else addRegisterClass(MVT::i32, &SystemZ::GR32BitRegClass); addRegisterClass(MVT::i64, &SystemZ::GR64BitRegClass); - if (Subtarget.hasVector()) { - addRegisterClass(MVT::f32, &SystemZ::VR32BitRegClass); - addRegisterClass(MVT::f64, &SystemZ::VR64BitRegClass); - } else { - addRegisterClass(MVT::f32, &SystemZ::FP32BitRegClass); - addRegisterClass(MVT::f64, &SystemZ::FP64BitRegClass); + if (!Subtarget.hasSoftFloat()) { + if (Subtarget.hasVector()) { + addRegisterClass(MVT::f32, &SystemZ::VR32BitRegClass); + addRegisterClass(MVT::f64, &SystemZ::VR64BitRegClass); + } else { + addRegisterClass(MVT::f32, &SystemZ::FP32BitRegClass); + addRegisterClass(MVT::f64, &SystemZ::FP64BitRegClass); + } + if (Subtarget.hasVectorEnhancements1()) + addRegisterClass(MVT::f128, &SystemZ::VR128BitRegClass); + else + addRegisterClass(MVT::f128, &SystemZ::FP128BitRegClass); } - if (Subtarget.hasVectorEnhancements1()) - addRegisterClass(MVT::f128, &SystemZ::VR128BitRegClass); - else - addRegisterClass(MVT::f128, &SystemZ::FP128BitRegClass); if (Subtarget.hasVector()) { addRegisterClass(MVT::v16i8, &SystemZ::VR128BitRegClass); @@ -666,6 +668,10 @@ IsStrictFPEnabled = true; } +bool SystemZTargetLowering::useSoftFloat() const { + return Subtarget.hasSoftFloat(); +} + EVT SystemZTargetLowering::getSetCCResultType(const DataLayout &DL, LLVMContext &, EVT VT) const { if (!VT.isVector()) @@ -1443,7 +1449,7 @@ // Store the FPR varargs in the reserved frame slots. (We store the // GPRs as part of the prologue.) - if (NumFixedFPRs < SystemZ::NumArgFPRs) { + if (NumFixedFPRs < SystemZ::NumArgFPRs && !useSoftFloat()) { SDValue MemOps[SystemZ::NumArgFPRs]; for (unsigned I = NumFixedFPRs; I < SystemZ::NumArgFPRs; ++I) { unsigned Offset = TFL->getRegSpillOffset(SystemZ::ArgFPRs[I]); Index: llvm/lib/Target/SystemZ/SystemZSubtarget.h =================================================================== --- llvm/lib/Target/SystemZ/SystemZSubtarget.h +++ llvm/lib/Target/SystemZ/SystemZSubtarget.h @@ -68,6 +68,7 @@ bool HasVectorPackedDecimalEnhancement; bool HasEnhancedSort; bool HasDeflateConversion; + bool HasSoftFloat; private: Triple TargetTriple; @@ -239,6 +240,9 @@ // Return true if the target has the deflate-conversion facility. bool hasDeflateConversion() const { return HasDeflateConversion; } + // Return true if soft float should be used. + bool hasSoftFloat() const { return HasSoftFloat; } + // Return true if GV can be accessed using LARL for reloc model RM // and code model CM. bool isPC32DBLSymbol(const GlobalValue *GV, CodeModel::Model CM) const; Index: llvm/lib/Target/SystemZ/SystemZSubtarget.cpp =================================================================== --- llvm/lib/Target/SystemZ/SystemZSubtarget.cpp +++ llvm/lib/Target/SystemZ/SystemZSubtarget.cpp @@ -57,7 +57,7 @@ HasInsertReferenceBitsMultiple(false), HasMiscellaneousExtensions3(false), HasMessageSecurityAssist9(false), HasVectorEnhancements2(false), HasVectorPackedDecimalEnhancement(false), - HasEnhancedSort(false), HasDeflateConversion(false), + HasEnhancedSort(false), HasDeflateConversion(false), HasSoftFloat(false), TargetTriple(TT), InstrInfo(initializeSubtargetDependencies(CPU, FS)), TLInfo(TM, *this), TSInfo(), FrameLowering() {} Index: llvm/lib/Target/SystemZ/SystemZTargetMachine.h =================================================================== --- llvm/lib/Target/SystemZ/SystemZTargetMachine.h +++ llvm/lib/Target/SystemZ/SystemZTargetMachine.h @@ -27,6 +27,7 @@ class SystemZTargetMachine : public LLVMTargetMachine { std::unique_ptr TLOF; SystemZSubtarget Subtarget; + mutable StringMap> SubtargetMap; public: SystemZTargetMachine(const Target &T, const Triple &TT, StringRef CPU, @@ -36,10 +37,7 @@ ~SystemZTargetMachine() override; const SystemZSubtarget *getSubtargetImpl() const { return &Subtarget; } - - const SystemZSubtarget *getSubtargetImpl(const Function &) const override { - return &Subtarget; - } + const SystemZSubtarget *getSubtargetImpl(const Function &) const override; // Override LLVMTargetMachine TargetPassConfig *createPassConfig(PassManagerBase &PM) override; Index: llvm/lib/Target/SystemZ/SystemZTargetMachine.cpp =================================================================== --- llvm/lib/Target/SystemZ/SystemZTargetMachine.cpp +++ llvm/lib/Target/SystemZ/SystemZTargetMachine.cpp @@ -161,6 +161,40 @@ SystemZTargetMachine::~SystemZTargetMachine() = default; +const SystemZSubtarget * +SystemZTargetMachine::getSubtargetImpl(const Function &F) const { + Attribute CPUAttr = F.getFnAttribute("target-cpu"); + Attribute FSAttr = F.getFnAttribute("target-features"); + + std::string CPU = !CPUAttr.hasAttribute(Attribute::None) + ? CPUAttr.getValueAsString().str() + : TargetCPU; + std::string FS = !FSAttr.hasAttribute(Attribute::None) + ? FSAttr.getValueAsString().str() + : TargetFS; + + // FIXME: This is related to the code below to reset the target options, + // we need to know whether or not the soft float flag is set on the + // function, so we can enable it as a subtarget feature. + bool SoftFloat = + F.getFnAttribute("use-soft-float").getValueAsString() == "true"; + // If the soft float attribute is set on the function turn on the soft float + // subtarget feature. + if (SoftFloat) + FS += FS.empty() ? "+soft-float" : ",+soft-float"; + + auto &I = SubtargetMap[CPU + FS]; + if (!I) { + // This needs to be done before we create a new subtarget since any + // creation will depend on the TM and the code generation flags on the + // function that reside in TargetOptions. + resetTargetOptions(F); + I = std::make_unique(TargetTriple, CPU, FS, *this); + } + + return I.get(); +} + namespace { /// SystemZ Code Generator Pass Configuration Options. Index: llvm/test/CodeGen/SystemZ/soft-float-01.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/SystemZ/soft-float-01.ll @@ -0,0 +1,235 @@ +; RUN: llc -mcpu=z10 -mattr=soft-float -O0 < %s | FileCheck %s + +; Arithmetic functions + +define float @test_addsf3(float %a, float %b) #0 { + ; CHECK-LABEL: test_addsf3: + ; CHECK: brasl %r14, __addsf3 + %add = fadd float %a, %b + ret float %add +} + +define double @test_adddf3(double %a, double %b) #0 { + ; CHECK-LABEL: test_adddf3: + ; CHECK: brasl %r14, __adddf3 + %add = fadd double %a, %b + ret double %add +} + +define fp128 @test_addtf3(fp128 %a, fp128 %b) #0 { + ; CHECK-LABEL: test_addtf3: + ; CHECK: brasl %r14, __addtf3 + %add = fadd fp128 %a, %b + ret fp128 %add +} + +define float @test_mulsf3(float %a, float %b) #0 { + ; CHECK-LABEL: test_mulsf3: + ; CHECK: brasl %r14, __mulsf3 + %mul = fmul float %a, %b + ret float %mul +} + +define double @test_muldf3(double %a, double %b) #0 { + ; CHECK-LABEL: test_muldf3: + ; CHECK: brasl %r14, __muldf3 + %mul = fmul double %a, %b + ret double %mul +} + +define fp128 @test_multf3(fp128 %a, fp128 %b) #0 { + ; CHECK-LABEL: test_multf3: + ; CHECK: brasl %r14, __multf3 + %mul = fmul fp128 %a, %b + ret fp128 %mul +} + +define float @test_subsf3(float %a, float %b) #0 { + ; CHECK-LABEL: test_subsf3: + ; CHECK: brasl %r14, __subsf3 + %sub = fsub float %a, %b + ret float %sub +} + +define double @test_subdf3(double %a, double %b) #0 { + ; CHECK-LABEL: test_subdf3: + ; CHECK: brasl %r14, __subdf3 + %sub = fsub double %a, %b + ret double %sub +} + +define fp128 @test_subtf3(fp128 %a, fp128 %b) #0 { + ; CHECK-LABEL: test_subtf3: + ; CHECK: brasl %r14, __subtf3 + %sub = fsub fp128 %a, %b + ret fp128 %sub +} + +define float @test_divsf3(float %a, float %b) #0 { + ; CHECK-LABEL: test_divsf3: + ; CHECK: brasl %r14, __divsf3 + %div = fdiv float %a, %b + ret float %div +} + +define double @test_divdf3(double %a, double %b) #0 { + ; CHECK-LABEL: test_divdf3: + ; CHECK: brasl %r14, __divdf3 + %div = fdiv double %a, %b + ret double %div +} + +define fp128 @test_divtf3(fp128 %a, fp128 %b) #0 { + ; CHECK-LABEL: test_divtf3: + ; CHECK: brasl %r14, __divtf3 + %div = fdiv fp128 %a, %b + ret fp128 %div +} + +; Comparison functions +define i1 @test_unordsf2(float %a, float %b) #0 { + ; CHECK-LABEL: test_unordsf2: + ; CHECK: brasl %r14, __unordsf2 + %cmp = fcmp uno float %a, %b + ret i1 %cmp +} + +define i1 @test_unorddf2(double %a, double %b) #0 { + ; CHECK-LABEL: test_unorddf2: + ; CHECK: brasl %r14, __unorddf2 + %cmp = fcmp uno double %a, %b + ret i1 %cmp +} + +define i1 @test_unordtf2(fp128 %a, fp128 %b) #0 { + ; CHECK-LABEL: test_unordtf2: + ; CHECK: brasl %r14, __unordtf2 + %cmp = fcmp uno fp128 %a, %b + ret i1 %cmp +} + +define i1 @test_eqsf2(float %a, float %b) #0 { + ; CHECK-LABEL: test_eqsf2: + ; CHECK: brasl %r14, __eqsf2 + %cmp = fcmp oeq float %a, %b + ret i1 %cmp +} + +define i1 @test_eqdf2(double %a, double %b) #0 { + ; CHECK-LABEL: test_eqdf2: + ; CHECK: brasl %r14, __eqdf2 + %cmp = fcmp oeq double %a, %b + ret i1 %cmp +} + +define i1 @test_eqtf2(fp128 %a, fp128 %b) #0 { + ; CHECK-LABEL: test_eqtf2: + ; CHECK: brasl %r14, __eqtf2 + %cmp = fcmp oeq fp128 %a, %b + ret i1 %cmp +} + +define i1 @test_nesf2(float %a, float %b) #0 { + ; CHECK-LABEL: test_nesf2: + ; CHECK: brasl %r14, __nesf2 + %cmp = fcmp une float %a, %b + ret i1 %cmp +} + +define i1 @test_nedf2(double %a, double %b) #0 { + ; CHECK-LABEL: test_nedf2: + ; CHECK: brasl %r14, __nedf2 + %cmp = fcmp une double %a, %b + ret i1 %cmp +} + +define i1 @test_netf2(fp128 %a, fp128 %b) #0 { + ; CHECK-LABEL: test_netf2: + ; CHECK: brasl %r14, __netf2 + %cmp = fcmp une fp128 %a, %b + ret i1 %cmp +} + +define i1 @test_gesf2(float %a, float %b) #0 { + ; CHECK-LABEL: test_gesf2: + ; CHECK: brasl %r14, __gesf2 + %cmp = fcmp oge float %a, %b + ret i1 %cmp +} + +define i1 @test_gedf2(double %a, double %b) #0 { + ; CHECK-LABEL: test_gedf2: + ; CHECK: brasl %r14, __gedf2 + %cmp = fcmp oge double %a, %b + ret i1 %cmp +} + +define i1 @test_getf2(fp128 %a, fp128 %b) #0 { + ; CHECK-LABEL: test_getf2: + ; CHECK: brasl %r14, __getf2 + %cmp = fcmp oge fp128 %a, %b + ret i1 %cmp +} + +define i1 @test_ltsf2(float %a, float %b) #0 { + ; CHECK-LABEL: test_ltsf2: + ; CHECK: brasl %r14, __ltsf2 + %cmp = fcmp olt float %a, %b + ret i1 %cmp +} + +define i1 @test_ltdf2(double %a, double %b) #0 { + ; CHECK-LABEL: test_ltdf2: + ; CHECK: brasl %r14, __ltdf2 + %cmp = fcmp olt double %a, %b + ret i1 %cmp +} + +define i1 @test_lttf2(fp128 %a, fp128 %b) #0 { + ; CHECK-LABEL: test_lttf2: + ; CHECK: brasl %r14, __lttf2 + %cmp = fcmp olt fp128 %a, %b + ret i1 %cmp +} + +define i1 @test_lesf2(float %a, float %b) #0 { + ; CHECK-LABEL: test_lesf2: + ; CHECK: brasl %r14, __lesf2 + %cmp = fcmp ole float %a, %b + ret i1 %cmp +} + +define i1 @test_ledf2(double %a, double %b) #0 { + ; CHECK-LABEL: test_ledf2: + ; CHECK: brasl %r14, __ledf2 + %cmp = fcmp ole double %a, %b + ret i1 %cmp +} + +define i1 @test_letf2(fp128 %a, fp128 %b) #0 { + ; CHECK-LABEL: test_letf2: + ; CHECK: brasl %r14, __letf2 + %cmp = fcmp ole fp128 %a, %b + ret i1 %cmp +} + +define i1 @test_gtsf2(float %a, float %b) #0 { + ; CHECK-LABEL: test_gtsf2: + ; CHECK: brasl %r14, __gtsf2 + %cmp = fcmp ogt float %a, %b + ret i1 %cmp +} + +define i1 @test_gtdf2(double %a, double %b) #0 { + ; CHECK-LABEL: test_gtdf2: + ; CHECK: brasl %r14, __gtdf2 + %cmp = fcmp ogt double %a, %b + ret i1 %cmp +} + +define i1 @test_gttf2(fp128 %a, fp128 %b) #0 { + ; CHECK-LABEL: test_gttf2: + ; CHECK: brasl %r14, __gttf2 + %cmp = fcmp ogt fp128 %a, %b + ret i1 %cmp +} Index: llvm/test/CodeGen/SystemZ/soft-float-02.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/SystemZ/soft-float-02.ll @@ -0,0 +1,24 @@ +; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s +; +; Check that FP registers are saved in a vararg function only if soft-float +; is not used. + +define void @fun0(...) { +; CHECK-LABEL: fun0 +; CHECK: std %f0 +; CHECK: std %f2 +; CHECK: std %f4 +; CHECK: std %f6 + ret void +} + +define void @fun1(...) #0 { +; CHECK-LABEL: fun1 +; CHECK-NOT: std %f0 +; CHECK-NOT: std %f2 +; CHECK-NOT: std %f4 +; CHECK-NOT: std %f6 + ret void +} + +attributes #0 = { "use-soft-float"="true" }