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<Builtin::Info> SystemZTargetInfo::getTargetBuiltins() const {
Index: clang/lib/CodeGen/TargetInfo.cpp
===================================================================
--- clang/lib/CodeGen/TargetInfo.cpp
+++ clang/lib/CodeGen/TargetInfo.cpp
@@ -6579,10 +6579,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;
@@ -6614,8 +6615,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)) {}
 };
 
 }
@@ -6654,6 +6655,9 @@
 }
 
 bool SystemZABIInfo::isFPArgumentType(QualType Ty) const {
+  if (IsSoftFloatABI)
+    return false;
+
   if (const BuiltinType *BT = Ty->getAs<BuiltinType>())
     switch (BT->getKind()) {
     case BuiltinType::Float:
@@ -6739,7 +6743,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;
@@ -9905,7 +9909,8 @@
 
   case llvm::Triple::systemz: {
     bool HasVector = getTarget().getABI() == "vector";
-    return SetCGInfo(new SystemZTargetCodeGenInfo(Types, HasVector));
+    bool SoftFloat = CodeGenOpts.FloatABI == "soft";
+    return SetCGInfo(new SystemZTargetCodeGenInfo(Types, HasVector, SoftFloat));
   }
 
   case llvm::Triple::tce:
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 <string>
@@ -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<llvm::StringRef> &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,28 @@
 using namespace clang;
 using namespace llvm::opt;
 
+systemz::FloatABI systemz::getSystemZFloatABI(const Driver &D,
+                                              const ArgList &Args) {
+  systemz::FloatABI ABI = systemz::FloatABI::Invalid;
+  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;
+    else if (A->getOption().matches(options::OPT_mhard_float))
+      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 +57,7 @@
   return "z10";
 }
 
-void systemz::getSystemZTargetFeatures(const ArgList &Args,
+void systemz::getSystemZTargetFeatures(const Driver &D, const ArgList &Args,
                                        std::vector<llvm::StringRef> &Features) {
   // -m(no-)htm overrides use of the transactional-execution facility.
   if (Arg *A = Args.getLastArg(options::OPT_mhtm, options::OPT_mno_htm)) {
@@ -42,11 +66,16 @@
     else
       Features.push_back("-transactional-execution");
   }
-  // -m(no-)vx overrides use of the vector facility.
-  if (Arg *A = Args.getLastArg(options::OPT_mvx, options::OPT_mno_vx)) {
-    if (A->getOption().matches(options::OPT_mvx))
-      Features.push_back("+vector");
-    else
-      Features.push_back("-vector");
-  }
+  // -m(no-)vx overrides use of the vector facility. -msoft-float implies
+  // -mno-vx.
+  systemz::FloatABI FloatABI = systemz::getSystemZFloatABI(D, Args);
+  if (FloatABI == systemz::FloatABI::Soft)
+    Features.push_back("-vector");
+  else
+    if (Arg *A = Args.getLastArg(options::OPT_mvx, options::OPT_mno_vx)) {
+      if (A->getOption().matches(options::OPT_mvx))
+        Features.push_back("+vector");
+      else
+        Features.push_back("-vector");
+    }
 }
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:
@@ -2010,6 +2010,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");
+  }
 }
 
 void Clang::AddX86TargetArgs(const ArgList &Args,
Index: clang/test/CodeGen/systemz-abi.c
===================================================================
--- clang/test/CodeGen/systemz-abi.c
+++ 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]]
Index: clang/test/CodeGen/systemz-abi.cpp
===================================================================
--- clang/test/CodeGen/systemz-abi.cpp
+++ 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 %{{.*}})
Index: clang/test/Driver/systemz-float.c
===================================================================
--- /dev/null
+++ clang/test/Driver/systemz-float.c
@@ -0,0 +1,46 @@
+// 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: "-target-feature" "-vector"
+// 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;
+}
+
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<list<SystemZFeature> x, list<SystemZFeature> y>
   : SystemZFeatureList<!listconcat(x, y)>;
 
+
+// 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,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())
@@ -1100,6 +1106,14 @@
 std::pair<unsigned, const TargetRegisterClass *>
 SystemZTargetLowering::getRegForInlineAsmConstraint(
     const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const {
+
+  if (useSoftFloat() &&
+      ((Constraint.size() == 1 &&
+        (Constraint[0] == 'f' || Constraint[0] == 'v')) ||
+      ((Constraint.size() > 0 && Constraint[0] == '{') &&
+       (Constraint[1] == 'f' || Constraint[1] == 'v'))))
+    report_fatal_error("Inline asm is using an fp/vector reg with soft-float.");
+
   if (Constraint.size() == 1) {
     // GCC Constraint Letters
     switch (Constraint[0]) {
@@ -1443,7 +1457,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
@@ -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() {}
 
Index: llvm/lib/Target/SystemZ/SystemZTDC.cpp
===================================================================
--- llvm/lib/Target/SystemZ/SystemZTDC.cpp
+++ llvm/lib/Target/SystemZ/SystemZTDC.cpp
@@ -310,6 +310,9 @@
 }
 
 bool SystemZTDCPass::runOnFunction(Function &F) {
+  if (F.getFnAttribute("use-soft-float").getValueAsString() == "true")
+    return false;
+
   ConvertedInsts.clear();
   LogicOpsWorklist.clear();
   PossibleJunk.clear();
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<TargetLoweringObjectFile> TLOF;
   SystemZSubtarget Subtarget;
+  mutable StringMap<std::unique_ptr<SystemZSubtarget>> 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<SystemZSubtarget>(TargetTriple, CPU, FS, *this);
+  }
+
+  return I.get();
+}
+
 namespace {
 
 /// SystemZ Code Generator Pass Configuration Options.
Index: llvm/test/CodeGen/SystemZ/args-07.ll
===================================================================
--- llvm/test/CodeGen/SystemZ/args-07.ll
+++ 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 }
 }
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" }
Index: llvm/test/CodeGen/SystemZ/soft-float-03.ll
===================================================================
--- /dev/null
+++ llvm/test/CodeGen/SystemZ/soft-float-03.ll
@@ -0,0 +1,23 @@
+; RUN: llc -mcpu=z13 -mattr=soft-float -O3 < %s | FileCheck %s
+;
+; Check that "use-soft-float"="true" implies "-vector", and that no vector
+; registers or instructions are used in either case.
+
+define <2 x i64> @f0(<2 x i64> %dummy, <2 x i64> %val1, <2 x i64> %val2) #0 {
+; CHECK-LABEL: f0:
+; CHECK-NOT: vag
+; CHECK-NOT: %v
+  %res = add <2 x i64> %val1, %val2
+  ret <2 x i64> %res
+}
+
+define <2 x i64> @f1(<2 x i64> %dummy, <2 x i64> %val1, <2 x i64> %val2) #1 {
+; CHECK-LABEL: f1:
+; CHECK-NOT: vag
+; CHECK-NOT: %v
+  %res = add <2 x i64> %val1, %val2
+  ret <2 x i64> %res
+}
+
+attributes #0 = { "target-features"="-vector" }
+attributes #1 = { "use-soft-float"="true" }
Index: llvm/test/CodeGen/SystemZ/soft-float-04.ll
===================================================================
--- /dev/null
+++ llvm/test/CodeGen/SystemZ/soft-float-04.ll
@@ -0,0 +1,25 @@
+; RUN: llc -mcpu=z14 -O3 < %s  | FileCheck %s
+;
+; Check that this function does not result in a s390.tdc intrinsic with
+; soft-float (which cannot be handled by SoftenFloatOperand).
+
+define void @fun(float %arg) #0 {
+; 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
+}
+
+attributes #0 = { "use-soft-float"="true" }
+
Index: llvm/test/CodeGen/SystemZ/soft-float-args.ll
===================================================================
--- /dev/null
+++ 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, <double 1.000000e+00, double 1.000000e+00>
+  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, <float 1.000000e+00, float 1.000000e+00>
+  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, <i64 1, i64 1>
+  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, <i32 1, i32 1>
+  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
+}
Index: llvm/test/CodeGen/SystemZ/soft-float-inline-asm-01.ll
===================================================================
--- /dev/null
+++ 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 "blah $0", "={f4},0" (float 1.0)
+  ret float %ret
+}
+
+; CHECK: LLVM ERROR: Inline asm is using an fp/vector reg with soft-float
Index: llvm/test/CodeGen/SystemZ/soft-float-inline-asm-02.ll
===================================================================
--- /dev/null
+++ 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 "blah $0", "=f,0" (float 1.0)
+  ret float %ret
+}
+
+; CHECK: LLVM ERROR: Inline asm is using an fp/vector reg with soft-float
Index: llvm/test/CodeGen/SystemZ/soft-float-inline-asm-03.ll
===================================================================
--- /dev/null
+++ 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 "blah $0", "={v4},0" (<2 x i64> zeroinitializer)
+  ret <2 x i64> %ret
+}
+
+; CHECK: LLVM ERROR: Inline asm is using an fp/vector reg with soft-float
Index: llvm/test/CodeGen/SystemZ/soft-float-inline-asm-04.ll
===================================================================
--- /dev/null
+++ llvm/test/CodeGen/SystemZ/soft-float-inline-asm-04.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 "blah $0", "=v,0" (<2 x i64> zeroinitializer)
+  ret <2 x i64> %ret
+}
+
+; CHECK: LLVM ERROR: Inline asm is using an fp/vector reg with soft-float
Index: llvm/test/CodeGen/SystemZ/vec-args-06.ll
===================================================================
--- llvm/test/CodeGen/SystemZ/vec-args-06.ll
+++ 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> <double 1.0, double 1.1>,
@@ -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> }
Index: llvm/test/CodeGen/SystemZ/vec-args-07.ll
===================================================================
--- llvm/test/CodeGen/SystemZ/vec-args-07.ll
+++ 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>,