[Driver] Incorporate -mfloat-abi in the computed triple on ARM

LLVM assumes that when it creates a call to a C library function it
can use the C calling convention. On ARM the effective calling
convention is determined from the target triple, however using
-mfloat-abi=hard on ARM means that calls to (and definitions of) C
library functions use the arm_aapcs_vfpcc calling convention which can
result in a mismatch.

Fix this by incorporating -mfloat-abi into the target triple, similar
to how -mbig-endian and -march/-mcpu are. This only works for EABI
targets and not Android or iOS, but there the float abi is fixed so
instead give an error.

Fixes PR45524

Differential Revision: https://reviews.llvm.org/D89573
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 8991216..9fa53ce 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -787,6 +787,37 @@
     }
     Triple.setArchName(ArchName + Suffix.str());
 
+    bool isHardFloat =
+        (arm::getARMFloatABI(getDriver(), Triple, Args) == arm::FloatABI::Hard);
+    switch (Triple.getEnvironment()) {
+    case Triple::GNUEABI:
+    case Triple::GNUEABIHF:
+      Triple.setEnvironment(isHardFloat ? Triple::GNUEABIHF : Triple::GNUEABI);
+      break;
+    case Triple::EABI:
+    case Triple::EABIHF:
+      Triple.setEnvironment(isHardFloat ? Triple::EABIHF : Triple::EABI);
+      break;
+    case Triple::MuslEABI:
+    case Triple::MuslEABIHF:
+      Triple.setEnvironment(isHardFloat ? Triple::MuslEABIHF
+                                        : Triple::MuslEABI);
+      break;
+    default: {
+      arm::FloatABI DefaultABI = arm::getDefaultFloatABI(Triple);
+      if (DefaultABI != arm::FloatABI::Invalid &&
+          isHardFloat != (DefaultABI == arm::FloatABI::Hard)) {
+        Arg *ABIArg =
+            Args.getLastArg(options::OPT_msoft_float, options::OPT_mhard_float,
+                            options::OPT_mfloat_abi_EQ);
+        assert(ABIArg && "Non-default float abi expected to be from arg");
+        D.Diag(diag::err_drv_unsupported_opt_for_target)
+            << ABIArg->getAsString(Args) << Triple.getTriple();
+      }
+      break;
+    }
+    }
+
     return Triple.getTriple();
   }
   }
diff --git a/clang/lib/Driver/ToolChains/Arch/ARM.cpp b/clang/lib/Driver/ToolChains/Arch/ARM.cpp
index d74d5db..309a729 100644
--- a/clang/lib/Driver/ToolChains/Arch/ARM.cpp
+++ b/clang/lib/Driver/ToolChains/Arch/ARM.cpp
@@ -134,6 +134,7 @@
   // The backend is hardwired to assume AAPCS for M-class processors, ensure
   // the frontend matches that.
   return T.getEnvironment() == llvm::Triple::EABI ||
+         T.getEnvironment() == llvm::Triple::EABIHF ||
          T.getOS() == llvm::Triple::UnknownOS || isARMMProfile(T);
 }
 
@@ -160,11 +161,73 @@
   return arm::getARMFloatABI(TC.getDriver(), TC.getEffectiveTriple(), Args);
 }
 
+arm::FloatABI arm::getDefaultFloatABI(const llvm::Triple &Triple) {
+  auto SubArch = getARMSubArchVersionNumber(Triple);
+  switch (Triple.getOS()) {
+  case llvm::Triple::Darwin:
+  case llvm::Triple::MacOSX:
+  case llvm::Triple::IOS:
+  case llvm::Triple::TvOS:
+    // Darwin defaults to "softfp" for v6 and v7.
+    if (Triple.isWatchABI())
+      return FloatABI::Hard;
+    else
+      return (SubArch == 6 || SubArch == 7) ? FloatABI::SoftFP : FloatABI::Soft;
+
+  case llvm::Triple::WatchOS:
+    return FloatABI::Hard;
+
+  // FIXME: this is invalid for WindowsCE
+  case llvm::Triple::Win32:
+    return FloatABI::Hard;
+
+  case llvm::Triple::NetBSD:
+    switch (Triple.getEnvironment()) {
+    case llvm::Triple::EABIHF:
+    case llvm::Triple::GNUEABIHF:
+      return FloatABI::Hard;
+    default:
+      return FloatABI::Soft;
+    }
+    break;
+
+  case llvm::Triple::FreeBSD:
+    switch (Triple.getEnvironment()) {
+    case llvm::Triple::GNUEABIHF:
+      return FloatABI::Hard;
+    default:
+      // FreeBSD defaults to soft float
+      return FloatABI::Soft;
+    }
+    break;
+
+  case llvm::Triple::OpenBSD:
+    return FloatABI::SoftFP;
+
+  default:
+    switch (Triple.getEnvironment()) {
+    case llvm::Triple::GNUEABIHF:
+    case llvm::Triple::MuslEABIHF:
+    case llvm::Triple::EABIHF:
+      return FloatABI::Hard;
+    case llvm::Triple::GNUEABI:
+    case llvm::Triple::MuslEABI:
+    case llvm::Triple::EABI:
+      // EABI is always AAPCS, and if it was not marked 'hard', it's softfp
+      return FloatABI::SoftFP;
+    case llvm::Triple::Android:
+      return (SubArch >= 7) ? FloatABI::SoftFP : FloatABI::Soft;
+    default:
+      return FloatABI::Invalid;
+    }
+  }
+  return FloatABI::Invalid;
+}
+
 // Select the float ABI as determined by -msoft-float, -mhard-float, and
 // -mfloat-abi=.
 arm::FloatABI arm::getARMFloatABI(const Driver &D, const llvm::Triple &Triple,
                                   const ArgList &Args) {
-  auto SubArch = getARMSubArchVersionNumber(Triple);
   arm::FloatABI ABI = FloatABI::Invalid;
   if (Arg *A =
           Args.getLastArg(options::OPT_msoft_float, options::OPT_mhard_float,
@@ -184,95 +247,23 @@
         ABI = FloatABI::Soft;
       }
     }
-
-    // It is incorrect to select hard float ABI on MachO platforms if the ABI is
-    // "apcs-gnu".
-    if (Triple.isOSBinFormatMachO() && !useAAPCSForMachO(Triple) &&
-        ABI == FloatABI::Hard) {
-      D.Diag(diag::err_drv_unsupported_opt_for_target) << A->getAsString(Args)
-                                                       << Triple.getArchName();
-    }
   }
 
   // If unspecified, choose the default based on the platform.
+  if (ABI == FloatABI::Invalid)
+    ABI = arm::getDefaultFloatABI(Triple);
+
   if (ABI == FloatABI::Invalid) {
-    switch (Triple.getOS()) {
-    case llvm::Triple::Darwin:
-    case llvm::Triple::MacOSX:
-    case llvm::Triple::IOS:
-    case llvm::Triple::TvOS: {
-      // Darwin defaults to "softfp" for v6 and v7.
-      ABI = (SubArch == 6 || SubArch == 7) ? FloatABI::SoftFP : FloatABI::Soft;
-      ABI = Triple.isWatchABI() ? FloatABI::Hard : ABI;
-      break;
-    }
-    case llvm::Triple::WatchOS:
+    // Assume "soft", but warn the user we are guessing.
+    if (Triple.isOSBinFormatMachO() &&
+        Triple.getSubArch() == llvm::Triple::ARMSubArch_v7em)
       ABI = FloatABI::Hard;
-      break;
+    else
+      ABI = FloatABI::Soft;
 
-    // FIXME: this is invalid for WindowsCE
-    case llvm::Triple::Win32:
-      ABI = FloatABI::Hard;
-      break;
-
-    case llvm::Triple::NetBSD:
-      switch (Triple.getEnvironment()) {
-      case llvm::Triple::EABIHF:
-      case llvm::Triple::GNUEABIHF:
-        ABI = FloatABI::Hard;
-        break;
-      default:
-        ABI = FloatABI::Soft;
-        break;
-      }
-      break;
-
-    case llvm::Triple::FreeBSD:
-      switch (Triple.getEnvironment()) {
-      case llvm::Triple::GNUEABIHF:
-        ABI = FloatABI::Hard;
-        break;
-      default:
-        // FreeBSD defaults to soft float
-        ABI = FloatABI::Soft;
-        break;
-      }
-      break;
-
-    case llvm::Triple::OpenBSD:
-      ABI = FloatABI::SoftFP;
-      break;
-
-    default:
-      switch (Triple.getEnvironment()) {
-      case llvm::Triple::GNUEABIHF:
-      case llvm::Triple::MuslEABIHF:
-      case llvm::Triple::EABIHF:
-        ABI = FloatABI::Hard;
-        break;
-      case llvm::Triple::GNUEABI:
-      case llvm::Triple::MuslEABI:
-      case llvm::Triple::EABI:
-        // EABI is always AAPCS, and if it was not marked 'hard', it's softfp
-        ABI = FloatABI::SoftFP;
-        break;
-      case llvm::Triple::Android:
-        ABI = (SubArch >= 7) ? FloatABI::SoftFP : FloatABI::Soft;
-        break;
-      default:
-        // Assume "soft", but warn the user we are guessing.
-        if (Triple.isOSBinFormatMachO() &&
-            Triple.getSubArch() == llvm::Triple::ARMSubArch_v7em)
-          ABI = FloatABI::Hard;
-        else
-          ABI = FloatABI::Soft;
-
-        if (Triple.getOS() != llvm::Triple::UnknownOS ||
-            !Triple.isOSBinFormatMachO())
-          D.Diag(diag::warn_drv_assuming_mfloat_abi_is) << "soft";
-        break;
-      }
-    }
+    if (Triple.getOS() != llvm::Triple::UnknownOS ||
+        !Triple.isOSBinFormatMachO())
+      D.Diag(diag::warn_drv_assuming_mfloat_abi_is) << "soft";
   }
 
   assert(ABI != FloatABI::Invalid && "must select an ABI");
diff --git a/clang/lib/Driver/ToolChains/Arch/ARM.h b/clang/lib/Driver/ToolChains/Arch/ARM.h
index 0ba1a59..091c09b 100644
--- a/clang/lib/Driver/ToolChains/Arch/ARM.h
+++ b/clang/lib/Driver/ToolChains/Arch/ARM.h
@@ -47,6 +47,7 @@
   Hard,
 };
 
+FloatABI getDefaultFloatABI(const llvm::Triple &Triple);
 FloatABI getARMFloatABI(const ToolChain &TC, const llvm::opt::ArgList &Args);
 FloatABI getARMFloatABI(const Driver &D, const llvm::Triple &Triple,
                         const llvm::opt::ArgList &Args);
diff --git a/clang/test/Driver/arm-float-abi-lto.c b/clang/test/Driver/arm-float-abi-lto.c
new file mode 100644
index 0000000..83c2435
--- /dev/null
+++ b/clang/test/Driver/arm-float-abi-lto.c
@@ -0,0 +1,63 @@
+// REQUIRES: arm-registered-target
+
+// RUN: %clang --target=arm-none-eabi -mcpu=cortex-m33 -mfloat-abi=hard -O1 %s -S -o - -emit-llvm -DCALL_LIB -DDEFINE_LIB | FileCheck %s
+
+// RUN: %clang --target=arm-none-eabi -mcpu=cortex-m33 -mfloat-abi=hard -O1 %s -flto=full -c -o %t.call_full.bc -DCALL_LIB
+// RUN: %clang --target=arm-none-eabi -mcpu=cortex-m33 -mfloat-abi=hard -O1 %s -flto=full -c -o %t.define_full.bc -DDEFINE_LIB
+// RUN: llvm-lto2 run -o %t.lto_full -save-temps %t.call_full.bc %t.define_full.bc \
+// RUN:  -r %t.call_full.bc,fn,px \
+// RUN:  -r %t.call_full.bc,fwrite,l \
+// RUN:  -r %t.call_full.bc,putchar,l \
+// RUN:  -r %t.call_full.bc,stdout,px \
+// RUN:  -r %t.define_full.bc,fwrite,px \
+// RUN:  -r %t.define_full.bc,putchar,px \
+// RUN:  -r %t.define_full.bc,otherfn,px
+// RUN: llvm-dis %t.lto_full.0.4.opt.bc -o - | FileCheck %s
+
+// RUN: %clang --target=arm-none-eabi -mcpu=cortex-m33 -mfloat-abi=hard -O1 %s -flto=thin -c -o %t.call_thin.bc -DCALL_LIB
+// RUN: %clang --target=arm-none-eabi -mcpu=cortex-m33 -mfloat-abi=hard -O1 %s -flto=thin -c -o %t.define_thin.bc -DDEFINE_LIB
+// RUN: llvm-lto2 run -o %t.lto_thin -save-temps %t.call_thin.bc %t.define_thin.bc \
+// RUN:  -r %t.call_thin.bc,fn,px \
+// RUN:  -r %t.call_thin.bc,fwrite,l \
+// RUN:  -r %t.call_thin.bc,putchar,l \
+// RUN:  -r %t.call_thin.bc,stdout,px \
+// RUN:  -r %t.define_thin.bc,fwrite,px \
+// RUN:  -r %t.define_thin.bc,putchar,px \
+// RUN:  -r %t.define_thin.bc,otherfn,px
+// RUN: llvm-dis %t.lto_thin.1.4.opt.bc -o - | FileCheck %s
+
+// We expect that the fprintf is optimised to fwrite, and the printf is
+// optimised to putchar. Check that we don't have a mismatch in calling
+// conventions causing the call to be replaced by a trap.
+// CHECK-LABEL: define{{.*}}void @fn()
+// CHECK-NOT: call void @llvm.trap()
+
+typedef struct FILE FILE;
+typedef unsigned int size_t;
+extern FILE *stdout;
+extern int fprintf(FILE *, const char *, ...);
+extern int printf(const char *, ...);
+extern void otherfn(const void *);
+
+#ifdef CALL_LIB
+
+void fn() {
+  fprintf(stdout, "hello world");
+  printf("a");
+}
+
+#endif
+
+#ifdef DEFINE_LIB
+
+size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) {
+  otherfn(ptr);
+  return 0;
+}
+
+int putchar(int c) {
+  otherfn(&c);
+  return 0;
+}
+
+#endif
diff --git a/clang/test/Driver/arm-float-abi.c b/clang/test/Driver/arm-float-abi.c
index 9a76d1e..74ba3fd 100644
--- a/clang/test/Driver/arm-float-abi.c
+++ b/clang/test/Driver/arm-float-abi.c
@@ -2,7 +2,7 @@
 // RUN: %clang %s -target armv7-apple-ios -mfloat-abi=softfp -### 2>&1 | FileCheck -check-prefix=NOERROR %s
 // RUN: %clang %s -arch armv7 -target thumbv7-apple-darwin-eabi -mfloat-abi=hard -### 2>&1 | FileCheck -check-prefix=NOERROR %s
 
-// ARMV7-ERROR: unsupported option '-mfloat-abi=hard' for target 'thumbv7'
+// ARMV7-ERROR: unsupported option '-mfloat-abi=hard' for target 'thumbv7-apple-ios'
 // NOERROR-NOT: unsupported option
 
 // RUN: %clang -target armv7-linux-androideabi21 %s -### -c 2>&1 \
@@ -14,3 +14,25 @@
 // RUN:   | FileCheck --check-prefix=CHECK-ARM8-ANDROID %s
 // CHECK-ARM8-ANDROID-NOT: "-target-feature" "+soft-float"
 // CHECK-ARM8-ANDROID: "-target-feature" "+soft-float-abi"
+
+// RUN: not %clang -target armv7-linux-androideabi21 %s -S -o - -mfloat-abi=hard 2>&1 \
+// RUN:   | FileCheck --check-prefix=CHECK-ANDROID-ERROR %s
+// CHECK-ANDROID-ERROR: unsupported option '-mfloat-abi=hard' for target 'armv7-unknown-linux-android21'
+
+// RUN: %clang -target armv7-linux-androideabi21 %s -S -o - -mfloat-abi=soft 2>&1 \
+// RUN:   | FileCheck --check-prefix=CHECK-ANDROID-NOERROR %s
+// RUN: %clang -target armv7-linux-androideabi21 %s -S -o - -mfloat-abi=softfp 2>&1 \
+// RUN:   | FileCheck --check-prefix=CHECK-ANDROID-NOERROR %s
+// CHECK-ANDROID-NOERROR-NOT: unsupported option
+
+// RUN: not %clang -target armv7-apple-watchos4 %s -S -o - -mfloat-abi=soft 2>&1 \
+// RUN:   | FileCheck --check-prefix=CHECK-WATCHOS-ERROR1 %s
+// CHECK-WATCHOS-ERROR1: unsupported option '-mfloat-abi=soft' for target 'thumbv7-apple-watchos4'
+
+// RUN: not %clang -target armv7-apple-watchos4 %s -S -o - -mfloat-abi=softfp 2>&1 \
+// RUN:   | FileCheck --check-prefix=CHECK-WATCHOS-ERROR2 %s
+// CHECK-WATCHOS-ERROR2: unsupported option '-mfloat-abi=softfp' for target 'thumbv7-apple-watchos4'
+
+// RUN: %clang -target armv7-apple-watchos4 %s -S -o - -mfloat-abi=hard 2>&1 \
+// RUN:   | FileCheck --check-prefix=CHECK-WATCHOS-NOERROR %s
+// CHECK-WATCHOS-NOERROR-NOT: unsupported option
diff --git a/clang/test/Driver/arm-triple.c b/clang/test/Driver/arm-triple.c
new file mode 100644
index 0000000..fa9f7b1
--- /dev/null
+++ b/clang/test/Driver/arm-triple.c
@@ -0,0 +1,48 @@
+// RUN: %clang -print-effective-triple \
+// RUN:   --target=arm-none-eabi \
+// RUN:   | FileCheck %s --check-prefix=CHECK-DEFAULT
+// RUN: %clang -print-effective-triple \
+// RUN:   --target=armeb-none-eabi -mlittle-endian \
+// RUN:   | FileCheck %s --check-prefix=CHECK-DEFAULT
+// RUN: %clang -print-effective-triple \
+// RUN:   --target=arm-none-eabihf -march=armv4t -mfloat-abi=softfp \
+// RUN:   | FileCheck %s --check-prefix=CHECK-DEFAULT
+// CHECK-DEFAULT: armv4t-none-unknown-eabi
+
+// RUN: %clang -print-effective-triple \
+// RUN:   --target=armeb-none-eabi \
+// RUN:   | FileCheck %s --check-prefix=CHECK-EB
+// RUN: %clang -print-effective-triple \
+// RUN:   --target=arm-none-eabi -mbig-endian \
+// RUN:   | FileCheck %s --check-prefix=CHECK-EB
+// CHECK-EB: armebv4t-none-unknown-eabi
+
+// RUN: %clang -print-effective-triple \
+// RUN:   --target=arm-none-eabihf -march=armv4t \
+// RUN:   | FileCheck %s --check-prefix=CHECK-HF
+// RUN: %clang -print-effective-triple \
+// RUN:   --target=arm-none-eabi -mfloat-abi=hard \
+// RUN:   | FileCheck %s --check-prefix=CHECK-HF
+// CHECK-HF: armv4t-none-unknown-eabihf
+
+// RUN: %clang -print-effective-triple \
+// RUN:   --target=armeb-none-eabihf -march=armv4t \
+// RUN:   | FileCheck %s --check-prefix=CHECK-EB-HF
+// RUN: %clang -print-effective-triple \
+// RUN:   --target=armeb-none-eabi -mfloat-abi=hard \
+// RUN:   | FileCheck %s --check-prefix=CHECK-EB-HF
+// RUN: %clang -print-effective-triple -march=armv4t \
+// RUN:   --target=arm-none-eabihf -mbig-endian \
+// RUN:   | FileCheck %s --check-prefix=CHECK-EB-HF
+// RUN: %clang -print-effective-triple \
+// RUN:   --target=arm-none-eabi -mbig-endian -mfloat-abi=hard \
+// RUN:   | FileCheck %s --check-prefix=CHECK-EB-HF
+// CHECK-EB-HF: armebv4t-none-unknown-eabihf
+
+// RUN: %clang -print-effective-triple \
+// RUN:   --target=arm-none-eabi -march=armv8m.main -mbig-endian -mfloat-abi=hard \
+// RUN:   | FileCheck %s --check-prefix=CHECK-V8M-EB-HF
+// RUN: %clang -print-effective-triple \
+// RUN:   --target=arm-none-eabi -mcpu=cortex-m33 -mbig-endian -mfloat-abi=hard \
+// RUN:   | FileCheck %s --check-prefix=CHECK-V8M-EB-HF
+// CHECK-V8M-EB-HF: thumbebv8m.main-none-unknown-eabihf
diff --git a/clang/test/Driver/windows-thumbv7em.cpp b/clang/test/Driver/windows-thumbv7em.cpp
index 5d7c00b..94d18e4 100644
--- a/clang/test/Driver/windows-thumbv7em.cpp
+++ b/clang/test/Driver/windows-thumbv7em.cpp
@@ -1,8 +1,8 @@
 // RUN: %clang -target thumb-none-windows-eabi-coff -mcpu=cortex-m7 -### -c %s 2>&1 \
 // RUN: | FileCheck %s --check-prefix CHECK-V7
-// CHECK-V7-NOT: error: the target architecture 'thumbv7em' is not supported by the target 'thumbv7em-none-windows-eabi'
+// CHECK-V7-NOT: error: the target architecture 'thumbv7em' is not supported by the target 'thumbv7em-none-windows-eabihf'
 
 // RUN: %clang -target thumb-none-windows-eabi-coff -mcpu=cortex-m1 -### -c %s 2>&1 \
 // RUN: | FileCheck %s --check-prefix CHECK-V6
-// CHECK-V6: error: the target architecture 'thumbv6m' is not supported by the target 'thumbv6m-none-windows-eabi'
+// CHECK-V6: error: the target architecture 'thumbv6m' is not supported by the target 'thumbv6m-none-windows-eabihf'