[llvm] 2d739f9 - [ARM] Allocatable Global Register Variables for ARM

Anna Welker via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 19 07:32:17 PST 2019


Hi Russell,

thanks for fixing this and sorry for any inconvenience it might have caused.
We are currently discussing some other aspects of the patch, so some more things might be moved or fixed, but in the interim your change solves the problem fine.

Many thanks,
Anna Welker

________________________________
From: Russell Gallop <russell.gallop at gmail.com>
Sent: Monday, November 18, 2019 15:07
To: Anna Welker <Anna.Welker at arm.com>; Anna Welker <llvmlistbot at llvm.org>
Cc: LLVM Commits <llvm-commits at lists.llvm.org>
Subject: Re: [llvm] 2d739f9 - [ARM] Allocatable Global Register Variables for ARM

Hi Anna,

The reserve_global_reg.ll test was failing on some buildbots which were only building X86 target (e.g. http://lab.llvm.org:8011/builders/llvm-clang-lld-x86_64-scei-ps4-windows10pro-fast/builds/29248).

I pushed aea7578fade2563cb5ea60548914667b515c457a to fix this. Please can you confirm this change is correct?

Thanks
Russ

On Mon, 18 Nov 2019 at 10:08, Anna Welker via llvm-commits <llvm-commits at lists.llvm.org<mailto:llvm-commits at lists.llvm.org>> wrote:

Author: Anna Welker
Date: 2019-11-18T10:07:37Z
New Revision: 2d739f98d8a53e38bf9faa88cdb6b0c2a363fb77

URL: https://github.com/llvm/llvm-project/commit/2d739f98d8a53e38bf9faa88cdb6b0c2a363fb77
DIFF: https://github.com/llvm/llvm-project/commit/2d739f98d8a53e38bf9faa88cdb6b0c2a363fb77.diff

LOG: [ARM] Allocatable Global Register Variables for ARM

      Provides support for using r6-r11 as globally scoped
      register variables. This requires a -ffixed-rN flag
      in order to reserve rN against general allocation.

      If for a given GRV declaration the corresponding flag
      is not found, or the the register in question is the
      target's FP, we fail with a diagnostic.

      Differential Revision: https://reviews.llvm.org/D68862

Added:
    clang/test/Driver/arm-reserved-reg-options.c
    clang/test/Sema/arm-global-regs.c
    llvm/test/CodeGen/ARM/reg-alloc-fixed-r6-vla.ll
    llvm/test/CodeGen/ARM/reg-alloc-with-fixed-reg-r6-modified.ll
    llvm/test/CodeGen/ARM/reg-alloc-with-fixed-reg-r6.ll
    llvm/test/CodeGen/ARM/reg-alloc-wout-fixed-regs.ll
    llvm/test/CodeGen/Thumb/callee_save_reserved.ll
    llvm/test/Feature/reserve_global_reg.ll

Modified:
    clang/docs/ClangCommandLineReference.rst
    clang/include/clang/Basic/DiagnosticDriverKinds.td
    clang/include/clang/Basic/DiagnosticGroups.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Basic/TargetInfo.h
    clang/include/clang/Driver/Options.td
    clang/lib/Basic/Targets/ARM.cpp
    clang/lib/Basic/Targets/ARM.h
    clang/lib/Driver/ToolChains/Arch/ARM.cpp
    clang/lib/Sema/SemaDecl.cpp
    llvm/lib/Target/ARM/ARM.td
    llvm/lib/Target/ARM/ARMAsmPrinter.cpp
    llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp
    llvm/lib/Target/ARM/ARMFrameLowering.cpp
    llvm/lib/Target/ARM/ARMISelLowering.cpp
    llvm/lib/Target/ARM/ARMSubtarget.cpp
    llvm/lib/Target/ARM/ARMSubtarget.h
    llvm/lib/Target/ARM/ARMTargetTransformInfo.h

Removed:



################################################################################
diff  --git a/clang/docs/ClangCommandLineReference.rst b/clang/docs/ClangCommandLineReference.rst
index e8d561fae956..492eec71f2e4 100644
--- a/clang/docs/ClangCommandLineReference.rst
+++ b/clang/docs/ClangCommandLineReference.rst
@@ -2430,10 +2430,31 @@ Enable XNACK (AMDGPU only)

 ARM
 ---
+
+.. option:: -ffixed-r6
+
+Reserve the r6 register (ARM only)
+
+.. option:: -ffixed-r7
+
+Reserve the r7 register (ARM only)
+
+.. option:: -ffixed-r8
+
+Reserve the r8 register (ARM only)
+
 .. option:: -ffixed-r9

 Reserve the r9 register (ARM only)

+.. option:: -ffixed-r10
+
+Reserve the r10 register (ARM only)
+
+.. option:: -ffixed-r11
+
+Reserve the r11 register (ARM only)
+
 .. option:: -mexecute-only, -mno-execute-only, -mpure-code

 Disallow generation of data access to code sections (ARM only)

diff  --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index 5ff03e133563..0e309909030e 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -464,6 +464,10 @@ def warn_drv_msp430_hwmult_no_device : Warning<"no MCU device specified, but "
   "specify a MSP430 device, or -mhwmult to set hardware multiply type "
   "explicitly.">, InGroup<InvalidCommandLineArgument>;

+// Frame pointer reservation.
+def err_reserved_frame_pointer : Error<
+  "'%0' has been specified but '%1' is used as the frame pointer for this target">;
+
 def warn_drv_libstdcxx_not_found : Warning<
   "include path for libstdc++ headers not found; pass '-stdlib=libc++' on the "
   "command line to use the libc++ standard library instead">,

diff  --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index bc66a8253074..6b83bf59ea89 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1115,3 +1115,6 @@ def CrossTU : DiagGroup<"ctu">;
 def CTADMaybeUnsupported : DiagGroup<"ctad-maybe-unsupported">;

 def FortifySource : DiagGroup<"fortify-source">;
+
+// Register reservation.
+def FixedRegs : DiagGroup<"fixed-registers">;

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index beb25c5a0892..49ad7c7cc462 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7716,6 +7716,8 @@ let CategoryName = "Inline Assembly Issue" in {
   def err_asm_unknown_register_name : Error<"unknown register name '%0' in asm">;
   def err_asm_invalid_global_var_reg : Error<"register '%0' unsuitable for "
     "global register variables on this target">;
+  def err_asm_missing_fixed_reg_opt : Error<"-ffixed-%0 is required for "
+    "global named register variable declaration">;
   def err_asm_register_size_mismatch : Error<"size of register '%0' does not "
     "match variable size">;
   def err_asm_bad_register_type : Error<"bad type for named register variable">;

diff  --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 33cecdadc686..cc83f4c34c14 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -938,6 +938,12 @@ class TargetInfo : public virtual TransferrableTargetInfo,
     return true;
   }

+  /// Check if the register is reserved globally
+  ///
+  /// This function returns true if the register passed in RegName is reserved
+  /// using the corresponding -ffixed-RegName option.
+  virtual bool isRegisterReservedGlobally(StringRef) const { return true; }
+
   // validateOutputConstraint, validateInputConstraint - Checks that
   // a constraint is valid and provides information about it.
   // FIXME: These should return a real error instead of just true/false.

diff  --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 6087b13228dc..3fca181b8784 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2266,8 +2266,9 @@ def mrestrict_it: Flag<["-"], "mrestrict-it">, Group<m_arm_Features_Group>,
 def mno_restrict_it: Flag<["-"], "mno-restrict-it">, Group<m_arm_Features_Group>,
   HelpText<"Allow generation of deprecated IT blocks for ARMv8. It is off by default for ARMv8 Thumb mode">;
 def marm : Flag<["-"], "marm">, Alias<mno_thumb>;
-def ffixed_r9 : Flag<["-"], "ffixed-r9">, Group<m_arm_Features_Group>,
-  HelpText<"Reserve the r9 register (ARM only)">;
+foreach i = {6-11} in
+  def ffixed_r#i : Flag<["-"], "ffixed-r"#i>, Group<m_arm_Features_Group>,
+    HelpText<"Reserve the r"#i#" register (ARM only)">;
 def mno_movt : Flag<["-"], "mno-movt">, Group<m_arm_Features_Group>,
   HelpText<"Disallow use of movt/movw pairs (ARM only)">;
 def mcrc : Flag<["-"], "mcrc">, Group<m_Group>,

diff  --git a/clang/lib/Basic/Targets/ARM.cpp b/clang/lib/Basic/Targets/ARM.cpp
index 437a77afdc99..92e5e26eba3c 100644
--- a/clang/lib/Basic/Targets/ARM.cpp
+++ b/clang/lib/Basic/Targets/ARM.cpp
@@ -879,6 +879,38 @@ ArrayRef<TargetInfo::GCCRegAlias> ARMTargetInfo::getGCCRegAliases() const {
   return llvm::makeArrayRef(GCCRegAliases);
 }

+bool ARMTargetInfo::validateGlobalRegisterVariable(
+    StringRef RegName, unsigned RegSize, bool &HasSizeMismatch) const {
+  bool isValid = llvm::StringSwitch<bool>(RegName)
+                     .Case("r6", true)
+                     .Case("r7", true)
+                     .Case("r8", true)
+                     .Case("r9", true)
+                     .Case("r10", true)
+                     .Case("r11", true)
+                     .Case("sp", true)
+                     .Default(false);
+  HasSizeMismatch = false;
+  return isValid;
+}
+
+bool ARMTargetInfo::isRegisterReservedGlobally(StringRef RegName) const {
+  // The "sp" register does not have a -ffixed-sp option,
+  // so reserve it unconditionally.
+  if (RegName.equals("sp"))
+    return true;
+
+  // reserve rN (N:6-11) registers only if the corresponding
+  // +reserve-rN feature is found
+  const std::vector<std::string> &Features = getTargetOpts().Features;
+  const std::string SearchFeature = "+reserve-" + RegName.str();
+  for (const std::string &Feature : Features) {
+    if (Feature.compare(SearchFeature) == 0)
+      return true;
+  }
+  return false;
+}
+
 bool ARMTargetInfo::validateAsmConstraint(
     const char *&Name, TargetInfo::ConstraintInfo &Info) const {
   switch (*Name) {

diff  --git a/clang/lib/Basic/Targets/ARM.h b/clang/lib/Basic/Targets/ARM.h
index ce87a6265934..90fb20f8f7a5 100644
--- a/clang/lib/Basic/Targets/ARM.h
+++ b/clang/lib/Basic/Targets/ARM.h
@@ -161,6 +161,9 @@ class LLVM_LIBRARY_VISIBILITY ARMTargetInfo : public TargetInfo {

   ArrayRef<const char *> getGCCRegNames() const override;
   ArrayRef<TargetInfo::GCCRegAlias> getGCCRegAliases() const override;
+  bool validateGlobalRegisterVariable(StringRef RegName, unsigned RegSize,
+                                      bool &HasSizeMismatch) const override;
+  bool isRegisterReservedGlobally(StringRef RegName) const override;
   bool validateAsmConstraint(const char *&Name,
                              TargetInfo::ConstraintInfo &Info) const override;
   std::string convertConstraint(const char *&Constraint) const override;

diff  --git a/clang/lib/Driver/ToolChains/Arch/ARM.cpp b/clang/lib/Driver/ToolChains/Arch/ARM.cpp
index 68a57310ad40..cca47722c204 100644
--- a/clang/lib/Driver/ToolChains/Arch/ARM.cpp
+++ b/clang/lib/Driver/ToolChains/Arch/ARM.cpp
@@ -592,11 +592,39 @@ void arm::getARMTargetFeatures(const ToolChain &TC,
       Features.push_back("+strict-align");
   }

-  // llvm does not support reserving registers in general. There is support
-  // for reserving r9 on ARM though (defined as a platform-specific register
-  // in ARM EABI).
-  if (Args.hasArg(options::OPT_ffixed_r9))
-    Features.push_back("+reserve-r9");
+  // Do not allow r9 reservation with -frwpi.
+  if (Args.hasArg(options::OPT_ffixed_r9) && Args.hasArg(options::OPT_frwpi)) {
+    Arg *A = Args.getLastArg(options::OPT_ffixed_r9);
+    Arg *B = Args.getLastArg(options::OPT_frwpi);
+    D.Diag(diag::err_opt_not_valid_with_opt)
+        << A->getAsString(Args) << B->getAsString(Args);
+  }
+
+  // The compiler can still use a FP in certain circumstances,
+  // even when frame pointer elimination is enabled. Thus we should
+  // not allow to reserve a target's FP register.
+  const llvm::opt::OptSpecifier RestrictFPOpt =
+      (Triple.isOSDarwin() || (!Triple.isOSWindows() && Triple.isThumb()))
+          ? options::OPT_ffixed_r7
+          : options::OPT_ffixed_r11;
+  if (Args.hasArg(RestrictFPOpt)) {
+    const std::string OptStr =
+        Args.getLastArg(RestrictFPOpt)->getAsString(Args);
+    const unsigned int SubStrIndex = strlen("ffixed-r");
+    D.Diag(diag::err_reserved_frame_pointer)
+        << OptStr << OptStr.substr(SubStrIndex);
+  }
+
+// Reservation of general purpose registers.
+#define HANDLE_FFIXED_R(n) \
+  if (Args.hasArg(options::OPT_ffixed_r##n)) \
+    Features.push_back("+reserve-r" #n)
+  HANDLE_FFIXED_R(6);
+  HANDLE_FFIXED_R(7);
+  HANDLE_FFIXED_R(8);
+  HANDLE_FFIXED_R(9);
+  HANDLE_FFIXED_R(10);
+  HANDLE_FFIXED_R(11);

   // The kext linker doesn't know how to deal with movw/movt.
   if (KernelOrKext || Args.hasArg(options::OPT_mno_movt))

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index aba7049b0a51..b469217108ce 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7009,6 +7009,8 @@ NamedDecl *Sema::ActOnVariableDeclarator(
           Diag(E->getExprLoc(), diag::err_asm_invalid_global_var_reg) << Label;
         else if (HasSizeMismatch)
           Diag(E->getExprLoc(), diag::err_asm_register_size_mismatch) << Label;
+        else if (!TI.isRegisterReservedGlobally(Label))
+          Diag(E->getExprLoc(), diag::err_asm_missing_fixed_reg_opt) << Label;
       }

       if (!R->isIntegralType(Context) && !R->isPointerType()) {

diff  --git a/clang/test/Driver/arm-reserved-reg-options.c b/clang/test/Driver/arm-reserved-reg-options.c
new file mode 100644
index 000000000000..e97c717d7e7e
--- /dev/null
+++ b/clang/test/Driver/arm-reserved-reg-options.c
@@ -0,0 +1,35 @@
+// ## FP ARM + Thumb
+// RUN: %clang -target arm-arm-none-eabi -### -ffixed-r11 -c %s 2>&1 | FileCheck -check-prefix=CHECK-ERROR-R11 %s
+// RUN: %clang -target arm-arm-none-eabi -### -ffixed-r7 -c %s 2>&1 | FileCheck -check-prefix=CHECK-NO-ERROR %s
+
+// RUN: %clang -target arm-arm-none-eabi -### -ffixed-r7 -mthumb -c %s 2>&1 | FileCheck -check-prefix=CHECK-ERROR-R7 %s
+// RUN: %clang -target arm-arm-none-eabi -### -ffixed-r11 -mthumb -c %s 2>&1 | FileCheck -check-prefix=CHECK-NO-ERROR %s
+
+// RUN: %clang -target thumbv6m-none-eabi -### -ffixed-r7 -c %s 2>&1 | FileCheck -check-prefix=CHECK-ERROR-R7 %s
+// RUN: %clang -target thumbv6m-none-eabi -### -ffixed-r11 -c %s 2>&1 | FileCheck -check-prefix=CHECK-NO-ERROR %s
+
+// ## FP Darwin (R7)
+// RUN: %clang -target armv6-apple-darwin9 -### -ffixed-r7 -c %s 2>&1 | FileCheck -check-prefix=CHECK-ERROR-R7 %s
+// RUN: %clang -target armv6-apple-darwin9 -### -ffixed-r11 -c %s 2>&1 | FileCheck -check-prefix=CHECK-NO-ERROR %s
+
+// RUN: %clang -target armv6-apple-ios3 -### -ffixed-r7 -c %s 2>&1 | FileCheck -check-prefix=CHECK-ERROR-R7 %s
+// RUN: %clang -target armv6-apple-ios3 -### -ffixed-r11 -c %s 2>&1 | FileCheck -check-prefix=CHECK-NO-ERROR %s
+
+// RUN: %clang -target armv7s-apple-darwin10 -### -ffixed-r7 -c %s 2>&1 | FileCheck -check-prefix=CHECK-ERROR-R7 %s
+// RUN: %clang -target armv7s-apple-darwin10 -### -ffixed-r11 -c %s 2>&1 | FileCheck -check-prefix=CHECK-NO-ERROR %s
+
+// ## FP Windows (R11)
+// RUN: %clang -target armv7-windows -### -ffixed-r11 -c %s 2>&1 | FileCheck -check-prefix=CHECK-ERROR-R11 %s
+// RUN: %clang -target armv7-windows -### -ffixed-r7 -c %s 2>&1 | FileCheck -check-prefix=CHECK-NO-ERROR %s
+
+// ## FRWPI (R9)
+// RUN: %clang -target arm-arm-none-eabi -### -frwpi -ffixed-r9 -c %s 2>&1 | FileCheck -check-prefix=CHECK-RESERVED-FRWPI-CONFLICT %s
+// RUN: %clang -target arm-arm-none-eabi -### -ffixed-r9 -c %s 2>&1 | FileCheck -check-prefix=CHECK-RESERVED-FRWPI-VALID %s
+// RUN: %clang -target arm-arm-none-eabi -### -frwpi -c %s 2>&1 | FileCheck -check-prefix=CHECK-RESERVED-FRWPI-VALID %s
+
+// CHECK-ERROR-R11: error: '-ffixed-r11' has been specified but 'r11' is used as the frame pointer for this target
+// CHECK-ERROR-R7: error: '-ffixed-r7' has been specified but 'r7' is used as the frame pointer for this target
+// CHECK-NO-ERROR-NOT: may still be used as a frame pointer
+
+// CHECK-RESERVED-FRWPI-CONFLICT: option '-ffixed-r9' cannot be specified with '-frwpi'
+// CHECK-RESERVED-FRWPI-VALID-NOT: option '-ffixed-r9' cannot be specified with '-frwpi'

diff  --git a/clang/test/Sema/arm-global-regs.c b/clang/test/Sema/arm-global-regs.c
new file mode 100644
index 000000000000..753cb60e6838
--- /dev/null
+++ b/clang/test/Sema/arm-global-regs.c
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -ffreestanding -fsyntax-only -target-feature +reserve-r9  -verify -triple arm-arm-none-eabi %s
+
+// Check a small subset of valid and invalid global register variable declarations.
+// Also check that for global register variables without -ffixed-reg options it throws an error.
+
+register unsigned arm_r3 __asm("r3"); //expected-error {{register 'r3' unsuitable for global register variables on this target}}
+
+register unsigned arm_r12 __asm("r12"); //expected-error {{register 'r12' unsuitable for global register variables on this target}}
+
+register unsigned arm_r5 __asm("r5"); //expected-error {{register 'r5' unsuitable for global register variables on this target}}
+
+register unsigned arm_r9 __asm("r9");
+
+register unsigned arm_r6 __asm("r6"); //expected-error {{-ffixed-r6 is required for global named register variable declaration}}
+
+register unsigned arm_r7 __asm("r7"); //expected-error {{-ffixed-r7 is required for global named register variable declaration}}
+
+register unsigned *parm_r7 __asm("r7"); //expected-error {{-ffixed-r7 is required for global named register variable declaration}}
+
+register unsigned arm_sp __asm("sp");

diff  --git a/llvm/lib/Target/ARM/ARM.td b/llvm/lib/Target/ARM/ARM.td
index 66bfd4c82e25..285dad1cf29a 100644
--- a/llvm/lib/Target/ARM/ARM.td
+++ b/llvm/lib/Target/ARM/ARM.td
@@ -391,9 +391,11 @@ def FeatureExecuteOnly    : SubtargetFeature<"execute-only",
                                              "Enable the generation of "
                                              "execute only code.">;

-def FeatureReserveR9      : SubtargetFeature<"reserve-r9", "ReserveR9", "true",
-                                             "Reserve R9, making it unavailable"
-                                             " as GPR">;
+foreach i = {6-11} in
+    def FeatureReserveR#i : SubtargetFeature<"reserve-r"#i,
+                                             "ReservedGPRegisters["#i#"]", "true",
+                                             "Reserve R"#i#", making it "
+                                             "unavailable as a GPR">;

 def FeatureNoMovt         : SubtargetFeature<"no-movt", "NoMovt", "true",
                                              "Don't use movt/movw pairs for "

diff  --git a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
index ed0969fa625b..10153dd2e395 100644
--- a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
+++ b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
@@ -752,7 +752,7 @@ void ARMAsmPrinter::emitAttributes() {
   if (STI.isRWPI())
     ATS.emitAttribute(ARMBuildAttrs::ABI_PCS_R9_use,
                       ARMBuildAttrs::R9IsSB);
-  else if (STI.isR9Reserved())
+  else if (STI.isGPRegisterReserved(9))
     ATS.emitAttribute(ARMBuildAttrs::ABI_PCS_R9_use,
                       ARMBuildAttrs::R9Reserved);
   else

diff  --git a/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp b/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp
index 4ace52b32e9f..afcdb648cbc8 100644
--- a/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp
+++ b/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp
@@ -198,9 +198,11 @@ getReservedRegs(const MachineFunction &MF) const {
     markSuperRegs(Reserved, getFramePointerReg(STI));
   if (hasBasePointer(MF))
     markSuperRegs(Reserved, BasePtr);
-  // Some targets reserve R9.
-  if (STI.isR9Reserved())
-    markSuperRegs(Reserved, ARM::R9);
+  for (size_t R = 0; R < ARM::GPRRegClass.getNumRegs(); ++R) {
+    if (STI.isGPRegisterReserved(R)) {
+      markSuperRegs(Reserved, ARM::R0 + R);
+    }
+  }
   // Reserve D16-D31 if the subtarget doesn't support them.
   if (!STI.hasD32()) {
     static_assert(ARM::D31 == ARM::D16 + 15, "Register list not consecutive!");
@@ -280,7 +282,7 @@ ARMBaseRegisterInfo::getRegPressureLimit(const TargetRegisterClass *RC,
   case ARM::GPRRegClassID: {
     bool HasFP = MF.getFrameInfo().isMaxCallFrameSizeComputed()
                  ? TFI->hasFP(MF) : true;
-    return 10 - HasFP - (STI.isR9Reserved() ? 1 : 0);
+    return 10 - HasFP - STI.getNumGPRegistersReserved();
   }
   case ARM::SPRRegClassID:  // Currently not used as 'rep' register class.
   case ARM::DPRRegClassID:
@@ -380,6 +382,11 @@ bool ARMBaseRegisterInfo::hasBasePointer(const MachineFunction &MF) const {
   const MachineFrameInfo &MFI = MF.getFrameInfo();
   const ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>();
   const ARMFrameLowering *TFI = getFrameLowering(MF);
+  const ARMSubtarget &STI = MF.getSubtarget<ARMSubtarget>();
+
+  // Disable base pointer R6 if -ffixed-r6 is used.
+  if (STI.isGPRegisterReserved(BasePtr - ARM::R0))
+    return false;

   // If we have stack realignment and VLAs, we have no pointer to use to
   // access the stack. If we have stack realignment, and a large call frame,
@@ -416,6 +423,7 @@ bool ARMBaseRegisterInfo::hasBasePointer(const MachineFunction &MF) const {
 bool ARMBaseRegisterInfo::canRealignStack(const MachineFunction &MF) const {
   const MachineRegisterInfo *MRI = &MF.getRegInfo();
   const ARMFrameLowering *TFI = getFrameLowering(MF);
+  const ARMSubtarget &STI = MF.getSubtarget<ARMSubtarget>();
   // We can't realign the stack if:
   // 1. Dynamic stack realignment is explicitly disabled,
   // 2. There are VLAs in the function and the base pointer is disabled.
@@ -425,6 +433,9 @@ bool ARMBaseRegisterInfo::canRealignStack(const MachineFunction &MF) const {
   // register allocation with frame pointer elimination, it is too late now.
   if (!MRI->canReserveReg(getFramePointerReg(MF.getSubtarget<ARMSubtarget>())))
     return false;
+  // Disable base pointer R6 if -ffixed-r6 is used.
+  if (STI.isGPRegisterReserved(BasePtr - ARM::R0))
+    return false;
   // We may also need a base pointer if there are dynamic allocas or stack
   // pointer adjustments around calls.
   if (TFI->hasReservedCallFrame(MF))

diff  --git a/llvm/lib/Target/ARM/ARMFrameLowering.cpp b/llvm/lib/Target/ARM/ARMFrameLowering.cpp
index 5428bd6c94b3..106894e28f03 100644
--- a/llvm/lib/Target/ARM/ARMFrameLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMFrameLowering.cpp
@@ -1704,6 +1704,19 @@ void ARMFrameLowering::determineCalleeSaves(MachineFunction &MF,
   const MCPhysReg *CSRegs = RegInfo->getCalleeSavedRegs(&MF);
   for (unsigned i = 0; CSRegs[i]; ++i) {
     unsigned Reg = CSRegs[i];
+    if (STI.isRWPI() && Reg == ARM::R9) {
+      // Paranoid check for use of R9 with RWPI. Clobbering R9 with -frwpi will
+      // emit warnings about undefined behaviour but maybe theres's a valid use
+      // case so on that basis allow it to be pushed/popped in the
+      // prologue/epilogue.
+    } else if (Reg > ARM::R0 && ARM::GPRRegClass.contains(Reg) &&
+               STI.isGPRegisterReserved(Reg - ARM::R0)) {
+      LLVM_DEBUG(dbgs() << printReg(Reg, TRI) << " has been reserved and"
+                        << " should not be allocatable"
+                        << " or spillable.\n");
+      SavedRegs.reset(Reg);
+      continue;
+    }
     bool Spilled = false;
     if (SavedRegs.test(Reg)) {
       Spilled = true;
@@ -1948,7 +1961,7 @@ void ARMFrameLowering::determineCalleeSaves(MachineFunction &MF,
           LLVM_DEBUG(dbgs() << printReg(Reg, TRI)
                             << " is saved low register, RegDeficit = "
                             << RegDeficit << "\n");
-        } else {
+        } else if (!STI.isGPRegisterReserved(Reg - ARM::R0)) {
           AvailableRegs.push_back(Reg);
           LLVM_DEBUG(
               dbgs()
@@ -1963,7 +1976,7 @@ void ARMFrameLowering::determineCalleeSaves(MachineFunction &MF,
           --RegDeficit;
           LLVM_DEBUG(dbgs() << "%r7 is saved low register, RegDeficit = "
                             << RegDeficit << "\n");
-        } else {
+        } else if (!STI.isGPRegisterReserved(7)) {
           AvailableRegs.push_back(ARM::R7);
           LLVM_DEBUG(
               dbgs()

diff  --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp
index c395a4feea70..b1d1d4fd5fc9 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp
@@ -5572,9 +5572,15 @@ SDValue ARMTargetLowering::LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const {
 Register ARMTargetLowering::getRegisterByName(const char* RegName, EVT VT,
                                               const MachineFunction &MF) const {
   Register Reg = StringSwitch<unsigned>(RegName)
-                       .Case("sp", ARM::SP)
-                       .Default(0);
-  if (Reg)
+                     .Case("r6", ARM::R6)
+                     .Case("r7", ARM::R7)
+                     .Case("r8", ARM::R8)
+                     .Case("r9", ARM::R9)
+                     .Case("r10", ARM::R10)
+                     .Case("r11", ARM::R11)
+                     .Case("sp", ARM::SP)
+                     .Default(ARM::NoRegister);
+  if (Reg != ARM::NoRegister)
     return Reg;
   report_fatal_error(Twine("Invalid register name \""
                               + StringRef(RegName)  + "\"."));

diff  --git a/llvm/lib/Target/ARM/ARMSubtarget.cpp b/llvm/lib/Target/ARM/ARMSubtarget.cpp
index eb4d39b01cbb..7a57376a6895 100644
--- a/llvm/lib/Target/ARM/ARMSubtarget.cpp
+++ b/llvm/lib/Target/ARM/ARMSubtarget.cpp
@@ -98,8 +98,9 @@ ARMSubtarget::ARMSubtarget(const Triple &TT, const std::string &CPU,
                            const ARMBaseTargetMachine &TM, bool IsLittle,
                            bool MinSize)
     : ARMGenSubtargetInfo(TT, CPU, FS), UseMulOps(UseFusedMulOps),
-      CPUString(CPU), OptMinSize(MinSize), IsLittle(IsLittle),
-      TargetTriple(TT), Options(TM.Options), TM(TM),
+      ReservedGPRegisters(ARM::GPRRegClass.getNumRegs()), CPUString(CPU),
+      OptMinSize(MinSize), IsLittle(IsLittle), TargetTriple(TT),
+      Options(TM.Options), TM(TM),
       FrameLowering(initializeFrameLowering(CPU, FS)),
       // At this point initializeSubtargetDependencies has been called so
       // we can query directly.
@@ -253,8 +254,18 @@ void ARMSubtarget::initSubtargetFeatures(StringRef CPU, StringRef FS) {
       (Options.UnsafeFPMath || isTargetDarwin()))
     UseNEONForSinglePrecisionFP = true;

-  if (isRWPI())
-    ReserveR9 = true;
+  if (isRWPI() || (isTargetMachO() && !HasV6Ops))
+    ReservedGPRegisters.set(9);
+
+  // Throw an error when trying to reserve a target's FP register. It may
+  // be used by the compiler even when frame pointer elimination is enabled.
+  // FIXME: Throw this error if -frame-pointer=none is not set; otherwise
+  //        only emit a warning.
+  const int restFP = (useR7AsFramePointer()) ? 7 : 11;
+  if (isGPRegisterReserved(restFP))
+    report_fatal_error(
+        "Register r" + std::to_string(restFP) +
+        " has been specified but is used as the frame pointer for this target.");

   // If MVEVectorCostFactor is still 0 (has not been set to anything else), default it to 2
   if (MVEVectorCostFactor == 0)

diff  --git a/llvm/lib/Target/ARM/ARMSubtarget.h b/llvm/lib/Target/ARM/ARMSubtarget.h
index f582a92f6563..c5836a3eca7b 100644
--- a/llvm/lib/Target/ARM/ARMSubtarget.h
+++ b/llvm/lib/Target/ARM/ARMSubtarget.h
@@ -229,8 +229,8 @@ class ARMSubtarget : public ARMGenSubtargetInfo {
   /// NoARM - True if subtarget does not support ARM mode execution.
   bool NoARM = false;

-  /// ReserveR9 - True if R9 is not available as a general purpose register.
-  bool ReserveR9 = false;
+  // ReservedGPRegisters[i] - R#i is not available as a general purpose register
+  BitVector ReservedGPRegisters;

   /// NoMovt - True if MOVT / MOVW pairs are not used for materialization of
   /// 32-bit imms (including global addresses).
@@ -763,8 +763,9 @@ class ARMSubtarget : public ARMGenSubtargetInfo {
   bool isAClass() const { return ARMProcClass == AClass; }
   bool isReadTPHard() const { return ReadTPHard; }

-  bool isR9Reserved() const {
-    return isTargetMachO() ? (ReserveR9 || !HasV6Ops) : ReserveR9;
+  bool isGPRegisterReserved(size_t i) const { return ReservedGPRegisters[i]; }
+  unsigned getNumGPRegistersReserved() const {
+    return ReservedGPRegisters.count();
   }

   bool useR7AsFramePointer() const {

diff  --git a/llvm/lib/Target/ARM/ARMTargetTransformInfo.h b/llvm/lib/Target/ARM/ARMTargetTransformInfo.h
index 5bb3bcaf10e7..c1fd01d2df9d 100644
--- a/llvm/lib/Target/ARM/ARMTargetTransformInfo.h
+++ b/llvm/lib/Target/ARM/ARMTargetTransformInfo.h
@@ -76,7 +76,9 @@ class ARMTTIImpl : public BasicTTIImplBase<ARMTTIImpl> {
       ARM::FeatureDSP, ARM::FeatureMP, ARM::FeatureVirtualization,
       ARM::FeatureMClass, ARM::FeatureRClass, ARM::FeatureAClass,
       ARM::FeatureNaClTrap, ARM::FeatureStrictAlign, ARM::FeatureLongCalls,
-      ARM::FeatureExecuteOnly, ARM::FeatureReserveR9, ARM::FeatureNoMovt,
+      ARM::FeatureExecuteOnly, ARM::FeatureReserveR6, ARM::FeatureReserveR7,
+      ARM::FeatureReserveR8, ARM::FeatureReserveR9, ARM::FeatureReserveR10,
+      ARM::FeatureReserveR11, ARM::FeatureNoMovt,
       ARM::FeatureNoNegativeImmediates
   };


diff  --git a/llvm/test/CodeGen/ARM/reg-alloc-fixed-r6-vla.ll b/llvm/test/CodeGen/ARM/reg-alloc-fixed-r6-vla.ll
new file mode 100644
index 000000000000..0b6fd7443af2
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/reg-alloc-fixed-r6-vla.ll
@@ -0,0 +1,44 @@
+; Using VLAs(Variable Length Arrays) in a function will use R6 to keep track
+; of the stack frame, and also spill/restore R6 to the stack.
+; This tests that using -ffixed-r6 (-mattr=+reserve-r6) will stop R6
+; being used and also stop it being spilled/restored to the stack.
+; RUN: llc < %s -mcpu=cortex-m0 -mtriple=thumbv7-arm-none-eabi  | FileCheck %s --check-prefix=CHECK-STATIC --check-prefix=CHECK-R6
+; RUN: llc < %s -mcpu=cortex-m0 -mtriple=thumbv7-arm-none-eabi -mattr=+reserve-r6  | FileCheck %s --check-prefix=CHECK-STATIC --check-prefix=CHECK-NO-R6
+
+define void @f() #0 {
+entry:
+  %i = alloca i32, align 4
+  store i32 0, i32* %i, align 4
+
+  %saved_stack = alloca i8*, align 4
+  %0 = call i8* @llvm.stacksave()
+  store i8* %0, i8** %saved_stack, align 4
+
+  %__vla_expr0 = alloca i32, align 4
+  %1 = load i32, i32* %i, align 4
+  %vla = alloca double, i32 %1, align 8
+  store i32 %1, i32* %__vla_expr0, align 4
+
+  %2 = load i8*, i8** %saved_stack, align 4
+  call void @llvm.stackrestore(i8* %2)
+
+  ret void
+}
+
+declare i8* @llvm.stacksave() #1
+declare void @llvm.stackrestore(i8* %ptr) #1
+
+attributes #0 = { noinline nounwind "stackrealign" }
+attributes #1 = { nounwind }
+
+; CHECK-STATIC: push {r4,
+; CHECK-R6: r6
+; CHECK-NO-R6-NOT: r6
+; CHECK-STATIC: lr}
+; CHECK-R6: r6
+; CHECK-NO-R6-NOT: r6
+; CHECK-STATIC: pop {r4,
+; CHECK-R6: r6
+; CHECK-NO-R6-NOT: r6
+; CHECK-STATIC: pc}
+

diff  --git a/llvm/test/CodeGen/ARM/reg-alloc-with-fixed-reg-r6-modified.ll b/llvm/test/CodeGen/ARM/reg-alloc-with-fixed-reg-r6-modified.ll
new file mode 100644
index 000000000000..e2a4af87dde7
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/reg-alloc-with-fixed-reg-r6-modified.ll
@@ -0,0 +1,63 @@
+; RUN: llc < %s -mattr=+reserve-r6 -mtriple=arm-linux-gnueabi -O0 -filetype=asm --regalloc=fast 2>&1 | FileCheck %s
+;
+; Equivalent C source code
+; register unsigned r6 asm("r6");
+; void bar(unsigned int i,
+;          unsigned int j,
+;          unsigned int k,
+;          unsigned int l,
+;          unsigned int m,
+;          unsigned int n,
+;          unsigned int o,
+;          unsigned int p)
+; {
+;     r6 = 10;
+;     unsigned int result = i + j + k + l + m + n + o + p;
+; }
+declare void @llvm.write_register.i32(metadata, i32) nounwind
+
+define void @bar(i32 %i, i32 %j, i32 %k, i32 %l, i32 %m, i32 %n, i32 %o, i32 %p) nounwind {
+entry:
+; CHECK-NOT: push {{{.*}}r6,{{.*}}}
+; CHECK: {{.*}}mov{{.*}}r6,{{.*}}
+; CHECK-NOT: {{.*}}r6{{.*}}
+  %i.addr = alloca i32, align 4
+  %j.addr = alloca i32, align 4
+  %k.addr = alloca i32, align 4
+  %l.addr = alloca i32, align 4
+  %m.addr = alloca i32, align 4
+  %n.addr = alloca i32, align 4
+  %o.addr = alloca i32, align 4
+  %p.addr = alloca i32, align 4
+  %result = alloca i32, align 4
+  store i32 %i, i32* %i.addr, align 4
+  store i32 %j, i32* %j.addr, align 4
+  store i32 %k, i32* %k.addr, align 4
+  store i32 %l, i32* %l.addr, align 4
+  store i32 %m, i32* %m.addr, align 4
+  store i32 %n, i32* %n.addr, align 4
+  store i32 %o, i32* %o.addr, align 4
+  store i32 %p, i32* %p.addr, align 4
+  call void @llvm.write_register.i32(metadata !0, i32 10)
+  %0 = load i32, i32* %i.addr, align 4
+  %1 = load i32, i32* %j.addr, align 4
+  %add = add i32 %0, %1
+  %2 = load i32, i32* %k.addr, align 4
+  %add1 = add i32 %add, %2
+  %3 = load i32, i32* %l.addr, align 4
+  %add2 = add i32 %add1, %3
+  %4 = load i32, i32* %m.addr, align 4
+  %add3 = add i32 %add2, %4
+  %5 = load i32, i32* %n.addr, align 4
+  %add4 = add i32 %add3, %5
+  %6 = load i32, i32* %o.addr, align 4
+  %add5 = add i32 %add4, %6
+  %7 = load i32, i32* %p.addr, align 4
+  %add6 = add i32 %add5, %7
+  store i32 %add6, i32* %result, align 4
+  ret void
+}
+
+!llvm.named.register.r6 = !{!0}
+!0 = !{!"r6"}
+

diff  --git a/llvm/test/CodeGen/ARM/reg-alloc-with-fixed-reg-r6.ll b/llvm/test/CodeGen/ARM/reg-alloc-with-fixed-reg-r6.ll
new file mode 100644
index 000000000000..3647c0701a7c
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/reg-alloc-with-fixed-reg-r6.ll
@@ -0,0 +1,57 @@
+; RUN: llc < %s -mattr=+reserve-r6 -mtriple=arm-linux-gnueabi -O0 -filetype=asm --regalloc=fast 2>&1 | FileCheck %s
+;
+; Equivalent C source code
+; void bar(unsigned int i,
+;          unsigned int j,
+;          unsigned int k,
+;          unsigned int l,
+;          unsigned int m,
+;          unsigned int n,
+;          unsigned int o,
+;          unsigned int p)
+; {
+;     unsigned int result = i + j + k + l + m + n + o + p;
+; }
+
+define void @bar(i32 %i, i32 %j, i32 %k, i32 %l, i32 %m, i32 %n, i32 %o, i32 %p) nounwind {
+entry:
+; CHECK-NOT: push {{{.*}}r6,{{.*}}}
+  %i.addr = alloca i32, align 4
+  %j.addr = alloca i32, align 4
+  %k.addr = alloca i32, align 4
+  %l.addr = alloca i32, align 4
+  %m.addr = alloca i32, align 4
+  %n.addr = alloca i32, align 4
+  %o.addr = alloca i32, align 4
+  %p.addr = alloca i32, align 4
+  %result = alloca i32, align 4
+  store i32 %i, i32* %i.addr, align 4
+  store i32 %j, i32* %j.addr, align 4
+  store i32 %k, i32* %k.addr, align 4
+  store i32 %l, i32* %l.addr, align 4
+  store i32 %m, i32* %m.addr, align 4
+  store i32 %n, i32* %n.addr, align 4
+  store i32 %o, i32* %o.addr, align 4
+  store i32 %p, i32* %p.addr, align 4
+  %0 = load i32, i32* %i.addr, align 4
+  %1 = load i32, i32* %j.addr, align 4
+  %add = add i32 %0, %1
+  %2 = load i32, i32* %k.addr, align 4
+  %add1 = add i32 %add, %2
+  %3 = load i32, i32* %l.addr, align 4
+  %add2 = add i32 %add1, %3
+  %4 = load i32, i32* %m.addr, align 4
+  %add3 = add i32 %add2, %4
+  %5 = load i32, i32* %n.addr, align 4
+  %add4 = add i32 %add3, %5
+  %6 = load i32, i32* %o.addr, align 4
+  %add5 = add i32 %add4, %6
+  %7 = load i32, i32* %p.addr, align 4
+  %add6 = add i32 %add5, %7
+  store i32 %add6, i32* %result, align 4
+; CHECK: {{.*}}r5{{.*}}
+; CHECK-NOT: {{.*}}r6{{.*}}
+  ret void
+; CHECK-NOT: pop {{{.*}}r6,{{.*}}}
+}
+

diff  --git a/llvm/test/CodeGen/ARM/reg-alloc-wout-fixed-regs.ll b/llvm/test/CodeGen/ARM/reg-alloc-wout-fixed-regs.ll
new file mode 100644
index 000000000000..d1f020936a3d
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/reg-alloc-wout-fixed-regs.ll
@@ -0,0 +1,58 @@
+; RUN: llc < %s -mtriple=arm-linux-gnueabi  -O0 -filetype=asm --regalloc=fast 2>&1 | FileCheck %s
+;
+; Equivalent C source code
+; void bar(unsigned int i,
+;          unsigned int j,
+;          unsigned int k,
+;          unsigned int l,
+;          unsigned int m,
+;          unsigned int n,
+;          unsigned int o,
+;          unsigned int p)
+; {
+;     unsigned int result = i + j + k + l + m + n + o + p;
+; }
+
+define void @bar(i32 %i, i32 %j, i32 %k, i32 %l, i32 %m, i32 %n, i32 %o, i32 %p) nounwind {
+entry:
+; CHECK: push {{{.*}}r4, r5{{.*}}}
+  %i.addr = alloca i32, align 4
+  %j.addr = alloca i32, align 4
+  %k.addr = alloca i32, align 4
+  %l.addr = alloca i32, align 4
+  %m.addr = alloca i32, align 4
+  %n.addr = alloca i32, align 4
+  %o.addr = alloca i32, align 4
+  %p.addr = alloca i32, align 4
+  %result = alloca i32, align 4
+  store i32 %i, i32* %i.addr, align 4
+  store i32 %j, i32* %j.addr, align 4
+  store i32 %k, i32* %k.addr, align 4
+  store i32 %l, i32* %l.addr, align 4
+  store i32 %m, i32* %m.addr, align 4
+  store i32 %n, i32* %n.addr, align 4
+  store i32 %o, i32* %o.addr, align 4
+  store i32 %p, i32* %p.addr, align 4
+  %0 = load i32, i32* %i.addr, align 4
+  %1 = load i32, i32* %j.addr, align 4
+  %add = add i32 %0, %1
+  %2 = load i32, i32* %k.addr, align 4
+  %add1 = add i32 %add, %2
+  %3 = load i32, i32* %l.addr, align 4
+  %add2 = add i32 %add1, %3
+  %4 = load i32, i32* %m.addr, align 4
+  %add3 = add i32 %add2, %4
+  %5 = load i32, i32* %n.addr, align 4
+  %add4 = add i32 %add3, %5
+  %6 = load i32, i32* %o.addr, align 4
+  %add5 = add i32 %add4, %6
+  %7 = load i32, i32* %p.addr, align 4
+  %add6 = add i32 %add5, %7
+  store i32 %add6, i32* %result, align 4
+; CHECK: {{.*}}r4{{.*}}
+; CHECK: {{.*}}r5{{.*}}
+
+; CHECK: pop {{{.*}}r4, r5{{.*}}}
+  ret void
+}
+

diff  --git a/llvm/test/CodeGen/Thumb/callee_save_reserved.ll b/llvm/test/CodeGen/Thumb/callee_save_reserved.ll
new file mode 100644
index 000000000000..0329d7886a2a
--- /dev/null
+++ b/llvm/test/CodeGen/Thumb/callee_save_reserved.ll
@@ -0,0 +1,15 @@
+; RUN: llc < %s -mtriple=thumbv6m-none-eabi -verify-machineinstrs -frame-pointer=none -mattr=+reserve-r6,+reserve-r8 \
+; RUN:     -asm-verbose=false | FileCheck --check-prefix=CHECK-INVALID %s
+
+; Reserved low registers should not be used to correct reg deficit.
+define <4 x i32> @four_high_four_return_reserved() {
+entry:
+  ; CHECK-INVALID-NOT: r{{6|8}}
+  tail call void asm sideeffect "", "~{r8},~{r9}"()
+  %vecinit = insertelement <4 x i32> undef, i32 1, i32 0
+  %vecinit11 = insertelement <4 x i32> %vecinit, i32 2, i32 1
+  %vecinit12 = insertelement <4 x i32> %vecinit11, i32 3, i32 2
+  %vecinit13 = insertelement <4 x i32> %vecinit12, i32 4, i32 3
+  ret <4 x i32> %vecinit13
+}
+

diff  --git a/llvm/test/Feature/reserve_global_reg.ll b/llvm/test/Feature/reserve_global_reg.ll
new file mode 100644
index 000000000000..06081cae1fb2
--- /dev/null
+++ b/llvm/test/Feature/reserve_global_reg.ll
@@ -0,0 +1,29 @@
+; RUN: not llc < %s -mtriple=thumbv7-apple-darwin -mattr=+reserve-r7 -o - 2>&1 | FileCheck -check-prefix=CHECK-RESERVE-FP7 %s
+; RUN: not llc < %s -mtriple=armv7-windows-msvc -mattr=+reserve-r11 -o - 2>&1 | FileCheck -check-prefix=CHECK-RESERVE-FP11 %s
+; RUN: not llc < %s -mtriple=thumbv7-windows -mattr=+reserve-r11 -o - 2>&1 | FileCheck -check-prefix=CHECK-RESERVE-FP11-2 %s
+
+; int test(int a, int b, int c) {
+;   return a + b + c;
+; }
+
+; Function Attrs: noinline nounwind optnone
+define hidden i32 @_Z4testiii(i32 %a, i32 %b, i32 %c) #0 {
+entry:
+  %a.addr = alloca i32, align 4
+  %b.addr = alloca i32, align 4
+  %c.addr = alloca i32, align 4
+  store i32 %a, i32* %a.addr, align 4
+  store i32 %b, i32* %b.addr, align 4
+  store i32 %c, i32* %c.addr, align 4
+  %0 = load i32, i32* %a.addr, align 4
+  %1 = load i32, i32* %b.addr, align 4
+  %add = add nsw i32 %0, %1
+  %2 = load i32, i32* %c.addr, align 4
+  %add1 = add nsw i32 %add, %2
+  ret i32 %add1
+}
+
+; CHECK-RESERVE-FP7: Register r7 has been specified but is used as the frame pointer for this target.
+; CHECK-RESERVE-FP11: Register r11 has been specified but is used as the frame pointer for this target.
+; CHECK-RESERVE-FP11-2: Register r11 has been specified but is used as the frame pointer for this target.
+



_______________________________________________
llvm-commits mailing list
llvm-commits at lists.llvm.org<mailto:llvm-commits at lists.llvm.org>
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20191119/22a6747f/attachment.html>


More information about the llvm-commits mailing list