diff --git a/clang/lib/Basic/Targets/SystemZ.h b/clang/lib/Basic/Targets/SystemZ.h --- a/clang/lib/Basic/Targets/SystemZ.h +++ b/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; @@ -109,12 +110,17 @@ DiagnosticsEngine &Diags) override { HasTransactionalExecution = false; HasVector = false; + SoftFloat = false; for (const auto &Feature : Features) { if (Feature == "+transactional-execution") HasTransactionalExecution = true; else if (Feature == "+vector") HasVector = true; + else if (Feature == "+soft-float") + SoftFloat = true; } + HasVector &= !SoftFloat; + // If we use the vector ABI, vector types are 64-bit aligned. if (HasVector) { MaxVectorAlign = 64; diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -6565,10 +6565,11 @@ class SystemZABIInfo : public SwiftABIInfo { bool HasVector; + bool IsSoftFloatABI; public: - SystemZABIInfo(CodeGenTypes &CGT, bool HV) - : SwiftABIInfo(CGT), HasVector(HV) {} + SystemZABIInfo(CodeGenTypes &CGT, bool HV, bool SF) + : SwiftABIInfo(CGT), HasVector(HV), IsSoftFloatABI(SF) {} bool isPromotableIntegerType(QualType Ty) const; bool isCompoundType(QualType Ty) const; @@ -6600,8 +6601,8 @@ class SystemZTargetCodeGenInfo : public TargetCodeGenInfo { public: - SystemZTargetCodeGenInfo(CodeGenTypes &CGT, bool HasVector) - : TargetCodeGenInfo(new SystemZABIInfo(CGT, HasVector)) {} + SystemZTargetCodeGenInfo(CodeGenTypes &CGT, bool HasVector, bool SoftFloatABI) + : TargetCodeGenInfo(new SystemZABIInfo(CGT, HasVector, SoftFloatABI)) {} }; } @@ -6640,6 +6641,9 @@ } bool SystemZABIInfo::isFPArgumentType(QualType Ty) const { + if (IsSoftFloatABI) + return false; + if (const BuiltinType *BT = Ty->getAs()) switch (BT->getKind()) { case BuiltinType::Float: @@ -6725,7 +6729,7 @@ } else { if (AI.getCoerceToType()) ArgTy = AI.getCoerceToType(); - InFPRs = ArgTy->isFloatTy() || ArgTy->isDoubleTy(); + InFPRs = (!IsSoftFloatABI && (ArgTy->isFloatTy() || ArgTy->isDoubleTy())); IsVector = ArgTy->isVectorTy(); UnpaddedSize = TyInfo.first; DirectAlign = TyInfo.second; @@ -9900,8 +9904,9 @@ } case llvm::Triple::systemz: { - bool HasVector = getTarget().getABI() == "vector"; - return SetCGInfo(new SystemZTargetCodeGenInfo(Types, HasVector)); + bool SoftFloat = CodeGenOpts.FloatABI == "soft"; + bool HasVector = !SoftFloat && getTarget().getABI() == "vector"; + return SetCGInfo(new SystemZTargetCodeGenInfo(Types, HasVector, SoftFloat)); } case llvm::Triple::tce: diff --git a/clang/lib/Driver/ToolChains/Arch/SystemZ.h b/clang/lib/Driver/ToolChains/Arch/SystemZ.h --- a/clang/lib/Driver/ToolChains/Arch/SystemZ.h +++ b/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,16 @@ namespace tools { namespace systemz { +enum class FloatABI { + 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 diff --git a/clang/lib/Driver/ToolChains/Arch/SystemZ.cpp b/clang/lib/Driver/ToolChains/Arch/SystemZ.cpp --- a/clang/lib/Driver/ToolChains/Arch/SystemZ.cpp +++ b/clang/lib/Driver/ToolChains/Arch/SystemZ.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "SystemZ.h" +#include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/Options.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/Host.h" @@ -16,6 +17,22 @@ using namespace clang; using namespace llvm::opt; +systemz::FloatABI systemz::getSystemZFloatABI(const Driver &D, + const ArgList &Args) { + // Hard float is the default. + systemz::FloatABI ABI = systemz::FloatABI::Hard; + if (Args.hasArg(options::OPT_mfloat_abi_EQ)) + D.Diag(diag::err_drv_unsupported_opt) + << Args.getLastArg(options::OPT_mfloat_abi_EQ)->getAsString(Args); + + if (Arg *A = Args.getLastArg(clang::driver::options::OPT_msoft_float, + options::OPT_mhard_float)) + if (A->getOption().matches(clang::driver::options::OPT_msoft_float)) + ABI = systemz::FloatABI::Soft; + + 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 +50,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 +66,8 @@ else Features.push_back("-vector"); } + + systemz::FloatABI FloatABI = systemz::getSystemZFloatABI(D, Args); + if (FloatABI == systemz::FloatABI::Soft) + Features.push_back("+soft-float"); } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/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: @@ -2013,6 +2013,16 @@ 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"); + } } static void addX86AlignBranchArgs(const Driver &D, const ArgList &Args, diff --git a/clang/test/CodeGen/systemz-abi.c b/clang/test/CodeGen/systemz-abi.c --- a/clang/test/CodeGen/systemz-abi.c +++ b/clang/test/CodeGen/systemz-abi.c @@ -1,19 +1,22 @@ // RUN: %clang_cc1 -triple s390x-linux-gnu \ -// RUN: -emit-llvm -o - %s | FileCheck %s +// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,HARD-FLOAT // RUN: %clang_cc1 -triple s390x-linux-gnu -target-feature +vector \ -// RUN: -emit-llvm -o - %s | FileCheck %s +// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,HARD-FLOAT // RUN: %clang_cc1 -triple s390x-linux-gnu -target-cpu z13 \ -// RUN: -emit-llvm -o - %s | FileCheck %s +// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,HARD-FLOAT // RUN: %clang_cc1 -triple s390x-linux-gnu -target-cpu arch11 \ -// RUN: -emit-llvm -o - %s | FileCheck %s +// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,HARD-FLOAT // RUN: %clang_cc1 -triple s390x-linux-gnu -target-cpu z14 \ -// RUN: -emit-llvm -o - %s | FileCheck %s +// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,HARD-FLOAT // RUN: %clang_cc1 -triple s390x-linux-gnu -target-cpu arch12 \ -// RUN: -emit-llvm -o - %s | FileCheck %s +// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,HARD-FLOAT // RUN: %clang_cc1 -triple s390x-linux-gnu -target-cpu z15 \ -// RUN: -emit-llvm -o - %s | FileCheck %s +// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,HARD-FLOAT // RUN: %clang_cc1 -triple s390x-linux-gnu -target-cpu arch13 \ -// RUN: -emit-llvm -o - %s | FileCheck %s +// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,HARD-FLOAT +// RUN: %clang_cc1 -triple s390x-linux-gnu -target-cpu arch13 \ +// RUN: -emit-llvm -o - %s -mfloat-abi soft | FileCheck %s \ +// RUN: --check-prefixes=CHECK,SOFT-FLOAT // Scalar types @@ -115,11 +118,13 @@ struct agg_float { float a; }; struct agg_float pass_agg_float(struct agg_float arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_float(%struct.agg_float* noalias sret %{{.*}}, float %{{.*}}) +// HARD-FLOAT-LABEL: define void @pass_agg_float(%struct.agg_float* noalias sret %{{.*}}, float %{{.*}}) +// SOFT-FLOAT-LABEL: define void @pass_agg_float(%struct.agg_float* noalias sret %{{.*}}, i32 %{{.*}}) struct agg_double { double a; }; struct agg_double pass_agg_double(struct agg_double arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_double(%struct.agg_double* noalias sret %{{.*}}, double %{{.*}}) +// HARD-FLOAT-LABEL: define void @pass_agg_double(%struct.agg_double* noalias sret %{{.*}}, double %{{.*}}) +// SOFT-FLOAT-LABEL: define void @pass_agg_double(%struct.agg_double* noalias sret %{{.*}}, i64 %{{.*}}) struct agg_longdouble { long double a; }; struct agg_longdouble pass_agg_longdouble(struct agg_longdouble arg) { return arg; } @@ -127,7 +132,8 @@ struct agg_float_a8 { float a __attribute__((aligned (8))); }; struct agg_float_a8 pass_agg_float_a8(struct agg_float_a8 arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_float_a8(%struct.agg_float_a8* noalias sret %{{.*}}, double %{{.*}}) +// HARD-FLOAT-LABEL: define void @pass_agg_float_a8(%struct.agg_float_a8* noalias sret %{{.*}}, double %{{.*}}) +// SOFT-FLOAT-LABEL: define void @pass_agg_float_a8(%struct.agg_float_a8* noalias sret %{{.*}}, i64 %{{.*}}) struct agg_float_a16 { float a __attribute__((aligned (16))); }; struct agg_float_a16 pass_agg_float_a16(struct agg_float_a16 arg) { return arg; } @@ -225,12 +231,15 @@ double va_double(__builtin_va_list l) { return __builtin_va_arg(l, double); } // CHECK-LABEL: define double @va_double(%struct.__va_list_tag* %{{.*}}) -// CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 1 +// HARD-FLOAT: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 1 +// SOFT-FLOAT: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] -// CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 4 +// HARD-FLOAT: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 4 +// SOFT-FLOAT: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 // CHECK: br i1 [[FITS_IN_REGS]], // CHECK: [[SCALED_REG_COUNT:%[^ ]+]] = mul i64 [[REG_COUNT]], 8 -// CHECK: [[REG_OFFSET:%[^ ]+]] = add i64 [[SCALED_REG_COUNT]], 128 +// HARD-FLOAT: [[REG_OFFSET:%[^ ]+]] = add i64 [[SCALED_REG_COUNT]], 128 +// SOFT-FLOAT: [[REG_OFFSET:%[^ ]+]] = add i64 [[SCALED_REG_COUNT]], 16 // CHECK: [[REG_SAVE_AREA_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 3 // CHECK: [[REG_SAVE_AREA:%[^ ]+]] = load i8*, i8** [[REG_SAVE_AREA_PTR:[^ ]+]] // CHECK: [[RAW_REG_ADDR:%[^ ]+]] = getelementptr i8, i8* [[REG_SAVE_AREA]], i64 [[REG_OFFSET]] @@ -415,12 +424,15 @@ struct agg_float va_agg_float(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_float); } // CHECK-LABEL: define void @va_agg_float(%struct.agg_float* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}} -// CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 1 +// HARD-FLOAT: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 1 +// SOFT-FLOAT: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] -// CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 4 +// HARD-FLOAT: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 4 +// SOFT-FLOAT: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 // CHECK: br i1 [[FITS_IN_REGS]], // CHECK: [[SCALED_REG_COUNT:%[^ ]+]] = mul i64 [[REG_COUNT]], 8 -// CHECK: [[REG_OFFSET:%[^ ]+]] = add i64 [[SCALED_REG_COUNT]], 128 +// HARD-FLOAT: [[REG_OFFSET:%[^ ]+]] = add i64 [[SCALED_REG_COUNT]], 128 +// SOFT-FLOAT: [[REG_OFFSET:%[^ ]+]] = add i64 [[SCALED_REG_COUNT]], 20 // CHECK: [[REG_SAVE_AREA_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 3 // CHECK: [[REG_SAVE_AREA:%[^ ]+]] = load i8*, i8** [[REG_SAVE_AREA_PTR:[^ ]+]] // CHECK: [[RAW_REG_ADDR:%[^ ]+]] = getelementptr i8, i8* [[REG_SAVE_AREA]], i64 [[REG_OFFSET]] @@ -438,12 +450,15 @@ struct agg_double va_agg_double(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_double); } // CHECK-LABEL: define void @va_agg_double(%struct.agg_double* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}} -// CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 1 +// HARD-FLOAT: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 1 +// SOFT-FLOAT: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] -// CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 4 +// HARD-FLOAT: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 4 +// SOFT-FLOAT: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 // CHECK: br i1 [[FITS_IN_REGS]], // CHECK: [[SCALED_REG_COUNT:%[^ ]+]] = mul i64 [[REG_COUNT]], 8 -// CHECK: [[REG_OFFSET:%[^ ]+]] = add i64 [[SCALED_REG_COUNT]], 128 +// HARD-FLOAT: [[REG_OFFSET:%[^ ]+]] = add i64 [[SCALED_REG_COUNT]], 128 +// SOFT-FLOAT: [[REG_OFFSET:%[^ ]+]] = add i64 [[SCALED_REG_COUNT]], 16 // CHECK: [[REG_SAVE_AREA_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 3 // CHECK: [[REG_SAVE_AREA:%[^ ]+]] = load i8*, i8** [[REG_SAVE_AREA_PTR:[^ ]+]] // CHECK: [[RAW_REG_ADDR:%[^ ]+]] = getelementptr i8, i8* [[REG_SAVE_AREA]], i64 [[REG_OFFSET]] @@ -485,12 +500,15 @@ struct agg_float_a8 va_agg_float_a8(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_float_a8); } // CHECK-LABEL: define void @va_agg_float_a8(%struct.agg_float_a8* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}} -// CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 1 +// HARD-FLOAT: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 1 +// SOFT-FLOAT: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] -// CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 4 +// HARD-FLOAT: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 4 +// SOFT-FLOAT: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 // CHECK: br i1 [[FITS_IN_REGS]], // CHECK: [[SCALED_REG_COUNT:%[^ ]+]] = mul i64 [[REG_COUNT]], 8 -// CHECK: [[REG_OFFSET:%[^ ]+]] = add i64 [[SCALED_REG_COUNT]], 128 +// HARD-FLOAT: [[REG_OFFSET:%[^ ]+]] = add i64 [[SCALED_REG_COUNT]], 128 +// SOFT-FLOAT: [[REG_OFFSET:%[^ ]+]] = add i64 [[SCALED_REG_COUNT]], 16 // CHECK: [[REG_SAVE_AREA_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 3 // CHECK: [[REG_SAVE_AREA:%[^ ]+]] = load i8*, i8** [[REG_SAVE_AREA_PTR:[^ ]+]] // CHECK: [[RAW_REG_ADDR:%[^ ]+]] = getelementptr i8, i8* [[REG_SAVE_AREA]], i64 [[REG_OFFSET]] diff --git a/clang/test/CodeGen/systemz-abi.cpp b/clang/test/CodeGen/systemz-abi.cpp --- a/clang/test/CodeGen/systemz-abi.cpp +++ b/clang/test/CodeGen/systemz-abi.cpp @@ -1,4 +1,6 @@ // RUN: %clang_cc1 -triple s390x-linux-gnu -emit-llvm -x c++ -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple s390x-linux-gnu -emit-llvm -x c++ -o - %s -mfloat-abi soft \ +// RUN: | FileCheck %s --check-prefix=SOFT-FLOAT // For compatibility with GCC, this structure is passed in an FPR in C++, // but passed in a GPR in C (checked in systemz-abi.c). @@ -6,4 +8,4 @@ struct agg_float_cpp { float a; int : 0; }; struct agg_float_cpp pass_agg_float_cpp(struct agg_float_cpp arg) { return arg; } // CHECK-LABEL: define void @_Z18pass_agg_float_cpp13agg_float_cpp(%struct.agg_float_cpp* noalias sret %{{.*}}, float %{{.*}}) - +// SOFT-FLOAT: define void @_Z18pass_agg_float_cpp13agg_float_cpp(%struct.agg_float_cpp* noalias sret %{{.*}}, i32 %{{.*}}) diff --git a/clang/test/CodeGen/target-data.c b/clang/test/CodeGen/target-data.c --- a/clang/test/CodeGen/target-data.c +++ b/clang/test/CodeGen/target-data.c @@ -193,6 +193,8 @@ // RUN: %clang_cc1 -triple s390x-unknown -o - -emit-llvm %s | \ // RUN: FileCheck %s -check-prefix=SYSTEMZ +// RUN: %clang_cc1 -triple s390x-unknown -target-cpu z13 -target-feature +soft-float -o - -emit-llvm %s | \ +// RUN: FileCheck %s -check-prefix=SYSTEMZ // SYSTEMZ: target datalayout = "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-a:8:16-n32:64" // RUN: %clang_cc1 -triple s390x-unknown -target-cpu z13 -o - -emit-llvm %s | \ diff --git a/clang/test/Driver/systemz-float-01.c b/clang/test/Driver/systemz-float-01.c new file mode 100644 --- /dev/null +++ b/clang/test/Driver/systemz-float-01.c @@ -0,0 +1,45 @@ +// 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: "-msoft-float" +// CHECK-DEF-NOT: "-mfloat-abi" "soft" +// +// -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" +// CHECK-HARD-NOT: "-mfloat-abi" "soft" +// +// -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: "-msoft-float" "-mfloat-abi" "soft" +// +// -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: error: unsupported option '-mfloat-abi=soft' +// +// -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: error: unsupported option '-mfloat-abi=hard' +// +// 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: unsupported option '-mfloat-abi=x' + +int foo(void) { + return 0; +} + diff --git a/clang/test/Driver/systemz-float-02.c b/clang/test/Driver/systemz-float-02.c new file mode 100644 --- /dev/null +++ b/clang/test/Driver/systemz-float-02.c @@ -0,0 +1,22 @@ +// RUN: %clang -target s390x-linux-gnu -march=z13 -S %s -o - -msoft-float | FileCheck %s +// +// Check that -msoft-float works all the way to assembly output. + +double fun0(double *A) { +// CHECK-LABEL: fun0 +// CHECK-NOT: {{%f[0-9]}} +// CHECK: brasl %r14, __adddf3@PLT + return *A + 1.0; +} + +typedef int v4si __attribute__ ((vector_size (16))); +v4si fun1(v4si *A) { +// CHECK-LABEL: fun1 +// CHECK-NOT: {{%[v][0-9]}} +// CHECK: ark +// CHECK-NEXT: ark +// CHECK-NEXT: ark +// CHECK-NEXT: ark + v4si B = {1, 1, 1, 1}; + return *A + B; +} diff --git a/llvm/lib/Target/SystemZ/SystemZFeatures.td b/llvm/lib/Target/SystemZ/SystemZFeatures.td --- a/llvm/lib/Target/SystemZ/SystemZFeatures.td +++ b/llvm/lib/Target/SystemZ/SystemZFeatures.td @@ -25,6 +25,13 @@ class SystemZFeatureAdd x, list y> : SystemZFeatureList; +// This feature is added as a subtarget feature whenever the function is +// compiled to use soft-float. +def FeatureSoftFloat : SystemZFeature< + "soft-float", "SoftFloat", + "Use software emulation for floating point" +>; + //===----------------------------------------------------------------------===// // // New features added in the Ninth Edition of the z/Architecture diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.h b/llvm/lib/Target/SystemZ/SystemZISelLowering.h --- a/llvm/lib/Target/SystemZ/SystemZISelLowering.h +++ b/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; diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp --- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp +++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp @@ -88,25 +88,27 @@ 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.hasVectorEnhancements1()) - addRegisterClass(MVT::f128, &SystemZ::VR128BitRegClass); - else - addRegisterClass(MVT::f128, &SystemZ::FP128BitRegClass); + if (!useSoftFloat()) { + 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.hasVector()) { - addRegisterClass(MVT::v16i8, &SystemZ::VR128BitRegClass); - addRegisterClass(MVT::v8i16, &SystemZ::VR128BitRegClass); - addRegisterClass(MVT::v4i32, &SystemZ::VR128BitRegClass); - addRegisterClass(MVT::v2i64, &SystemZ::VR128BitRegClass); - addRegisterClass(MVT::v4f32, &SystemZ::VR128BitRegClass); - addRegisterClass(MVT::v2f64, &SystemZ::VR128BitRegClass); + if (Subtarget.hasVector()) { + addRegisterClass(MVT::v16i8, &SystemZ::VR128BitRegClass); + addRegisterClass(MVT::v8i16, &SystemZ::VR128BitRegClass); + addRegisterClass(MVT::v4i32, &SystemZ::VR128BitRegClass); + addRegisterClass(MVT::v2i64, &SystemZ::VR128BitRegClass); + addRegisterClass(MVT::v4f32, &SystemZ::VR128BitRegClass); + addRegisterClass(MVT::v2f64, &SystemZ::VR128BitRegClass); + } } // Compute derived properties from the register classes @@ -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()) @@ -1123,12 +1129,14 @@ return std::make_pair(0U, &SystemZ::GRH32BitRegClass); case 'f': // Floating-point register - if (VT == MVT::f64) - return std::make_pair(0U, &SystemZ::FP64BitRegClass); - else if (VT == MVT::f128) - return std::make_pair(0U, &SystemZ::FP128BitRegClass); - return std::make_pair(0U, &SystemZ::FP32BitRegClass); - + if (!useSoftFloat()) { + if (VT == MVT::f64) + return std::make_pair(0U, &SystemZ::FP64BitRegClass); + else if (VT == MVT::f128) + return std::make_pair(0U, &SystemZ::FP128BitRegClass); + return std::make_pair(0U, &SystemZ::FP32BitRegClass); + } + break; case 'v': // Vector register if (Subtarget.hasVector()) { if (VT == MVT::f32) @@ -1155,7 +1163,7 @@ return parseRegisterNumber(Constraint, &SystemZ::GR64BitRegClass, SystemZMC::GR64Regs, 16); } - if (Constraint[1] == 'f') { + if (Constraint[1] == 'f' && !useSoftFloat()) { if (VT == MVT::f32) return parseRegisterNumber(Constraint, &SystemZ::FP32BitRegClass, SystemZMC::FP32Regs, 16); @@ -1443,7 +1451,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]); diff --git a/llvm/lib/Target/SystemZ/SystemZSubtarget.h b/llvm/lib/Target/SystemZ/SystemZSubtarget.h --- a/llvm/lib/Target/SystemZ/SystemZSubtarget.h +++ b/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; diff --git a/llvm/lib/Target/SystemZ/SystemZSubtarget.cpp b/llvm/lib/Target/SystemZ/SystemZSubtarget.cpp --- a/llvm/lib/Target/SystemZ/SystemZSubtarget.cpp +++ b/llvm/lib/Target/SystemZ/SystemZSubtarget.cpp @@ -33,6 +33,11 @@ CPUName = "generic"; // Parse features string. ParseSubtargetFeatures(CPUName, FS); + + // -msoft-float implies -mno-vx. + if (HasSoftFloat) + HasVector = false; + return *this; } @@ -57,7 +62,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() {} diff --git a/llvm/lib/Target/SystemZ/SystemZTargetMachine.cpp b/llvm/lib/Target/SystemZ/SystemZTargetMachine.cpp --- a/llvm/lib/Target/SystemZ/SystemZTargetMachine.cpp +++ b/llvm/lib/Target/SystemZ/SystemZTargetMachine.cpp @@ -40,6 +40,7 @@ // This is the case by default if CPU is z13 or later, and can be // overridden via "[+-]vector" feature string elements. bool VectorABI = true; + bool SoftFloat = false; if (CPU.empty() || CPU == "generic" || CPU == "z10" || CPU == "z196" || CPU == "zEC12") VectorABI = false; @@ -51,9 +52,13 @@ VectorABI = true; if (Feature == "-vector") VectorABI = false; + if (Feature == "soft-float" || Feature == "+soft-float") + SoftFloat = true; + if (Feature == "-soft-float") + SoftFloat = false; } - return VectorABI; + return VectorABI && !SoftFloat; } static std::string computeDataLayout(const Triple &TT, StringRef CPU, @@ -193,7 +198,8 @@ void SystemZPassConfig::addIRPasses() { if (getOptLevel() != CodeGenOpt::None) { - addPass(createSystemZTDCPass()); + if (!getTM().getSubtargetImpl()->hasSoftFloat()) + addPass(createSystemZTDCPass()); addPass(createLoopDataPrefetchPass()); } diff --git a/llvm/test/CodeGen/SystemZ/args-07.ll b/llvm/test/CodeGen/SystemZ/args-07.ll --- a/llvm/test/CodeGen/SystemZ/args-07.ll +++ b/llvm/test/CodeGen/SystemZ/args-07.ll @@ -1,6 +1,8 @@ ; Test multiple return values (LLVM ABI extension) ; ; RUN: llc < %s -mtriple=s390x-linux-gnu -verify-machineinstrs| FileCheck %s +; RUN: llc < %s -mtriple=s390x-linux-gnu -verify-machineinstrs \ +; RUN: -mattr=soft-float | FileCheck %s --check-prefix=SOFT-FLOAT ; Up to four integer return values fit into GPRs. define { i64, i64, i64, i64 } @f1() { @@ -37,6 +39,14 @@ ; CHECK: larl [[TMP:%r[0-5]]], .LCPI ; CHECK: ldeb %f6, 0([[TMP]]) ; CHECK: br %r14 + +; SOFT-FLOAT-LABEL: f3: +; SOFT-FLOAT-NOT: %{{[fv]}} +; SOFT-FLOAT: llihh %r2, 16368 +; SOFT-FLOAT-NEXT: llihh %r3, 16384 +; SOFT-FLOAT-NEXT: llihh %r4, 16392 +; SOFT-FLOAT-NEXT: llihh %r5, 16400 +; SOFT-FLOAT-NEXT: br %r14 ret { double, double, double, double } { double 1.0, double 2.0, double 3.0, double 4.0 } } @@ -55,6 +65,21 @@ ; CHECK: llihh [[TMP:%r[0-5]]], 16368 ; CHECK: stg [[TMP]], 0(%r2) ; CHECK: br %r14 + +; SOFT-FLOAT-LABEL: f4: +; SOFT-FLOAT-NOT: %{{[fv]}} +; SOFT-FLOAT-NOT: %r2 +; SOFT-FLOAT: llihh %r0, 16404 +; SOFT-FLOAT-NEXT: stg %r0, 32(%r2) +; SOFT-FLOAT-NEXT: llihh %r0, 16400 +; SOFT-FLOAT-NEXT: stg %r0, 24(%r2) +; SOFT-FLOAT-NEXT: llihh %r0, 16392 +; SOFT-FLOAT-NEXT: stg %r0, 16(%r2) +; SOFT-FLOAT-NEXT: llihh %r0, 16384 +; SOFT-FLOAT-NEXT: stg %r0, 8(%r2) +; SOFT-FLOAT-NEXT: llihh %r0, 16368 +; SOFT-FLOAT-NEXT: stg %r0, 0(%r2) +; SOFT-FLOAT-NEXT: br %r14 ret { double, double, double, double, double } { double 1.0, double 2.0, double 3.0, double 4.0, double 5.0 } } diff --git a/llvm/test/CodeGen/SystemZ/soft-float-01.ll b/llvm/test/CodeGen/SystemZ/soft-float-01.ll new file mode 100644 --- /dev/null +++ b/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) { + ; CHECK-LABEL: test_addsf3: + ; CHECK: brasl %r14, __addsf3 + %add = fadd float %a, %b + ret float %add +} + +define double @test_adddf3(double %a, double %b) { + ; CHECK-LABEL: test_adddf3: + ; CHECK: brasl %r14, __adddf3 + %add = fadd double %a, %b + ret double %add +} + +define fp128 @test_addtf3(fp128 %a, fp128 %b) { + ; CHECK-LABEL: test_addtf3: + ; CHECK: brasl %r14, __addtf3 + %add = fadd fp128 %a, %b + ret fp128 %add +} + +define float @test_mulsf3(float %a, float %b) { + ; CHECK-LABEL: test_mulsf3: + ; CHECK: brasl %r14, __mulsf3 + %mul = fmul float %a, %b + ret float %mul +} + +define double @test_muldf3(double %a, double %b) { + ; CHECK-LABEL: test_muldf3: + ; CHECK: brasl %r14, __muldf3 + %mul = fmul double %a, %b + ret double %mul +} + +define fp128 @test_multf3(fp128 %a, fp128 %b) { + ; CHECK-LABEL: test_multf3: + ; CHECK: brasl %r14, __multf3 + %mul = fmul fp128 %a, %b + ret fp128 %mul +} + +define float @test_subsf3(float %a, float %b) { + ; CHECK-LABEL: test_subsf3: + ; CHECK: brasl %r14, __subsf3 + %sub = fsub float %a, %b + ret float %sub +} + +define double @test_subdf3(double %a, double %b) { + ; CHECK-LABEL: test_subdf3: + ; CHECK: brasl %r14, __subdf3 + %sub = fsub double %a, %b + ret double %sub +} + +define fp128 @test_subtf3(fp128 %a, fp128 %b) { + ; CHECK-LABEL: test_subtf3: + ; CHECK: brasl %r14, __subtf3 + %sub = fsub fp128 %a, %b + ret fp128 %sub +} + +define float @test_divsf3(float %a, float %b) { + ; CHECK-LABEL: test_divsf3: + ; CHECK: brasl %r14, __divsf3 + %div = fdiv float %a, %b + ret float %div +} + +define double @test_divdf3(double %a, double %b) { + ; CHECK-LABEL: test_divdf3: + ; CHECK: brasl %r14, __divdf3 + %div = fdiv double %a, %b + ret double %div +} + +define fp128 @test_divtf3(fp128 %a, fp128 %b) { + ; 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) { + ; 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) { + ; 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) { + ; 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) { + ; 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) { + ; 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) { + ; 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) { + ; 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) { + ; 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) { + ; 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) { + ; 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) { + ; 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) { + ; 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) { + ; 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) { + ; 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) { + ; 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) { + ; 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) { + ; 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) { + ; 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) { + ; 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) { + ; 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) { + ; CHECK-LABEL: test_gttf2: + ; CHECK: brasl %r14, __gttf2 + %cmp = fcmp ogt fp128 %a, %b + ret i1 %cmp +} diff --git a/llvm/test/CodeGen/SystemZ/soft-float-02.ll b/llvm/test/CodeGen/SystemZ/soft-float-02.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/soft-float-02.ll @@ -0,0 +1,15 @@ +; RUN: llc < %s -mtriple=s390x-linux-gnu -mattr=soft-float | FileCheck %s +; +; Check that FP registers are not saved in a vararg function if soft-float is +; used. + +define void @fun0(...) { +; CHECK-LABEL: fun0 +; CHECK-NOT: std %f0 +; CHECK-NOT: std %f2 +; CHECK-NOT: std %f4 +; CHECK-NOT: std %f6 + ret void +} + + diff --git a/llvm/test/CodeGen/SystemZ/soft-float-03.ll b/llvm/test/CodeGen/SystemZ/soft-float-03.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/soft-float-03.ll @@ -0,0 +1,11 @@ +; RUN: llc -mcpu=z13 -mattr=soft-float -O3 < %s | FileCheck %s +; +; Check that soft-float implies "-vector". + +define <2 x i64> @f0(<2 x i64> %dummy, <2 x i64> %val1, <2 x i64> %val2) { +; CHECK-LABEL: f0: +; CHECK-NOT: vag +; CHECK-NOT: %v + %res = add <2 x i64> %val1, %val2 + ret <2 x i64> %res +} diff --git a/llvm/test/CodeGen/SystemZ/soft-float-04.ll b/llvm/test/CodeGen/SystemZ/soft-float-04.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/soft-float-04.ll @@ -0,0 +1,22 @@ +; RUN: llc -mcpu=z14 -O3 -mattr=soft-float < %s | FileCheck %s +; +; Check that this function with soft-float does not result in a s390.tdc +; intrinsic (which cannot be handled by SoftenFloatOperand). + +define void @fun(float %arg) { +; CHECK-LABEL: fun: +; CHECK: cijl +bb: + %tmp = bitcast float %arg to i32 + br label %bb1 + +bb1: ; preds = %bb + %tmp2 = icmp sgt i32 %tmp, -1 + br i1 %tmp2, label %bb3, label %bb4 + +bb3: ; preds = %bb1 + unreachable + +bb4: ; preds = %bb1 + unreachable +} diff --git a/llvm/test/CodeGen/SystemZ/soft-float-args.ll b/llvm/test/CodeGen/SystemZ/soft-float-args.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/soft-float-args.ll @@ -0,0 +1,308 @@ +; RUN: llc -mcpu=z13 -mattr=soft-float -O3 < %s | FileCheck %s +; +; Test that arguments and return values of fp/vector types are always handled +; with gprs with soft-float. + +define double @f1(double %arg) { +; CHECK-LABEL: f1: +; CHECK-NOT: %r2 +; CHECK-NOT: %{{[fv]}} +; CHECK: llihh %r3, 16368 +; CHECK-NEXT: brasl %r14, __adddf3@PLT +; CHECK-NEXT: lmg %r14, %r15, 272(%r15) +; CHECK-NEXT: br %r14 + %res = fadd double %arg, 1.0 + ret double %res +} + +define float @f2(float %arg) { +; CHECK-LABEL: f2: +; CHECK-NOT: %r2 +; CHECK-NOT: %{{[fv]}} +; CHECK: llgfr %r2, %r2 +; CHECK-NEXT: llilh %r3, 16256 +; CHECK-NEXT: brasl %r14, __addsf3@PLT +; CHECK-NEXT: # kill: def $r2l killed $r2l killed $r2d +; CHECK-NEXT: lmg %r14, %r15, 272(%r15) +; CHECK-NEXT: br %r14 + %res = fadd float %arg, 1.0 + ret float %res +} + +define fp128 @f2_fp128(fp128 %arg) { +; CHECK-LABEL: f2_fp128: +; CHECK-NOT: %{{[fv]}} +; CHECK: aghi %r15, -208 +; CHECK-NEXT: .cfi_def_cfa_offset 368 +; CHECK-NEXT: lg %r0, 0(%r2) +; CHECK-NEXT: lg %r1, 8(%r2) +; CHECK-NEXT: llihf %r2, 1073823744 +; CHECK-NEXT: stg %r2, 160(%r15) +; CHECK-NEXT: la %r2, 192(%r15) +; CHECK-NEXT: la %r3, 176(%r15) +; CHECK-NEXT: la %r4, 160(%r15) +; CHECK-NEXT: stg %r1, 184(%r15) +; CHECK-NEXT: stg %r0, 176(%r15) +; CHECK-NEXT: mvghi 168(%r15), 0 +; CHECK-NEXT: brasl %r14, __addtf3@PLT +; CHECK-NEXT: lg %r2, 192(%r15) +; CHECK-NEXT: lg %r3, 200(%r15) +; CHECK-NEXT: lmg %r14, %r15, 320(%r15) +; CHECK-NEXT: br %r14 + %res = fadd fp128 %arg, 0xL00000000000000004001400000000000 + ret fp128 %res +} + +define <2 x double> @f3(<2 x double> %arg) { +; CHECK-LABEL: f3: +; CHECK-NOT: %{{[fv]}} +; CHECK: lg %r13, 8(%r2) +; CHECK-NEXT: lg %r2, 0(%r2) +; CHECK-NEXT: llihh %r3, 16368 +; CHECK-NEXT: brasl %r14, __adddf3@PLT +; CHECK-NEXT: lgr %r12, %r2 +; CHECK-NEXT: lgr %r2, %r13 +; CHECK-NEXT: llihh %r3, 16368 +; CHECK-NEXT: brasl %r14, __adddf3@PLT +; CHECK-NEXT: lgr %r3, %r2 +; CHECK-NEXT: lgr %r2, %r12 +; CHECK-NEXT: lmg %r12, %r15, 256(%r15) +; CHECK-NEXT: br %r14 + %res = fadd <2 x double> %arg, + ret <2 x double> %res +} + +define <2 x float> @f4(<2 x float> %arg) { +; CHECK-LABEL: f4: +; CHECK-NOT: %{{[fv]}} +; CHECK: lr %r13, %r3 +; CHECK-NEXT: llgfr %r2, %r2 +; CHECK-NEXT: llilh %r3, 16256 +; CHECK-NEXT: brasl %r14, __addsf3@PLT +; CHECK-NEXT: lgr %r12, %r2 +; CHECK-NEXT: llgfr %r2, %r13 +; CHECK-NEXT: llilh %r3, 16256 +; CHECK-NEXT: brasl %r14, __addsf3@PLT +; CHECK-NEXT: lgr %r3, %r2 +; CHECK-NEXT: lr %r2, %r12 +; CHECK-NEXT: # kill: def $r3l killed $r3l killed $r3d +; CHECK-NEXT: lmg %r12, %r15, 256(%r15) +; CHECK-NEXT: br %r14 + %res = fadd <2 x float> %arg, + ret <2 x float> %res +} + +define <2 x i64> @f5(<2 x i64> %arg) { +; CHECK-LABEL: f5: +; CHECK-NOT: %{{[fv]}} +; CHECK: lghi %r0, 1 +; CHECK-NEXT: ag %r0, 0(%r2) +; CHECK-NEXT: lghi %r3, 1 +; CHECK-NEXT: ag %r3, 8(%r2) +; CHECK-NEXT: lgr %r2, %r0 +; CHECK-NEXT: br %r14 + %res = add <2 x i64> %arg, + ret <2 x i64> %res +} + +define <2 x i32> @f6(<2 x i32> %arg) { +; CHECK-LABEL: f6: +; CHECK-NOT: %{{[fv]}} +; CHECK: ahi %r2, 1 +; CHECK-NEXT: ahi %r3, 1 +; CHECK-NEXT: br %r14 + %res = add <2 x i32> %arg, + ret <2 x i32> %res +} + +;; Stack arguments + +define double @f7(double %A, double %B, double %C, double %D, double %E, + double %F) { +; CHECK-LABEL: f7: +; CHECK-NOT: %{{[fv]}} +; CHECK: aghi %r15, -160 +; CHECK-NEXT: .cfi_def_cfa_offset 320 +; CHECK-NEXT: lg %r3, 320(%r15) +; CHECK-NEXT: brasl %r14, __adddf3@PLT +; CHECK-NEXT: lmg %r14, %r15, 272(%r15) +; CHECK-NEXT: br %r14 + + %res = fadd double %A, %F + ret double %res +} + +define float @f8(float %A, float %B, float %C, float %D, float %E, + float %F) { +; CHECK-LABEL: f8: +; CHECK-NOT: %{{[fv]}} +; CHECK: aghi %r15, -160 +; CHECK-NEXT: .cfi_def_cfa_offset 320 +; CHECK-NEXT: llgf %r3, 324(%r15) +; CHECK-NEXT: llgfr %r2, %r2 +; CHECK-NEXT: brasl %r14, __addsf3@PLT +; CHECK-NEXT: # kill: def $r2l killed $r2l killed $r2d +; CHECK-NEXT: lmg %r14, %r15, 272(%r15) +; CHECK-NEXT: br %r14 + %res = fadd float %A, %F + ret float %res +} + +define <2 x double> @f9(<2 x double> %A, <2 x double> %B, <2 x double> %C, + <2 x double> %D, <2 x double> %E, <2 x double> %F, + <2 x double> %G, <2 x double> %H, <2 x double> %I) { +; CHECK-LABEL: f9: +; CHECK-NOT: %{{[fv]}} +; CHECK: aghi %r15, -160 +; CHECK-NEXT: .cfi_def_cfa_offset 320 +; CHECK-NEXT: lg %r1, 344(%r15) +; CHECK-NEXT: lg %r13, 8(%r2) +; CHECK-NEXT: lg %r2, 0(%r2) +; CHECK-NEXT: lg %r3, 0(%r1) +; CHECK-NEXT: lg %r12, 8(%r1) +; CHECK-NEXT: brasl %r14, __adddf3@PLT +; CHECK-NEXT: lgr %r11, %r2 +; CHECK-NEXT: lgr %r2, %r13 +; CHECK-NEXT: lgr %r3, %r12 +; CHECK-NEXT: brasl %r14, __adddf3@PLT +; CHECK-NEXT: lgr %r3, %r2 +; CHECK-NEXT: lgr %r2, %r11 +; CHECK-NEXT: lmg %r11, %r15, 248(%r15) +; CHECK-NEXT: br %r14 + %res = fadd <2 x double> %A, %I + ret <2 x double> %res +} + +define <2 x float> @f10(<2 x float> %A, <2 x float> %B, <2 x float> %C, + <2 x float> %D, <2 x float> %E, <2 x float> %F, + <2 x float> %G, <2 x float> %H, <2 x float> %I) { +; CHECK-LABEL: f10: +; CHECK-NOT: %{{[fv]}} +; CHECK: aghi %r15, -160 +; CHECK-NEXT: .cfi_def_cfa_offset 320 +; CHECK-NEXT: lr %r13, %r3 +; CHECK-NEXT: llgf %r3, 412(%r15) +; CHECK-NEXT: llgf %r12, 420(%r15) +; CHECK-NEXT: llgfr %r2, %r2 +; CHECK-NEXT: brasl %r14, __addsf3@PLT +; CHECK-NEXT: lgr %r11, %r2 +; CHECK-NEXT: llgfr %r2, %r13 +; CHECK-NEXT: lgr %r3, %r12 +; CHECK-NEXT: brasl %r14, __addsf3@PLT +; CHECK-NEXT: lgr %r3, %r2 +; CHECK-NEXT: lr %r2, %r11 +; CHECK-NEXT: # kill: def $r3l killed $r3l killed $r3d +; CHECK-NEXT: lmg %r11, %r15, 248(%r15) +; CHECK-NEXT: br %r14 + + %res = fadd <2 x float> %A, %I + ret <2 x float> %res +} + +define <2 x i64> @f11(<2 x i64> %A, <2 x i64> %B, <2 x i64> %C, + <2 x i64> %D, <2 x i64> %E, <2 x i64> %F, + <2 x i64> %G, <2 x i64> %H, <2 x i64> %I) { +; CHECK-LABEL: f11: +; CHECK-NOT: %{{[fv]}} +; CHECK: lg %r1, 184(%r15) +; CHECK-NEXT: lg %r3, 8(%r2) +; CHECK-NEXT: lg %r2, 0(%r2) +; CHECK-NEXT: ag %r2, 0(%r1) +; CHECK-NEXT: ag %r3, 8(%r1) +; CHECK-NEXT: br %r14 + %res = add <2 x i64> %A, %I + ret <2 x i64> %res +} + +;; calls + +declare double @bar_double(double %arg); +define double @f12(double %arg, double %arg2) { +; CHECK-LABEL: f12: +; CHECK-NOT: %{{[fv]}} +; CHECK-NOT: %r{{[23]}} +; CHECK: lgr %r2, %r3 +; CHECK-NEXT: brasl %r14, bar_double@PLT +; CHECK-NEXT: lmg %r14, %r15, 272(%r15) +; CHECK-NEXT: br %r14 + %res = call double @bar_double(double %arg2) + ret double %res +} + +declare float @bar_float(float %arg); +define float @f13(float %arg, float %arg2) { +; CHECK-LABEL: f13: +; CHECK-NOT: %{{[fv]}} +; CHECK-NOT: %r{{[23]}} +; CHECK: lr %r2, %r3 +; CHECK-NEXT: brasl %r14, bar_float@PLT +; CHECK-NEXT: lmg %r14, %r15, 272(%r15) +; CHECK-NEXT: br %r14 + %res = call float @bar_float(float %arg2) + ret float %res +} + +declare fp128 @bar_fp128(fp128 %arg); +define fp128 @f14(fp128 %arg, fp128 %arg2) { +; CHECK-LABEL: f14: +; CHECK-NOT: %{{[fv]}} +; CHECK-NOT: %r3 +; CHECK: lg %r0, 0(%r3) +; CHECK-NEXT: lg %r1, 8(%r3) +; CHECK-NEXT: la %r2, 160(%r15) +; CHECK-NEXT: stg %r1, 168(%r15) +; CHECK-NEXT: stg %r0, 160(%r15) +; CHECK-NEXT: brasl %r14, bar_fp128@PLT +; CHECK-NEXT: lmg %r14, %r15, 288(%r15) +; CHECK-NEXT: br %r14 + %res = call fp128 @bar_fp128(fp128 %arg2) + ret fp128 %res +} + +declare <2 x double> @bar_v2f64(<2 x double> %arg); +define <2 x double> @f15(<2 x double> %arg, <2 x double> %arg2) { +; CHECK-LABEL: f15: +; CHECK-NOT: %{{[fv]}} +; CHECK-NOT: %r3 +; CHECK: lg %r0, 0(%r3) +; CHECK-NEXT: lg %r1, 8(%r3) +; CHECK-NEXT: la %r2, 160(%r15) +; CHECK-NEXT: stg %r1, 168(%r15) +; CHECK-NEXT: stg %r0, 160(%r15) +; CHECK-NEXT: brasl %r14, bar_v2f64@PLT +; CHECK-NEXT: lmg %r14, %r15, 288(%r15) +; CHECK-NEXT: br %r14 + %res = call <2 x double> @bar_v2f64(<2 x double> %arg2) + ret <2 x double> %res +} + +declare <2 x float> @bar_v2f32(<2 x float> %arg); +define <2 x float> @f16(<2 x float> %arg, <2 x float> %arg2) { +; CHECK-LABEL: f16: +; CHECK-NOT: %{{[fv]}} +; CHECK-NOT: %r{{[2345]}} +; CHECK: lr %r3, %r5 +; CHECK-NEXT: lr %r2, %r4 +; CHECK-NEXT: brasl %r14, bar_v2f32@PLT +; CHECK-NEXT: lmg %r14, %r15, 272(%r15) +; CHECK-NEXT: br %r14 + %res = call <2 x float> @bar_v2f32(<2 x float> %arg2) + ret <2 x float> %res +} + +declare <2 x i64> @bar_v2i64(<2 x i64> %arg); +define <2 x i64> @f17(<2 x i64> %arg, <2 x i64> %arg2) { +; CHECK-LABEL: f17: +; CHECK-NOT: %{{[fv]}} +; CHECK-NOT: %r3 +; CHECK: lg %r0, 0(%r3) +; CHECK-NEXT: lg %r1, 8(%r3) +; CHECK-NEXT: la %r2, 160(%r15) +; CHECK-NEXT: stg %r1, 168(%r15) +; CHECK-NEXT: stg %r0, 160(%r15) +; CHECK-NEXT: brasl %r14, bar_v2i64@PLT +; CHECK-NEXT: lmg %r14, %r15, 288(%r15) +; CHECK-NEXT: br %r14 + %res = call <2 x i64> @bar_v2i64(<2 x i64> %arg2) + ret <2 x i64> %res +} diff --git a/llvm/test/CodeGen/SystemZ/soft-float-inline-asm-01.ll b/llvm/test/CodeGen/SystemZ/soft-float-inline-asm-01.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/soft-float-inline-asm-01.ll @@ -0,0 +1,10 @@ +; RUN: not llc < %s -mcpu=z13 -mattr=soft-float -O3 2>&1 | FileCheck %s +; +; Verify that inline asms cannot use fp/vector registers with soft-float. + +define float @f1() { + %ret = call float asm "", "=f" () + ret float %ret +} + +; CHECK: error: couldn't allocate output register for constraint 'f' diff --git a/llvm/test/CodeGen/SystemZ/soft-float-inline-asm-02.ll b/llvm/test/CodeGen/SystemZ/soft-float-inline-asm-02.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/soft-float-inline-asm-02.ll @@ -0,0 +1,10 @@ +; RUN: not llc < %s -mcpu=z13 -mattr=soft-float -O3 2>&1 | FileCheck %s +; +; Verify that inline asms cannot use fp/vector registers with soft-float. + +define float @f1() { + %ret = call float asm "", "={f0}" () + ret float %ret +} + +; CHECK: error: couldn't allocate output register for constraint '{f0}' diff --git a/llvm/test/CodeGen/SystemZ/soft-float-inline-asm-03.ll b/llvm/test/CodeGen/SystemZ/soft-float-inline-asm-03.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/soft-float-inline-asm-03.ll @@ -0,0 +1,10 @@ +; RUN: not llc < %s -mcpu=z13 -mattr=soft-float -O3 2>&1 | FileCheck %s +; +; Verify that inline asms cannot use fp/vector registers with soft-float. + +define <2 x i64> @f1() { + %ret = call <2 x i64> asm "", "=v" () + ret <2 x i64> %ret +} + +; CHECK: error: couldn't allocate output register for constraint 'v' diff --git a/llvm/test/CodeGen/SystemZ/vec-abi-align.ll b/llvm/test/CodeGen/SystemZ/vec-abi-align.ll --- a/llvm/test/CodeGen/SystemZ/vec-abi-align.ll +++ b/llvm/test/CodeGen/SystemZ/vec-abi-align.ll @@ -32,6 +32,15 @@ ; RUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z13 -mattr=-vector | \ ; RUN: FileCheck -check-prefix=CHECK-NOVECTOR %s +; RUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z13 -mattr=+soft-float | \ +; RUN: FileCheck -check-prefix=CHECK-NOVECTOR %s +; RUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z13 \ +; RUN: -mattr=soft-float,-soft-float | \ +; RUN: FileCheck -check-prefix=CHECK-VECTOR %s +; RUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z13 \ +; RUN: -mattr=-soft-float,soft-float | \ +; RUN: FileCheck -check-prefix=CHECK-NOVECTOR %s + %struct.S = type { i8, <2 x i64> } define void @test(%struct.S* %s) nounwind { diff --git a/llvm/test/CodeGen/SystemZ/vec-args-06.ll b/llvm/test/CodeGen/SystemZ/vec-args-06.ll --- a/llvm/test/CodeGen/SystemZ/vec-args-06.ll +++ b/llvm/test/CodeGen/SystemZ/vec-args-06.ll @@ -1,6 +1,8 @@ ; Test multiple return values (LLVM ABI extension) ; ; RUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z13 | FileCheck %s +; RUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z13 -mattr=soft-float \ +; RUN: | FileCheck %s --check-prefix=SOFT-FLOAT ; Up to eight vector return values fit into VRs. define { <2 x double>, <2 x double>, <2 x double>, <2 x double>, @@ -23,6 +25,17 @@ ; CHECK: larl [[TMP:%r[0-5]]], .LCPI ; CHECK: vl %v31, 0([[TMP]]) ; CHECK: br %r14 + +; SOFT-FLOAT-LABEL: f1: +; SOFT-FLOAT-NOT: %{{[fv]}} +; SOFT-FLOAT: llihf +; SOFT-FLOAT-NEXT: oilf +; SOFT-FLOAT-NEXT: stg +; SOFT-FLOAT-NEXT: llihh +; SOFT-FLOAT-NEXT: stg +; SOFT-FLOAT-NEXT: llihf +; SOFT-FLOAT-NOT: %{{[fv]}} +; SOFT-FLOAT: br %r14 ret { <2 x double>, <2 x double>, <2 x double>, <2 x double>, <2 x double>, <2 x double>, <2 x double>, <2 x double> } { <2 x double> , @@ -68,6 +81,17 @@ ; CHECK: vl [[VTMP:%v[0-9]+]], 0([[TMP]]) ; CHECK: vst [[VTMP]], 0(%r2) ; CHECK: br %r14 + +; SOFT-FLOAT-LABEL: f2: +; SOFT-FLOAT-NOT: %{{[fv]}} +; SOFT-FLOAT: llihf +; SOFT-FLOAT-NEXT: oilf +; SOFT-FLOAT-NEXT: stg +; SOFT-FLOAT-NEXT: llihh +; SOFT-FLOAT-NEXT: stg +; SOFT-FLOAT-NEXT: llihf +; SOFT-FLOAT-NOT: %{{[fv]}} +; SOFT-FLOAT: br %r14 ret { <2 x double>, <2 x double>, <2 x double>, <2 x double>, <2 x double>, <2 x double>, <2 x double>, <2 x double>, <2 x double> } diff --git a/llvm/test/CodeGen/SystemZ/vec-args-07.ll b/llvm/test/CodeGen/SystemZ/vec-args-07.ll --- a/llvm/test/CodeGen/SystemZ/vec-args-07.ll +++ b/llvm/test/CodeGen/SystemZ/vec-args-07.ll @@ -1,6 +1,8 @@ ; Test calling functions with multiple return values (LLVM ABI extension) ; ; RUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z13 | FileCheck %s +; RUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z13 -mattr=soft-float \ +; RUN: | FileCheck %s --check-prefix=SOFT-FLOAT ; Up to eight vector return values fit into VRs. declare { <2 x double>, <2 x double>, <2 x double>, <2 x double>, @@ -11,6 +13,14 @@ ; CHECK: brasl %r14, bar1 ; CHECK: vlr %v24, %v31 ; CHECK: br %r14 + +; SOFT-FLOAT-LABEL: f1: +; SOFT-FLOAT-NOT: %{{[fv]}} +; SOFT-FLOAT: brasl %r14, bar1 +; SOFT-FLOAT-NEXT: lg %r3, 280(%r15) +; SOFT-FLOAT-NEXT: lg %r2, 272(%r15) +; SOFT-FLOAT-NEXT: lmg %r14, %r15, 400(%r15) +; SOFT-FLOAT-NEXT: br %r14 %mret = call { <2 x double>, <2 x double>, <2 x double>, <2 x double>, <2 x double>, <2 x double>, @@ -33,6 +43,14 @@ ; CHECK: brasl %r14, bar2 ; CHECK: vl %v24, 288(%r15) ; CHECK: br %r14 + +; SOFT-FLOAT-LABEL: f2: +; SOFT-FLOAT-NOT: %{{[fv]}} +; SOFT-FLOAT: brasl %r14, bar2 +; SOFT-FLOAT-NEXT: lg %r3, 296(%r15) +; SOFT-FLOAT-NEXT: lg %r2, 288(%r15) +; SOFT-FLOAT-NEXT: lmg %r14, %r15, 416(%r15) +; SOFT-FLOAT-NEXT: br %r14 %mret = call { <2 x double>, <2 x double>, <2 x double>, <2 x double>, <2 x double>, <2 x double>,