[clang] [llvm] Add FramePointerKind::NonLeafNoReserve (PR #163775)

Nabeel Omer via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 24 09:28:35 PDT 2025


https://github.com/omern1 updated https://github.com/llvm/llvm-project/pull/163775

>From 2cb9a74fe45202d542891b3f609cf56d80130e07 Mon Sep 17 00:00:00 2001
From: Nabeel Omer <Nabeel.Omer at sony.com>
Date: Thu, 16 Oct 2025 10:44:55 +0100
Subject: [PATCH 1/6] Add FramePointerKind::NonLeafNoReserve

This patch adds a new FramePointerKind::NonLeafNoReserve and makes it
the default for -momit-frame-pointer=leaf.

This should fix #154379, the main impact of this patch can be found in
clang/lib/Driver/ToolChains/CommonArgs.cpp.
---
 clang/include/clang/Basic/CodeGenOptions.def |   2 +-
 clang/include/clang/Basic/CodeGenOptions.h   |   5 +-
 clang/include/clang/Driver/Options.td        |   4 +-
 clang/lib/CodeGen/CGCall.cpp                 |   1 +
 clang/lib/CodeGen/CodeGenModule.cpp          |   3 +
 clang/lib/Driver/ToolChains/Clang.cpp        |   3 +
 clang/lib/Driver/ToolChains/CommonArgs.cpp   |  42 +-
 clang/lib/Driver/ToolChains/Flang.cpp        |   3 +
 clang/test/Driver/frame-pointer-elim.c       |   8 +-
 clang/test/Driver/fuchsia.c                  |   2 +-
 llvm/include/llvm/Support/CodeGen.h          |   2 +-
 llvm/lib/CodeGen/CommandFlags.cpp            |   4 +
 llvm/lib/CodeGen/TargetOptionsImpl.cpp       |   3 +-
 llvm/lib/IR/Function.cpp                     |   3 +
 llvm/lib/IR/Verifier.cpp                     |   2 +-
 llvm/test/CodeGen/X86/regalloc-fp.ll         | 775 +++++++++++++++++++
 16 files changed, 836 insertions(+), 26 deletions(-)
 create mode 100644 llvm/test/CodeGen/X86/regalloc-fp.ll

diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index 90e1f8d1eb5e9..52360b67b306c 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -54,7 +54,7 @@ CODEGENOPT(SeparateNamedSections, 1, 0, Benign) ///< Set for -fseparate-named-se
 CODEGENOPT(EnableAIXExtendedAltivecABI, 1, 0, Benign) ///< Set for -mabi=vec-extabi. Enables the extended Altivec ABI on AIX.
 CODEGENOPT(XCOFFReadOnlyPointers, 1, 0, Benign) ///< Set for -mxcoff-roptr.
 CODEGENOPT(AllTocData, 1, 0, Benign) ///< AIX -mtocdata
-ENUM_CODEGENOPT(FramePointer, FramePointerKind, 2, FramePointerKind::None, Benign) /// frame-pointer: all,non-leaf,reserved,none
+ENUM_CODEGENOPT(FramePointer, FramePointerKind, 3, FramePointerKind::None, Benign) /// frame-pointer: all,non-leaf,non-leaf-no-reserve,reserved,none
 
 ENUM_CODEGENOPT(ExceptionHandling, ExceptionHandlingKind, 3, ExceptionHandlingKind::None, NotCompatible)
 
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index cae06c3c9495a..3dd0140ea0d9f 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -155,9 +155,10 @@ class CodeGenOptions : public CodeGenOptionsBase {
   std::string BinutilsVersion;
 
   enum class FramePointerKind {
+    NonLeafNoReserve, // Keep non-leaf frame pointers, allow the FP to be used as a GPR in leaf functions.
     None,     // Omit all frame pointers.
     Reserved, // Maintain valid frame pointer chain.
-    NonLeaf,  // Keep non-leaf frame pointers.
+    NonLeaf,  // Keep non-leaf frame pointers, don't allow the FP to be used as a GPR in leaf functions.
     All,      // Keep all frame pointers.
   };
 
@@ -167,6 +168,8 @@ class CodeGenOptions : public CodeGenOptionsBase {
       return "none";
     case FramePointerKind::Reserved:
       return "reserved";
+    case FramePointerKind::NonLeafNoReserve:
+      return "non-leaf-no-reserve";
     case FramePointerKind::NonLeaf:
       return "non-leaf";
     case FramePointerKind::All:
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 7ae153deb9a55..5f7ef34a36be6 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -8481,8 +8481,8 @@ def pic_is_pie : Flag<["-"], "pic-is-pie">,
   MarshallingInfoFlag<LangOpts<"PIE">>;
 
 def mframe_pointer_EQ : Joined<["-"], "mframe-pointer=">,
-  HelpText<"Specify which frame pointers to retain.">, Values<"all,non-leaf,reserved,none">,
-  NormalizedValuesScope<"CodeGenOptions::FramePointerKind">, NormalizedValues<["All", "NonLeaf", "Reserved", "None"]>,
+  HelpText<"Specify which frame pointers to retain.">, Values<"all,non-leaf,non-leaf-no-reserve,reserved,none">,
+  NormalizedValuesScope<"CodeGenOptions::FramePointerKind">, NormalizedValues<["All", "NonLeaf", "NonLeafNoReserve", "Reserved", "None"]>,
   MarshallingInfoEnum<CodeGenOpts<"FramePointer">, "None">;
 
 
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 741fa44713ac8..60a7779786ee0 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -1990,6 +1990,7 @@ static void getTrivialDefaultFunctionAttributes(
       // This is the default behavior.
       break;
     case CodeGenOptions::FramePointerKind::Reserved:
+    case CodeGenOptions::FramePointerKind::NonLeafNoReserve:
     case CodeGenOptions::FramePointerKind::NonLeaf:
     case CodeGenOptions::FramePointerKind::All:
       FuncAttrs.addAttribute("frame-pointer",
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index c5eb14e329315..2488b2b1ded70 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -1506,6 +1506,9 @@ void CodeGenModule::Release() {
   case CodeGenOptions::FramePointerKind::Reserved:
     getModule().setFramePointer(llvm::FramePointerKind::Reserved);
     break;
+  case CodeGenOptions::FramePointerKind::NonLeafNoReserve:
+    getModule().setFramePointer(llvm::FramePointerKind::NonLeafNoReserve);
+    break;
   case CodeGenOptions::FramePointerKind::NonLeaf:
     getModule().setFramePointer(llvm::FramePointerKind::NonLeaf);
     break;
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index a7310ba2da061..7072a41d2507a 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5748,6 +5748,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
   case CodeGenOptions::FramePointerKind::Reserved:
     FPKeepKindStr = "-mframe-pointer=reserved";
     break;
+  case CodeGenOptions::FramePointerKind::NonLeafNoReserve:
+    FPKeepKindStr = "-mframe-pointer=non-leaf-no-reserve";
+    break;
   case CodeGenOptions::FramePointerKind::NonLeaf:
     FPKeepKindStr = "-mframe-pointer=non-leaf";
     break;
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 99400ac701fbe..55a57acebfd9a 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -222,26 +222,36 @@ static bool framePointerImpliesLeafFramePointer(const llvm::opt::ArgList &Args,
 clang::CodeGenOptions::FramePointerKind
 getFramePointerKind(const llvm::opt::ArgList &Args,
                     const llvm::Triple &Triple) {
-  // There are three things to consider here:
+  // There are four things to consider here:
   // * Should a frame record be created for non-leaf functions?
   // * Should a frame record be created for leaf functions?
-  // * Is the frame pointer register reserved, i.e. must it always point to
-  //   either a new, valid frame record or be un-modified?
+  // * Is the frame pointer register reserved in non-leaf functions?
+  //   i.e. must it always point to either a new, valid frame record or be un-modified?
+  // * Is the frame pointer register reserved in leaf functions?
   //
   //  Not all combinations of these are valid:
   //  * It's not useful to have leaf frame records without non-leaf ones.
   //  * It's not useful to have frame records without reserving the frame
   //    pointer.
   //
-  // | Non-leaf | Leaf | Reserved |
-  // | N        | N    | N        | FramePointerKind::None
-  // | N        | N    | Y        | FramePointerKind::Reserved
-  // | N        | Y    | N        | Invalid
-  // | N        | Y    | Y        | Invalid
-  // | Y        | N    | N        | Invalid
-  // | Y        | N    | Y        | FramePointerKind::NonLeaf
-  // | Y        | Y    | N        | Invalid
-  // | Y        | Y    | Y        | FramePointerKind::All
+  // | Non-leaf | Leaf | Reserved In Non-Leaf  | Reserved In Leaf |
+  // |----------|------|-----------------------|------------------|
+  // | N        | N    | N                     | N                | FramePointerKind::None
+  // | N        | N    | N                     | Y                | Invalid
+  // | N        | N    | Y                     | N                | Invalid
+  // | N        | N    | Y                     | Y                | FramePointerKind::Reserved
+  // | N        | Y    | N                     | N                | Invalid
+  // | N        | Y    | N                     | Y                | Invalid
+  // | N        | Y    | Y                     | N                | Invalid
+  // | N        | Y    | Y                     | Y                | Invalid
+  // | Y        | N    | N                     | N                | Invalid
+  // | Y        | N    | N                     | Y                | Invalid
+  // | Y        | N    | Y                     | N                | FramePointerKind::NonLeafNoReserve
+  // | Y        | N    | Y                     | Y                | FramePointerKind::NonLeaf
+  // | Y        | Y    | N                     | N                | Invalid
+  // | Y        | Y    | N                     | Y                | Invalid
+  // | Y        | Y    | Y                     | N                | Invalid
+  // | Y        | Y    | Y                     | Y                | FramePointerKind::All
   //
   // The FramePointerKind::Reserved case is currently only reachable for Arm,
   // which has the -mframe-chain= option which can (in combination with
@@ -261,12 +271,16 @@ getFramePointerKind(const llvm::opt::ArgList &Args,
       clang::driver::options::OPT_mno_omit_leaf_frame_pointer,
       clang::driver::options::OPT_momit_leaf_frame_pointer, DefaultLeafFP);
 
-  bool FPRegReserved = EnableFP || mustMaintainValidFrameChain(Args, Triple);
+  bool FPRegReserved = mustMaintainValidFrameChain(Args, Triple);
 
   if (EnableFP) {
     if (EnableLeafFP)
       return clang::CodeGenOptions::FramePointerKind::All;
-    return clang::CodeGenOptions::FramePointerKind::NonLeaf;
+
+    if (FPRegReserved)
+      return clang::CodeGenOptions::FramePointerKind::NonLeaf;
+
+    return clang::CodeGenOptions::FramePointerKind::NonLeafNoReserve;
   }
   if (FPRegReserved)
     return clang::CodeGenOptions::FramePointerKind::Reserved;
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index a56fa41c49d34..9eba3914b1e1e 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -1064,6 +1064,9 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA,
   case CodeGenOptions::FramePointerKind::Reserved:
     FPKeepKindStr = "-mframe-pointer=reserved";
     break;
+  case CodeGenOptions::FramePointerKind::NonLeafNoReserve:
+    FPKeepKindStr = "-mframe-pointer=non-leaf-no-reserve";
+    break;
   case CodeGenOptions::FramePointerKind::NonLeaf:
     FPKeepKindStr = "-mframe-pointer=non-leaf";
     break;
diff --git a/clang/test/Driver/frame-pointer-elim.c b/clang/test/Driver/frame-pointer-elim.c
index 6d719828c6a06..9b0e6fa15b0d4 100644
--- a/clang/test/Driver/frame-pointer-elim.c
+++ b/clang/test/Driver/frame-pointer-elim.c
@@ -1,7 +1,7 @@
 // KEEP-ALL-NOT:  warning: argument unused
 // KEEP-ALL:      "-mframe-pointer=all"
 // KEEP-NON-LEAF-NOT: warning: argument unused
-// KEEP-NON-LEAF: "-mframe-pointer=non-leaf"
+// KEEP-NON-LEAF: "-mframe-pointer=non-leaf-no-reserve"
 // KEEP-NONE-NOT: warning: argument unused
 // KEEP-NONE:     "-mframe-pointer=none"
 // KEEP-RESERVED-NOT: warning: argument unused
@@ -73,17 +73,17 @@
 // RUN: %clang -### -target armv7s-apple-ios -fomit-frame-pointer %s 2>&1 | \
 // RUN:   FileCheck --check-prefix=WARN-OMIT-7S %s
 // WARN-OMIT-7S: warning: optimization flag '-fomit-frame-pointer' is not supported for target 'armv7s'
-// WARN-OMIT-7S: "-mframe-pointer=non-leaf"
+// WARN-OMIT-7S: "-mframe-pointer=non-leaf-no-reserve"
 
 // RUN: %clang -### -target armv7k-apple-watchos -fomit-frame-pointer %s 2>&1 | \
 // RUN:   FileCheck --check-prefix=WARN-OMIT-7K %s
 // WARN-OMIT-7K: warning: optimization flag '-fomit-frame-pointer' is not supported for target 'armv7k'
-// WARN-OMIT-7K: "-mframe-pointer=non-leaf"
+// WARN-OMIT-7K: "-mframe-pointer=non-leaf-no-reserve"
 
 // RUN: %clang -### -target armv7s-apple-ios8.0 -momit-leaf-frame-pointer %s 2>&1 | \
 // RUN:   FileCheck --check-prefix=WARN-OMIT-LEAF-7S %s
 // WARN-OMIT-LEAF-7S-NOT: warning: optimization flag '-momit-leaf-frame-pointer' is not supported for target 'armv7s'
-// WARN-OMIT-LEAF-7S: "-mframe-pointer=non-leaf"
+// WARN-OMIT-LEAF-7S: "-mframe-pointer=non-leaf-no-reserve"
 
 // On AArch64, PS4, PS5, and VE, default to omitting the frame pointer on leaf
 // functions
diff --git a/clang/test/Driver/fuchsia.c b/clang/test/Driver/fuchsia.c
index cf92f85040901..71aabcc9f7fc1 100644
--- a/clang/test/Driver/fuchsia.c
+++ b/clang/test/Driver/fuchsia.c
@@ -68,7 +68,7 @@
 // RUN: %clang -### %s --target=aarch64-unknown-fuchsia -O3 2>&1 \
 // RUN:     | FileCheck %s -check-prefix=CHECK-FP-NONE
 // CHECK-FP-ALL: "-mframe-pointer=all"
-// CHECK-FP-NONLEAF: "-mframe-pointer=non-leaf"
+// CHECK-FP-NONLEAF: "-mframe-pointer=non-leaf-no-reserve"
 // CHECK-FP-NONE: "-mframe-pointer=none"
 
 // RUN: not %clang -### %s --target=x86_64-unknown-fuchsia -rtlib=libgcc 2>&1 \
diff --git a/llvm/include/llvm/Support/CodeGen.h b/llvm/include/llvm/Support/CodeGen.h
index cd1f9167b996d..6675f131bc406 100644
--- a/llvm/include/llvm/Support/CodeGen.h
+++ b/llvm/include/llvm/Support/CodeGen.h
@@ -115,7 +115,7 @@ namespace llvm {
   };
 
   // Specify what functions should keep the frame pointer.
-  enum class FramePointerKind { None, NonLeaf, All, Reserved };
+  enum class FramePointerKind { None, NonLeaf, All, Reserved, NonLeafNoReserve };
 
   // Specify what type of zeroing callee-used registers.
   namespace ZeroCallUsedRegs {
diff --git a/llvm/lib/CodeGen/CommandFlags.cpp b/llvm/lib/CodeGen/CommandFlags.cpp
index 0522698adf183..e3089dd1eb07b 100644
--- a/llvm/lib/CodeGen/CommandFlags.cpp
+++ b/llvm/lib/CodeGen/CommandFlags.cpp
@@ -211,6 +211,8 @@ codegen::RegisterCodeGenFlags::RegisterCodeGenFlags() {
           clEnumValN(FramePointerKind::All, "all",
                      "Disable frame pointer elimination"),
           clEnumValN(FramePointerKind::NonLeaf, "non-leaf",
+                     "Disable frame pointer elimination for non-leaf frame but reserve the register in leaf functions"),
+          clEnumValN(FramePointerKind::NonLeafNoReserve, "non-leaf-no-reserve",
                      "Disable frame pointer elimination for non-leaf frame"),
           clEnumValN(FramePointerKind::Reserved, "reserved",
                      "Enable frame pointer elimination, but reserve the frame "
@@ -695,6 +697,8 @@ void codegen::setFunctionAttributes(StringRef CPU, StringRef Features,
       NewAttrs.addAttribute("frame-pointer", "all");
     else if (getFramePointerUsage() == FramePointerKind::NonLeaf)
       NewAttrs.addAttribute("frame-pointer", "non-leaf");
+    else if (getFramePointerUsage() == FramePointerKind::NonLeafNoReserve)
+      NewAttrs.addAttribute("frame-pointer", "non-leaf-no-reserve");
     else if (getFramePointerUsage() == FramePointerKind::Reserved)
       NewAttrs.addAttribute("frame-pointer", "reserved");
     else if (getFramePointerUsage() == FramePointerKind::None)
diff --git a/llvm/lib/CodeGen/TargetOptionsImpl.cpp b/llvm/lib/CodeGen/TargetOptionsImpl.cpp
index 5eb86e740ff7c..90d61f7b359e5 100644
--- a/llvm/lib/CodeGen/TargetOptionsImpl.cpp
+++ b/llvm/lib/CodeGen/TargetOptionsImpl.cpp
@@ -30,7 +30,7 @@ bool TargetOptions::DisableFramePointerElim(const MachineFunction &MF) const {
   StringRef FP = FPAttr.getValueAsString();
   if (FP == "all")
     return true;
-  if (FP == "non-leaf")
+  if (FP == "non-leaf" || FP == "non-leaf-no-reserve")
     return MF.getFrameInfo().hasCalls();
   if (FP == "none" || FP == "reserved")
     return false;
@@ -45,6 +45,7 @@ bool TargetOptions::FramePointerIsReserved(const MachineFunction &MF) const {
 
   return StringSwitch<bool>(FPAttr.getValueAsString())
       .Cases("all", "non-leaf", "reserved", true)
+      .Case(("non-leaf-no-reserve"), MF.getFrameInfo().hasCalls())
       .Case("none", false);
 }
 
diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp
index fc067459dcba3..31a294447152e 100644
--- a/llvm/lib/IR/Function.cpp
+++ b/llvm/lib/IR/Function.cpp
@@ -396,6 +396,9 @@ Function *Function::createWithDefaultAttr(FunctionType *Ty,
   case FramePointerKind::NonLeaf:
     B.addAttribute("frame-pointer", "non-leaf");
     break;
+  case FramePointerKind::NonLeafNoReserve:
+    B.addAttribute("frame-pointer", "non-leaf-no-reserve");
+    break;
   case FramePointerKind::All:
     B.addAttribute("frame-pointer", "all");
     break;
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index c79a95087dbdd..4a5e0b6bdae97 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -2484,7 +2484,7 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs,
 
   if (Attribute FPAttr = Attrs.getFnAttr("frame-pointer"); FPAttr.isValid()) {
     StringRef FP = FPAttr.getValueAsString();
-    if (FP != "all" && FP != "non-leaf" && FP != "none" && FP != "reserved")
+    if (FP != "all" && FP != "non-leaf" && FP != "none" && FP != "reserved" && FP != "non-leaf-no-reserve")
       CheckFailed("invalid value for 'frame-pointer' attribute: " + FP, V);
   }
 
diff --git a/llvm/test/CodeGen/X86/regalloc-fp.ll b/llvm/test/CodeGen/X86/regalloc-fp.ll
new file mode 100644
index 0000000000000..e89e5ab1d6b59
--- /dev/null
+++ b/llvm/test/CodeGen/X86/regalloc-fp.ll
@@ -0,0 +1,775 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; Context:
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown | FileCheck %s
+define i32 @check_none() "frame-pointer"="none" {
+; CHECK-LABEL: check_none:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    pushq %rbp
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    pushq %r15
+; CHECK-NEXT:    .cfi_def_cfa_offset 24
+; CHECK-NEXT:    pushq %r14
+; CHECK-NEXT:    .cfi_def_cfa_offset 32
+; CHECK-NEXT:    pushq %r13
+; CHECK-NEXT:    .cfi_def_cfa_offset 40
+; CHECK-NEXT:    pushq %r12
+; CHECK-NEXT:    .cfi_def_cfa_offset 48
+; CHECK-NEXT:    pushq %rbx
+; CHECK-NEXT:    .cfi_def_cfa_offset 56
+; CHECK-NEXT:    .cfi_offset %rbx, -56
+; CHECK-NEXT:    .cfi_offset %r12, -48
+; CHECK-NEXT:    .cfi_offset %r13, -40
+; CHECK-NEXT:    .cfi_offset %r14, -32
+; CHECK-NEXT:    .cfi_offset %r15, -24
+; CHECK-NEXT:    .cfi_offset %rbp, -16
+; CHECK-NEXT:    movl $0, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $1, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $2, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $3, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $4, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $5, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $6, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $7, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $8, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $9, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $16, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $17, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $18, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $19, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $20, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %eax
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %ecx
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %edx
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %esi
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %edi
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r8d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r9d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r10d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r11d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %ebx
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %ebp
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r14d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r15d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r12d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r13d
+; CHECK-NEXT:    #APP
+; CHECK-NEXT:    nop
+; CHECK-NEXT:    #NO_APP
+; CHECK-NEXT:    movl %eax, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %ecx, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %edx, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %esi, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %edi, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r8d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r9d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r10d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r11d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %ebx, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %ebp, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r14d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r15d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r12d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r13d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    xorl %eax, %eax
+; CHECK-NEXT:    popq %rbx
+; CHECK-NEXT:    .cfi_def_cfa_offset 48
+; CHECK-NEXT:    popq %r12
+; CHECK-NEXT:    .cfi_def_cfa_offset 40
+; CHECK-NEXT:    popq %r13
+; CHECK-NEXT:    .cfi_def_cfa_offset 32
+; CHECK-NEXT:    popq %r14
+; CHECK-NEXT:    .cfi_def_cfa_offset 24
+; CHECK-NEXT:    popq %r15
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    popq %rbp
+; CHECK-NEXT:    .cfi_def_cfa_offset 8
+; CHECK-NEXT:    retq
+entry:
+  %reg0 = alloca i32, align 4
+  %reg1 = alloca i32, align 4
+  %reg2 = alloca i32, align 4
+  %reg3 = alloca i32, align 4
+  %reg4 = alloca i32, align 4
+  %reg5 = alloca i32, align 4
+  %reg6 = alloca i32, align 4
+  %reg7 = alloca i32, align 4
+  %reg8 = alloca i32, align 4
+  %reg9 = alloca i32, align 4
+  %reg10 = alloca i32, align 4
+  %reg11 = alloca i32, align 4
+  %reg12 = alloca i32, align 4
+  %reg13 = alloca i32, align 4
+  %reg14 = alloca i32, align 4
+  store volatile i32 0, ptr %reg0, align 4
+  store volatile i32 1, ptr %reg1, align 4
+  store volatile i32 2, ptr %reg2, align 4
+  store volatile i32 3, ptr %reg3, align 4
+  store volatile i32 4, ptr %reg4, align 4
+  store volatile i32 5, ptr %reg5, align 4
+  store volatile i32 6, ptr %reg6, align 4
+  store volatile i32 7, ptr %reg7, align 4
+  store volatile i32 8, ptr %reg8, align 4
+  store volatile i32 9, ptr %reg9, align 4
+  store volatile i32 16, ptr %reg10, align 4
+  store volatile i32 17, ptr %reg11, align 4
+  store volatile i32 18, ptr %reg12, align 4
+  store volatile i32 19, ptr %reg13, align 4
+  store volatile i32 20, ptr %reg14, align 4
+  %0 = load volatile i32, ptr %reg0, align 4
+  %1 = load volatile i32, ptr %reg1, align 4
+  %2 = load volatile i32, ptr %reg2, align 4
+  %3 = load volatile i32, ptr %reg3, align 4
+  %4 = load volatile i32, ptr %reg4, align 4
+  %5 = load volatile i32, ptr %reg5, align 4
+  %6 = load volatile i32, ptr %reg6, align 4
+  %7 = load volatile i32, ptr %reg7, align 4
+  %8 = load volatile i32, ptr %reg8, align 4
+  %9 = load volatile i32, ptr %reg9, align 4
+  %10 = load volatile i32, ptr %reg10, align 4
+  %11 = load volatile i32, ptr %reg11, align 4
+  %12 = load volatile i32, ptr %reg12, align 4
+  %13 = load volatile i32, ptr %reg13, align 4
+  %14 = load volatile i32, ptr %reg14, align 4
+  %15 = call { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } asm "nop", "=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,~{dirflag},~{fpsr},~{flags}"(i32 %0, i32 %1, i32 %2, i32 %3, i32 %4, i32 %5, i32 %6, i32 %7, i32 %8, i32 %9, i32 %10, i32 %11, i32 %12, i32 %13, i32 %14) #1
+  %asmresult = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 0
+  %asmresult1 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 1
+  %asmresult2 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 2
+  %asmresult3 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 3
+  %asmresult4 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 4
+  %asmresult5 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 5
+  %asmresult6 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 6
+  %asmresult7 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 7
+  %asmresult8 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 8
+  %asmresult9 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 9
+  %asmresult10 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 10
+  %asmresult11 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 11
+  %asmresult12 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 12
+  %asmresult13 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 13
+  %asmresult14 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 14
+  store volatile i32 %asmresult, ptr %reg0, align 4
+  store volatile i32 %asmresult1, ptr %reg1, align 4
+  store volatile i32 %asmresult2, ptr %reg2, align 4
+  store volatile i32 %asmresult3, ptr %reg3, align 4
+  store volatile i32 %asmresult4, ptr %reg4, align 4
+  store volatile i32 %asmresult5, ptr %reg5, align 4
+  store volatile i32 %asmresult6, ptr %reg6, align 4
+  store volatile i32 %asmresult7, ptr %reg7, align 4
+  store volatile i32 %asmresult8, ptr %reg8, align 4
+  store volatile i32 %asmresult9, ptr %reg9, align 4
+  store volatile i32 %asmresult10, ptr %reg10, align 4
+  store volatile i32 %asmresult11, ptr %reg11, align 4
+  store volatile i32 %asmresult12, ptr %reg12, align 4
+  store volatile i32 %asmresult13, ptr %reg13, align 4
+  store volatile i32 %asmresult14, ptr %reg14, align 4
+  ret i32 0
+}
+
+define i32 @test_non_leaf_no_reserve() "frame-pointer"="non-leaf-no-reserve" {
+; CHECK-LABEL: test_non_leaf_no_reserve:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    pushq %rbp
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    pushq %r15
+; CHECK-NEXT:    .cfi_def_cfa_offset 24
+; CHECK-NEXT:    pushq %r14
+; CHECK-NEXT:    .cfi_def_cfa_offset 32
+; CHECK-NEXT:    pushq %r13
+; CHECK-NEXT:    .cfi_def_cfa_offset 40
+; CHECK-NEXT:    pushq %r12
+; CHECK-NEXT:    .cfi_def_cfa_offset 48
+; CHECK-NEXT:    pushq %rbx
+; CHECK-NEXT:    .cfi_def_cfa_offset 56
+; CHECK-NEXT:    .cfi_offset %rbx, -56
+; CHECK-NEXT:    .cfi_offset %r12, -48
+; CHECK-NEXT:    .cfi_offset %r13, -40
+; CHECK-NEXT:    .cfi_offset %r14, -32
+; CHECK-NEXT:    .cfi_offset %r15, -24
+; CHECK-NEXT:    .cfi_offset %rbp, -16
+; CHECK-NEXT:    movl $0, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $1, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $2, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $3, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $4, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $5, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $6, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $7, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $8, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $9, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $16, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $17, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $18, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $19, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $20, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %eax
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %ecx
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %edx
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %esi
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %edi
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r8d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r9d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r10d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r11d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %ebx
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %ebp
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r14d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r15d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r12d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r13d
+; CHECK-NEXT:    #APP
+; CHECK-NEXT:    nop
+; CHECK-NEXT:    #NO_APP
+; CHECK-NEXT:    movl %eax, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %ecx, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %edx, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %esi, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %edi, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r8d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r9d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r10d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r11d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %ebx, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %ebp, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r14d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r15d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r12d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r13d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    xorl %eax, %eax
+; CHECK-NEXT:    popq %rbx
+; CHECK-NEXT:    .cfi_def_cfa_offset 48
+; CHECK-NEXT:    popq %r12
+; CHECK-NEXT:    .cfi_def_cfa_offset 40
+; CHECK-NEXT:    popq %r13
+; CHECK-NEXT:    .cfi_def_cfa_offset 32
+; CHECK-NEXT:    popq %r14
+; CHECK-NEXT:    .cfi_def_cfa_offset 24
+; CHECK-NEXT:    popq %r15
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    popq %rbp
+; CHECK-NEXT:    .cfi_def_cfa_offset 8
+; CHECK-NEXT:    retq
+entry:
+  %reg0 = alloca i32, align 4
+  %reg1 = alloca i32, align 4
+  %reg2 = alloca i32, align 4
+  %reg3 = alloca i32, align 4
+  %reg4 = alloca i32, align 4
+  %reg5 = alloca i32, align 4
+  %reg6 = alloca i32, align 4
+  %reg7 = alloca i32, align 4
+  %reg8 = alloca i32, align 4
+  %reg9 = alloca i32, align 4
+  %reg10 = alloca i32, align 4
+  %reg11 = alloca i32, align 4
+  %reg12 = alloca i32, align 4
+  %reg13 = alloca i32, align 4
+  %reg14 = alloca i32, align 4
+  store volatile i32 0, ptr %reg0, align 4
+  store volatile i32 1, ptr %reg1, align 4
+  store volatile i32 2, ptr %reg2, align 4
+  store volatile i32 3, ptr %reg3, align 4
+  store volatile i32 4, ptr %reg4, align 4
+  store volatile i32 5, ptr %reg5, align 4
+  store volatile i32 6, ptr %reg6, align 4
+  store volatile i32 7, ptr %reg7, align 4
+  store volatile i32 8, ptr %reg8, align 4
+  store volatile i32 9, ptr %reg9, align 4
+  store volatile i32 16, ptr %reg10, align 4
+  store volatile i32 17, ptr %reg11, align 4
+  store volatile i32 18, ptr %reg12, align 4
+  store volatile i32 19, ptr %reg13, align 4
+  store volatile i32 20, ptr %reg14, align 4
+  %0 = load volatile i32, ptr %reg0, align 4
+  %1 = load volatile i32, ptr %reg1, align 4
+  %2 = load volatile i32, ptr %reg2, align 4
+  %3 = load volatile i32, ptr %reg3, align 4
+  %4 = load volatile i32, ptr %reg4, align 4
+  %5 = load volatile i32, ptr %reg5, align 4
+  %6 = load volatile i32, ptr %reg6, align 4
+  %7 = load volatile i32, ptr %reg7, align 4
+  %8 = load volatile i32, ptr %reg8, align 4
+  %9 = load volatile i32, ptr %reg9, align 4
+  %10 = load volatile i32, ptr %reg10, align 4
+  %11 = load volatile i32, ptr %reg11, align 4
+  %12 = load volatile i32, ptr %reg12, align 4
+  %13 = load volatile i32, ptr %reg13, align 4
+  %14 = load volatile i32, ptr %reg14, align 4
+  %15 = call { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } asm "nop", "=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,~{dirflag},~{fpsr},~{flags}"(i32 %0, i32 %1, i32 %2, i32 %3, i32 %4, i32 %5, i32 %6, i32 %7, i32 %8, i32 %9, i32 %10, i32 %11, i32 %12, i32 %13, i32 %14) #1
+  %asmresult = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 0
+  %asmresult1 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 1
+  %asmresult2 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 2
+  %asmresult3 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 3
+  %asmresult4 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 4
+  %asmresult5 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 5
+  %asmresult6 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 6
+  %asmresult7 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 7
+  %asmresult8 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 8
+  %asmresult9 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 9
+  %asmresult10 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 10
+  %asmresult11 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 11
+  %asmresult12 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 12
+  %asmresult13 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 13
+  %asmresult14 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %15, 14
+  store volatile i32 %asmresult, ptr %reg0, align 4
+  store volatile i32 %asmresult1, ptr %reg1, align 4
+  store volatile i32 %asmresult2, ptr %reg2, align 4
+  store volatile i32 %asmresult3, ptr %reg3, align 4
+  store volatile i32 %asmresult4, ptr %reg4, align 4
+  store volatile i32 %asmresult5, ptr %reg5, align 4
+  store volatile i32 %asmresult6, ptr %reg6, align 4
+  store volatile i32 %asmresult7, ptr %reg7, align 4
+  store volatile i32 %asmresult8, ptr %reg8, align 4
+  store volatile i32 %asmresult9, ptr %reg9, align 4
+  store volatile i32 %asmresult10, ptr %reg10, align 4
+  store volatile i32 %asmresult11, ptr %reg11, align 4
+  store volatile i32 %asmresult12, ptr %reg12, align 4
+  store volatile i32 %asmresult13, ptr %reg13, align 4
+  store volatile i32 %asmresult14, ptr %reg14, align 4
+  ret i32 0
+}
+
+define i32 @test_non_leaf() "frame-pointer"="non-leaf" {
+; CHECK-LABEL: test_non_leaf:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    pushq %r15
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    pushq %r14
+; CHECK-NEXT:    .cfi_def_cfa_offset 24
+; CHECK-NEXT:    pushq %r13
+; CHECK-NEXT:    .cfi_def_cfa_offset 32
+; CHECK-NEXT:    pushq %r12
+; CHECK-NEXT:    .cfi_def_cfa_offset 40
+; CHECK-NEXT:    pushq %rbx
+; CHECK-NEXT:    .cfi_def_cfa_offset 48
+; CHECK-NEXT:    .cfi_offset %rbx, -48
+; CHECK-NEXT:    .cfi_offset %r12, -40
+; CHECK-NEXT:    .cfi_offset %r13, -32
+; CHECK-NEXT:    .cfi_offset %r14, -24
+; CHECK-NEXT:    .cfi_offset %r15, -16
+; CHECK-NEXT:    movl $0, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $1, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $2, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $3, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $4, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $5, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $6, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $7, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $8, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $9, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $16, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $17, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $18, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $19, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %eax
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %ecx
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %edx
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %esi
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %edi
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r8d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r9d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r10d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r11d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %ebx
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r14d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r15d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r12d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r13d
+; CHECK-NEXT:    #APP
+; CHECK-NEXT:    nop
+; CHECK-NEXT:    #NO_APP
+; CHECK-NEXT:    movl %eax, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %ecx, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %edx, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %esi, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %edi, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r8d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r9d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r10d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r11d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %ebx, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r14d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r15d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r12d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r13d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    xorl %eax, %eax
+; CHECK-NEXT:    popq %rbx
+; CHECK-NEXT:    .cfi_def_cfa_offset 40
+; CHECK-NEXT:    popq %r12
+; CHECK-NEXT:    .cfi_def_cfa_offset 32
+; CHECK-NEXT:    popq %r13
+; CHECK-NEXT:    .cfi_def_cfa_offset 24
+; CHECK-NEXT:    popq %r14
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    popq %r15
+; CHECK-NEXT:    .cfi_def_cfa_offset 8
+; CHECK-NEXT:    retq
+entry:
+  %reg0 = alloca i32, align 4
+  %reg1 = alloca i32, align 4
+  %reg2 = alloca i32, align 4
+  %reg3 = alloca i32, align 4
+  %reg4 = alloca i32, align 4
+  %reg5 = alloca i32, align 4
+  %reg6 = alloca i32, align 4
+  %reg7 = alloca i32, align 4
+  %reg8 = alloca i32, align 4
+  %reg9 = alloca i32, align 4
+  %reg10 = alloca i32, align 4
+  %reg11 = alloca i32, align 4
+  %reg12 = alloca i32, align 4
+  %reg13 = alloca i32, align 4
+  store volatile i32 0, ptr %reg0, align 4
+  store volatile i32 1, ptr %reg1, align 4
+  store volatile i32 2, ptr %reg2, align 4
+  store volatile i32 3, ptr %reg3, align 4
+  store volatile i32 4, ptr %reg4, align 4
+  store volatile i32 5, ptr %reg5, align 4
+  store volatile i32 6, ptr %reg6, align 4
+  store volatile i32 7, ptr %reg7, align 4
+  store volatile i32 8, ptr %reg8, align 4
+  store volatile i32 9, ptr %reg9, align 4
+  store volatile i32 16, ptr %reg10, align 4
+  store volatile i32 17, ptr %reg11, align 4
+  store volatile i32 18, ptr %reg12, align 4
+  store volatile i32 19, ptr %reg13, align 4
+  %0 = load volatile i32, ptr %reg0, align 4
+  %1 = load volatile i32, ptr %reg1, align 4
+  %2 = load volatile i32, ptr %reg2, align 4
+  %3 = load volatile i32, ptr %reg3, align 4
+  %4 = load volatile i32, ptr %reg4, align 4
+  %5 = load volatile i32, ptr %reg5, align 4
+  %6 = load volatile i32, ptr %reg6, align 4
+  %7 = load volatile i32, ptr %reg7, align 4
+  %8 = load volatile i32, ptr %reg8, align 4
+  %9 = load volatile i32, ptr %reg9, align 4
+  %10 = load volatile i32, ptr %reg10, align 4
+  %11 = load volatile i32, ptr %reg11, align 4
+  %12 = load volatile i32, ptr %reg12, align 4
+  %13 = load volatile i32, ptr %reg13, align 4
+  %14 = call { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } asm "nop", "=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,0,1,2,3,4,5,6,7,8,9,10,11,12,13,~{dirflag},~{fpsr},~{flags}"(i32 %0, i32 %1, i32 %2, i32 %3, i32 %4, i32 %5, i32 %6, i32 %7, i32 %8, i32 %9, i32 %10, i32 %11, i32 %12, i32 %13) #1
+  %asmresult = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 0
+  %asmresult1 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 1
+  %asmresult2 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 2
+  %asmresult3 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 3
+  %asmresult4 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 4
+  %asmresult5 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 5
+  %asmresult6 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 6
+  %asmresult7 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 7
+  %asmresult8 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 8
+  %asmresult9 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 9
+  %asmresult10 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 10
+  %asmresult11 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 11
+  %asmresult12 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 12
+  %asmresult13 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 13
+  store volatile i32 %asmresult, ptr %reg0, align 4
+  store volatile i32 %asmresult1, ptr %reg1, align 4
+  store volatile i32 %asmresult2, ptr %reg2, align 4
+  store volatile i32 %asmresult3, ptr %reg3, align 4
+  store volatile i32 %asmresult4, ptr %reg4, align 4
+  store volatile i32 %asmresult5, ptr %reg5, align 4
+  store volatile i32 %asmresult6, ptr %reg6, align 4
+  store volatile i32 %asmresult7, ptr %reg7, align 4
+  store volatile i32 %asmresult8, ptr %reg8, align 4
+  store volatile i32 %asmresult9, ptr %reg9, align 4
+  store volatile i32 %asmresult10, ptr %reg10, align 4
+  store volatile i32 %asmresult11, ptr %reg11, align 4
+  store volatile i32 %asmresult12, ptr %reg12, align 4
+  store volatile i32 %asmresult13, ptr %reg13, align 4
+  ret i32 0
+}
+
+define i32 @test_reserved() "frame-pointer"="reserved" {
+; CHECK-LABEL: test_reserved:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    pushq %r15
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    pushq %r14
+; CHECK-NEXT:    .cfi_def_cfa_offset 24
+; CHECK-NEXT:    pushq %r13
+; CHECK-NEXT:    .cfi_def_cfa_offset 32
+; CHECK-NEXT:    pushq %r12
+; CHECK-NEXT:    .cfi_def_cfa_offset 40
+; CHECK-NEXT:    pushq %rbx
+; CHECK-NEXT:    .cfi_def_cfa_offset 48
+; CHECK-NEXT:    .cfi_offset %rbx, -48
+; CHECK-NEXT:    .cfi_offset %r12, -40
+; CHECK-NEXT:    .cfi_offset %r13, -32
+; CHECK-NEXT:    .cfi_offset %r14, -24
+; CHECK-NEXT:    .cfi_offset %r15, -16
+; CHECK-NEXT:    movl $0, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $1, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $2, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $3, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $4, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $5, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $6, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $7, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $8, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $9, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $16, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $17, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $18, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl $19, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %eax
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %ecx
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %edx
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %esi
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %edi
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r8d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r9d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r10d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r11d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %ebx
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r14d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r15d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r12d
+; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %r13d
+; CHECK-NEXT:    #APP
+; CHECK-NEXT:    nop
+; CHECK-NEXT:    #NO_APP
+; CHECK-NEXT:    movl %eax, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %ecx, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %edx, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %esi, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %edi, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r8d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r9d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r10d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r11d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %ebx, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r14d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r15d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r12d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    movl %r13d, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:    xorl %eax, %eax
+; CHECK-NEXT:    popq %rbx
+; CHECK-NEXT:    .cfi_def_cfa_offset 40
+; CHECK-NEXT:    popq %r12
+; CHECK-NEXT:    .cfi_def_cfa_offset 32
+; CHECK-NEXT:    popq %r13
+; CHECK-NEXT:    .cfi_def_cfa_offset 24
+; CHECK-NEXT:    popq %r14
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    popq %r15
+; CHECK-NEXT:    .cfi_def_cfa_offset 8
+; CHECK-NEXT:    retq
+entry:
+  %reg0 = alloca i32, align 4
+  %reg1 = alloca i32, align 4
+  %reg2 = alloca i32, align 4
+  %reg3 = alloca i32, align 4
+  %reg4 = alloca i32, align 4
+  %reg5 = alloca i32, align 4
+  %reg6 = alloca i32, align 4
+  %reg7 = alloca i32, align 4
+  %reg8 = alloca i32, align 4
+  %reg9 = alloca i32, align 4
+  %reg10 = alloca i32, align 4
+  %reg11 = alloca i32, align 4
+  %reg12 = alloca i32, align 4
+  %reg13 = alloca i32, align 4
+  store volatile i32 0, ptr %reg0, align 4
+  store volatile i32 1, ptr %reg1, align 4
+  store volatile i32 2, ptr %reg2, align 4
+  store volatile i32 3, ptr %reg3, align 4
+  store volatile i32 4, ptr %reg4, align 4
+  store volatile i32 5, ptr %reg5, align 4
+  store volatile i32 6, ptr %reg6, align 4
+  store volatile i32 7, ptr %reg7, align 4
+  store volatile i32 8, ptr %reg8, align 4
+  store volatile i32 9, ptr %reg9, align 4
+  store volatile i32 16, ptr %reg10, align 4
+  store volatile i32 17, ptr %reg11, align 4
+  store volatile i32 18, ptr %reg12, align 4
+  store volatile i32 19, ptr %reg13, align 4
+  %0 = load volatile i32, ptr %reg0, align 4
+  %1 = load volatile i32, ptr %reg1, align 4
+  %2 = load volatile i32, ptr %reg2, align 4
+  %3 = load volatile i32, ptr %reg3, align 4
+  %4 = load volatile i32, ptr %reg4, align 4
+  %5 = load volatile i32, ptr %reg5, align 4
+  %6 = load volatile i32, ptr %reg6, align 4
+  %7 = load volatile i32, ptr %reg7, align 4
+  %8 = load volatile i32, ptr %reg8, align 4
+  %9 = load volatile i32, ptr %reg9, align 4
+  %10 = load volatile i32, ptr %reg10, align 4
+  %11 = load volatile i32, ptr %reg11, align 4
+  %12 = load volatile i32, ptr %reg12, align 4
+  %13 = load volatile i32, ptr %reg13, align 4
+  %14 = call { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } asm "nop", "=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,0,1,2,3,4,5,6,7,8,9,10,11,12,13,~{dirflag},~{fpsr},~{flags}"(i32 %0, i32 %1, i32 %2, i32 %3, i32 %4, i32 %5, i32 %6, i32 %7, i32 %8, i32 %9, i32 %10, i32 %11, i32 %12, i32 %13) #1
+  %asmresult = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 0
+  %asmresult1 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 1
+  %asmresult2 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 2
+  %asmresult3 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 3
+  %asmresult4 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 4
+  %asmresult5 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 5
+  %asmresult6 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 6
+  %asmresult7 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 7
+  %asmresult8 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 8
+  %asmresult9 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 9
+  %asmresult10 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 10
+  %asmresult11 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 11
+  %asmresult12 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 12
+  %asmresult13 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 13
+  store volatile i32 %asmresult, ptr %reg0, align 4
+  store volatile i32 %asmresult1, ptr %reg1, align 4
+  store volatile i32 %asmresult2, ptr %reg2, align 4
+  store volatile i32 %asmresult3, ptr %reg3, align 4
+  store volatile i32 %asmresult4, ptr %reg4, align 4
+  store volatile i32 %asmresult5, ptr %reg5, align 4
+  store volatile i32 %asmresult6, ptr %reg6, align 4
+  store volatile i32 %asmresult7, ptr %reg7, align 4
+  store volatile i32 %asmresult8, ptr %reg8, align 4
+  store volatile i32 %asmresult9, ptr %reg9, align 4
+  store volatile i32 %asmresult10, ptr %reg10, align 4
+  store volatile i32 %asmresult11, ptr %reg11, align 4
+  store volatile i32 %asmresult12, ptr %reg12, align 4
+  store volatile i32 %asmresult13, ptr %reg13, align 4
+  ret i32 0
+}
+
+define i32 @test_all() "frame-pointer"="all" {
+; CHECK-LABEL: test_all:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    pushq %rbp
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    .cfi_offset %rbp, -16
+; CHECK-NEXT:    movq %rsp, %rbp
+; CHECK-NEXT:    .cfi_def_cfa_register %rbp
+; CHECK-NEXT:    pushq %r15
+; CHECK-NEXT:    pushq %r14
+; CHECK-NEXT:    pushq %r13
+; CHECK-NEXT:    pushq %r12
+; CHECK-NEXT:    pushq %rbx
+; CHECK-NEXT:    .cfi_offset %rbx, -56
+; CHECK-NEXT:    .cfi_offset %r12, -48
+; CHECK-NEXT:    .cfi_offset %r13, -40
+; CHECK-NEXT:    .cfi_offset %r14, -32
+; CHECK-NEXT:    .cfi_offset %r15, -24
+; CHECK-NEXT:    movl $0, -96(%rbp)
+; CHECK-NEXT:    movl $1, -92(%rbp)
+; CHECK-NEXT:    movl $2, -88(%rbp)
+; CHECK-NEXT:    movl $3, -84(%rbp)
+; CHECK-NEXT:    movl $4, -80(%rbp)
+; CHECK-NEXT:    movl $5, -76(%rbp)
+; CHECK-NEXT:    movl $6, -72(%rbp)
+; CHECK-NEXT:    movl $7, -68(%rbp)
+; CHECK-NEXT:    movl $8, -64(%rbp)
+; CHECK-NEXT:    movl $9, -60(%rbp)
+; CHECK-NEXT:    movl $16, -56(%rbp)
+; CHECK-NEXT:    movl $17, -52(%rbp)
+; CHECK-NEXT:    movl $18, -48(%rbp)
+; CHECK-NEXT:    movl $19, -44(%rbp)
+; CHECK-NEXT:    movl -96(%rbp), %eax
+; CHECK-NEXT:    movl -92(%rbp), %ecx
+; CHECK-NEXT:    movl -88(%rbp), %edx
+; CHECK-NEXT:    movl -84(%rbp), %esi
+; CHECK-NEXT:    movl -80(%rbp), %edi
+; CHECK-NEXT:    movl -76(%rbp), %r8d
+; CHECK-NEXT:    movl -72(%rbp), %r9d
+; CHECK-NEXT:    movl -68(%rbp), %r10d
+; CHECK-NEXT:    movl -64(%rbp), %r11d
+; CHECK-NEXT:    movl -60(%rbp), %ebx
+; CHECK-NEXT:    movl -56(%rbp), %r14d
+; CHECK-NEXT:    movl -52(%rbp), %r15d
+; CHECK-NEXT:    movl -48(%rbp), %r12d
+; CHECK-NEXT:    movl -44(%rbp), %r13d
+; CHECK-NEXT:    #APP
+; CHECK-NEXT:    nop
+; CHECK-NEXT:    #NO_APP
+; CHECK-NEXT:    movl %eax, -96(%rbp)
+; CHECK-NEXT:    movl %ecx, -92(%rbp)
+; CHECK-NEXT:    movl %edx, -88(%rbp)
+; CHECK-NEXT:    movl %esi, -84(%rbp)
+; CHECK-NEXT:    movl %edi, -80(%rbp)
+; CHECK-NEXT:    movl %r8d, -76(%rbp)
+; CHECK-NEXT:    movl %r9d, -72(%rbp)
+; CHECK-NEXT:    movl %r10d, -68(%rbp)
+; CHECK-NEXT:    movl %r11d, -64(%rbp)
+; CHECK-NEXT:    movl %ebx, -60(%rbp)
+; CHECK-NEXT:    movl %r14d, -56(%rbp)
+; CHECK-NEXT:    movl %r15d, -52(%rbp)
+; CHECK-NEXT:    movl %r12d, -48(%rbp)
+; CHECK-NEXT:    movl %r13d, -44(%rbp)
+; CHECK-NEXT:    xorl %eax, %eax
+; CHECK-NEXT:    popq %rbx
+; CHECK-NEXT:    popq %r12
+; CHECK-NEXT:    popq %r13
+; CHECK-NEXT:    popq %r14
+; CHECK-NEXT:    popq %r15
+; CHECK-NEXT:    popq %rbp
+; CHECK-NEXT:    .cfi_def_cfa %rsp, 8
+; CHECK-NEXT:    retq
+entry:
+  %reg0 = alloca i32, align 4
+  %reg1 = alloca i32, align 4
+  %reg2 = alloca i32, align 4
+  %reg3 = alloca i32, align 4
+  %reg4 = alloca i32, align 4
+  %reg5 = alloca i32, align 4
+  %reg6 = alloca i32, align 4
+  %reg7 = alloca i32, align 4
+  %reg8 = alloca i32, align 4
+  %reg9 = alloca i32, align 4
+  %reg10 = alloca i32, align 4
+  %reg11 = alloca i32, align 4
+  %reg12 = alloca i32, align 4
+  %reg13 = alloca i32, align 4
+  store volatile i32 0, ptr %reg0, align 4
+  store volatile i32 1, ptr %reg1, align 4
+  store volatile i32 2, ptr %reg2, align 4
+  store volatile i32 3, ptr %reg3, align 4
+  store volatile i32 4, ptr %reg4, align 4
+  store volatile i32 5, ptr %reg5, align 4
+  store volatile i32 6, ptr %reg6, align 4
+  store volatile i32 7, ptr %reg7, align 4
+  store volatile i32 8, ptr %reg8, align 4
+  store volatile i32 9, ptr %reg9, align 4
+  store volatile i32 16, ptr %reg10, align 4
+  store volatile i32 17, ptr %reg11, align 4
+  store volatile i32 18, ptr %reg12, align 4
+  store volatile i32 19, ptr %reg13, align 4
+  %0 = load volatile i32, ptr %reg0, align 4
+  %1 = load volatile i32, ptr %reg1, align 4
+  %2 = load volatile i32, ptr %reg2, align 4
+  %3 = load volatile i32, ptr %reg3, align 4
+  %4 = load volatile i32, ptr %reg4, align 4
+  %5 = load volatile i32, ptr %reg5, align 4
+  %6 = load volatile i32, ptr %reg6, align 4
+  %7 = load volatile i32, ptr %reg7, align 4
+  %8 = load volatile i32, ptr %reg8, align 4
+  %9 = load volatile i32, ptr %reg9, align 4
+  %10 = load volatile i32, ptr %reg10, align 4
+  %11 = load volatile i32, ptr %reg11, align 4
+  %12 = load volatile i32, ptr %reg12, align 4
+  %13 = load volatile i32, ptr %reg13, align 4
+  %14 = call { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } asm "nop", "=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,=r,0,1,2,3,4,5,6,7,8,9,10,11,12,13,~{dirflag},~{fpsr},~{flags}"(i32 %0, i32 %1, i32 %2, i32 %3, i32 %4, i32 %5, i32 %6, i32 %7, i32 %8, i32 %9, i32 %10, i32 %11, i32 %12, i32 %13) #1
+  %asmresult = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 0
+  %asmresult1 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 1
+  %asmresult2 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 2
+  %asmresult3 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 3
+  %asmresult4 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 4
+  %asmresult5 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 5
+  %asmresult6 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 6
+  %asmresult7 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 7
+  %asmresult8 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 8
+  %asmresult9 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 9
+  %asmresult10 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 10
+  %asmresult11 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 11
+  %asmresult12 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 12
+  %asmresult13 = extractvalue { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } %14, 13
+  store volatile i32 %asmresult, ptr %reg0, align 4
+  store volatile i32 %asmresult1, ptr %reg1, align 4
+  store volatile i32 %asmresult2, ptr %reg2, align 4
+  store volatile i32 %asmresult3, ptr %reg3, align 4
+  store volatile i32 %asmresult4, ptr %reg4, align 4
+  store volatile i32 %asmresult5, ptr %reg5, align 4
+  store volatile i32 %asmresult6, ptr %reg6, align 4
+  store volatile i32 %asmresult7, ptr %reg7, align 4
+  store volatile i32 %asmresult8, ptr %reg8, align 4
+  store volatile i32 %asmresult9, ptr %reg9, align 4
+  store volatile i32 %asmresult10, ptr %reg10, align 4
+  store volatile i32 %asmresult11, ptr %reg11, align 4
+  store volatile i32 %asmresult12, ptr %reg12, align 4
+  store volatile i32 %asmresult13, ptr %reg13, align 4
+  ret i32 0
+}

>From 20fe190f084347b20969f00021b9577988bd764b Mon Sep 17 00:00:00 2001
From: Nabeel Omer <Nabeel.Omer at sony.com>
Date: Thu, 16 Oct 2025 14:21:20 +0100
Subject: [PATCH 2/6] Fix formatting

---
 clang/include/clang/Basic/CodeGenOptions.h | 12 +++++---
 clang/lib/Driver/ToolChains/CommonArgs.cpp | 36 ++++++++++++----------
 llvm/include/llvm/Support/CodeGen.h        |  8 ++++-
 llvm/lib/CodeGen/CommandFlags.cpp          |  3 +-
 llvm/lib/IR/Verifier.cpp                   |  3 +-
 5 files changed, 37 insertions(+), 25 deletions(-)

diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 3dd0140ea0d9f..0eb48dc377ae8 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -155,11 +155,13 @@ class CodeGenOptions : public CodeGenOptionsBase {
   std::string BinutilsVersion;
 
   enum class FramePointerKind {
-    NonLeafNoReserve, // Keep non-leaf frame pointers, allow the FP to be used as a GPR in leaf functions.
-    None,     // Omit all frame pointers.
-    Reserved, // Maintain valid frame pointer chain.
-    NonLeaf,  // Keep non-leaf frame pointers, don't allow the FP to be used as a GPR in leaf functions.
-    All,      // Keep all frame pointers.
+    NonLeafNoReserve, // Keep non-leaf frame pointers, allow the FP to be used
+                      // as a GPR in leaf functions.
+    None,             // Omit all frame pointers.
+    Reserved,         // Maintain valid frame pointer chain.
+    NonLeaf, // Keep non-leaf frame pointers, don't allow the FP to be used as a
+             // GPR in leaf functions.
+    All,     // Keep all frame pointers.
   };
 
   static StringRef getFramePointerKindName(FramePointerKind Kind) {
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 55a57acebfd9a..4172f7950a545 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -226,7 +226,8 @@ getFramePointerKind(const llvm::opt::ArgList &Args,
   // * Should a frame record be created for non-leaf functions?
   // * Should a frame record be created for leaf functions?
   // * Is the frame pointer register reserved in non-leaf functions?
-  //   i.e. must it always point to either a new, valid frame record or be un-modified?
+  //   i.e. must it always point to either a new, valid frame record or be
+  //   un-modified?
   // * Is the frame pointer register reserved in leaf functions?
   //
   //  Not all combinations of these are valid:
@@ -236,22 +237,23 @@ getFramePointerKind(const llvm::opt::ArgList &Args,
   //
   // | Non-leaf | Leaf | Reserved In Non-Leaf  | Reserved In Leaf |
   // |----------|------|-----------------------|------------------|
-  // | N        | N    | N                     | N                | FramePointerKind::None
-  // | N        | N    | N                     | Y                | Invalid
-  // | N        | N    | Y                     | N                | Invalid
-  // | N        | N    | Y                     | Y                | FramePointerKind::Reserved
-  // | N        | Y    | N                     | N                | Invalid
-  // | N        | Y    | N                     | Y                | Invalid
-  // | N        | Y    | Y                     | N                | Invalid
-  // | N        | Y    | Y                     | Y                | Invalid
-  // | Y        | N    | N                     | N                | Invalid
-  // | Y        | N    | N                     | Y                | Invalid
-  // | Y        | N    | Y                     | N                | FramePointerKind::NonLeafNoReserve
-  // | Y        | N    | Y                     | Y                | FramePointerKind::NonLeaf
-  // | Y        | Y    | N                     | N                | Invalid
-  // | Y        | Y    | N                     | Y                | Invalid
-  // | Y        | Y    | Y                     | N                | Invalid
-  // | Y        | Y    | Y                     | Y                | FramePointerKind::All
+  // | N        | N    | N                     | N                |
+  // FramePointerKind::None | N        | N    | N                     | Y |
+  // Invalid | N        | N    | Y                     | N                |
+  // Invalid | N        | N    | Y                     | Y                |
+  // FramePointerKind::Reserved | N        | Y    | N                     | N |
+  // Invalid | N        | Y    | N                     | Y                |
+  // Invalid | N        | Y    | Y                     | N                |
+  // Invalid | N        | Y    | Y                     | Y                |
+  // Invalid | Y        | N    | N                     | N                |
+  // Invalid | Y        | N    | N                     | Y                |
+  // Invalid | Y        | N    | Y                     | N                |
+  // FramePointerKind::NonLeafNoReserve | Y        | N    | Y | Y |
+  // FramePointerKind::NonLeaf | Y        | Y    | N                     | N |
+  // Invalid | Y        | Y    | N                     | Y                |
+  // Invalid | Y        | Y    | Y                     | N                |
+  // Invalid | Y        | Y    | Y                     | Y                |
+  // FramePointerKind::All
   //
   // The FramePointerKind::Reserved case is currently only reachable for Arm,
   // which has the -mframe-chain= option which can (in combination with
diff --git a/llvm/include/llvm/Support/CodeGen.h b/llvm/include/llvm/Support/CodeGen.h
index 6675f131bc406..15df265556339 100644
--- a/llvm/include/llvm/Support/CodeGen.h
+++ b/llvm/include/llvm/Support/CodeGen.h
@@ -115,7 +115,13 @@ namespace llvm {
   };
 
   // Specify what functions should keep the frame pointer.
-  enum class FramePointerKind { None, NonLeaf, All, Reserved, NonLeafNoReserve };
+  enum class FramePointerKind {
+    None,
+    NonLeaf,
+    All,
+    Reserved,
+    NonLeafNoReserve
+  };
 
   // Specify what type of zeroing callee-used registers.
   namespace ZeroCallUsedRegs {
diff --git a/llvm/lib/CodeGen/CommandFlags.cpp b/llvm/lib/CodeGen/CommandFlags.cpp
index e3089dd1eb07b..6169d8e00dc33 100644
--- a/llvm/lib/CodeGen/CommandFlags.cpp
+++ b/llvm/lib/CodeGen/CommandFlags.cpp
@@ -211,7 +211,8 @@ codegen::RegisterCodeGenFlags::RegisterCodeGenFlags() {
           clEnumValN(FramePointerKind::All, "all",
                      "Disable frame pointer elimination"),
           clEnumValN(FramePointerKind::NonLeaf, "non-leaf",
-                     "Disable frame pointer elimination for non-leaf frame but reserve the register in leaf functions"),
+                     "Disable frame pointer elimination for non-leaf frame but "
+                     "reserve the register in leaf functions"),
           clEnumValN(FramePointerKind::NonLeafNoReserve, "non-leaf-no-reserve",
                      "Disable frame pointer elimination for non-leaf frame"),
           clEnumValN(FramePointerKind::Reserved, "reserved",
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 4a5e0b6bdae97..7cbc2c5f95a6f 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -2484,7 +2484,8 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs,
 
   if (Attribute FPAttr = Attrs.getFnAttr("frame-pointer"); FPAttr.isValid()) {
     StringRef FP = FPAttr.getValueAsString();
-    if (FP != "all" && FP != "non-leaf" && FP != "none" && FP != "reserved" && FP != "non-leaf-no-reserve")
+    if (FP != "all" && FP != "non-leaf" && FP != "none" && FP != "reserved" &&
+        FP != "non-leaf-no-reserve")
       CheckFailed("invalid value for 'frame-pointer' attribute: " + FP, V);
   }
 

>From c6fde3db39e335a9f023c950ffd8fed36601524f Mon Sep 17 00:00:00 2001
From: Nabeel Omer <Nabeel.Omer at sony.com>
Date: Mon, 20 Oct 2025 16:47:57 +0100
Subject: [PATCH 3/6] Add `-mreserve-frame-pointer-reg`

---
 clang/include/clang/Driver/Options.td      |  3 +
 clang/lib/Driver/ToolChains/CommonArgs.cpp | 46 ++++++++------
 clang/test/Driver/frame-pointer-elim.c     | 72 +++++++++++++---------
 3 files changed, 72 insertions(+), 49 deletions(-)

diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 5f7ef34a36be6..9084aadefaaa4 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -5657,6 +5657,9 @@ def mno_warn_nonportable_cfstrings : Flag<["-"], "mno-warn-nonportable-cfstrings
 def mno_omit_leaf_frame_pointer : Flag<["-"], "mno-omit-leaf-frame-pointer">, Group<m_Group>;
 def momit_leaf_frame_pointer : Flag<["-"], "momit-leaf-frame-pointer">, Group<m_Group>,
   HelpText<"Omit frame pointer setup for leaf functions">;
+def mno_reserve_frame_pointer_reg : Flag<["-"], "mno-reserve-frame-pointer-reg">, Group<m_Group>;
+def mreserve_frame_pointer_reg : Flag<["-"], "mreserve-frame-pointer-reg">, Group<m_Group>,
+  HelpText<"Reserve the frame pointer register even if the function doesn't have a frame">;
 def moslib_EQ : Joined<["-"], "moslib=">, Group<m_Group>;
 def mpascal_strings : Flag<["-"], "mpascal-strings">, Alias<fpascal_strings>;
 def mred_zone : Flag<["-"], "mred-zone">, Group<m_Group>;
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 4172f7950a545..aa3095d310a56 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -235,25 +235,26 @@ getFramePointerKind(const llvm::opt::ArgList &Args,
   //  * It's not useful to have frame records without reserving the frame
   //    pointer.
   //
-  // | Non-leaf | Leaf | Reserved In Non-Leaf  | Reserved In Leaf |
-  // |----------|------|-----------------------|------------------|
-  // | N        | N    | N                     | N                |
-  // FramePointerKind::None | N        | N    | N                     | Y |
-  // Invalid | N        | N    | Y                     | N                |
-  // Invalid | N        | N    | Y                     | Y                |
-  // FramePointerKind::Reserved | N        | Y    | N                     | N |
-  // Invalid | N        | Y    | N                     | Y                |
-  // Invalid | N        | Y    | Y                     | N                |
-  // Invalid | N        | Y    | Y                     | Y                |
-  // Invalid | Y        | N    | N                     | N                |
-  // Invalid | Y        | N    | N                     | Y                |
-  // Invalid | Y        | N    | Y                     | N                |
-  // FramePointerKind::NonLeafNoReserve | Y        | N    | Y | Y |
-  // FramePointerKind::NonLeaf | Y        | Y    | N                     | N |
-  // Invalid | Y        | Y    | N                     | Y                |
-  // Invalid | Y        | Y    | Y                     | N                |
-  // Invalid | Y        | Y    | Y                     | Y                |
-  // FramePointerKind::All
+  // | Frame Setup     | Reg Reserved    |
+  // |-----------------|-----------------|
+  // | Non-leaf | Leaf | Non-Leaf | Leaf |
+  // |----------|------|----------|------|
+  // | N        | N    | N        | N    | FramePointerKind::None
+  // | N        | N    | N        | Y    | Invalid
+  // | N        | N    | Y        | N    | Invalid
+  // | N        | N    | Y        | Y    | FramePointerKind::Reserved
+  // | N        | Y    | N        | N    | Invalid
+  // | N        | Y    | N        | Y    | Invalid
+  // | N        | Y    | Y        | N    | Invalid
+  // | N        | Y    | Y        | Y    | Invalid
+  // | Y        | N    | N        | N    | Invalid
+  // | Y        | N    | N        | Y    | Invalid
+  // | Y        | N    | Y        | N    | FramePointerKind::NonLeafNoReserve
+  // | Y        | N    | Y        | Y    | FramePointerKind::NonLeaf
+  // | Y        | Y    | N        | N    | Invalid
+  // | Y        | Y    | N        | Y    | Invalid
+  // | Y        | Y    | Y        | N    | Invalid
+  // | Y        | Y    | Y        | Y    | FramePointerKind::All
   //
   // The FramePointerKind::Reserved case is currently only reachable for Arm,
   // which has the -mframe-chain= option which can (in combination with
@@ -273,7 +274,12 @@ getFramePointerKind(const llvm::opt::ArgList &Args,
       clang::driver::options::OPT_mno_omit_leaf_frame_pointer,
       clang::driver::options::OPT_momit_leaf_frame_pointer, DefaultLeafFP);
 
-  bool FPRegReserved = mustMaintainValidFrameChain(Args, Triple);
+  bool FPRegReserved =
+      Args.hasFlag(clang::driver::options::OPT_mreserve_frame_pointer_reg,
+                   clang::driver::options::OPT_mno_reserve_frame_pointer_reg,
+                   false);
+  
+  FPRegReserved |= mustMaintainValidFrameChain(Args, Triple);
 
   if (EnableFP) {
     if (EnableLeafFP)
diff --git a/clang/test/Driver/frame-pointer-elim.c b/clang/test/Driver/frame-pointer-elim.c
index 9b0e6fa15b0d4..d5e093a58599c 100644
--- a/clang/test/Driver/frame-pointer-elim.c
+++ b/clang/test/Driver/frame-pointer-elim.c
@@ -1,7 +1,9 @@
 // KEEP-ALL-NOT:  warning: argument unused
 // KEEP-ALL:      "-mframe-pointer=all"
 // KEEP-NON-LEAF-NOT: warning: argument unused
-// KEEP-NON-LEAF: "-mframe-pointer=non-leaf-no-reserve"
+// KEEP-NON-LEAF: "-mframe-pointer=non-leaf"
+// KEEP-NON-LEAF-NO-RESERVE-NOT: warning: argument unused
+// KEEP-NON-LEAF-NO-RESERVE: "-mframe-pointer=non-leaf-no-reserve"
 // KEEP-NONE-NOT: warning: argument unused
 // KEEP-NONE:     "-mframe-pointer=none"
 // KEEP-RESERVED-NOT: warning: argument unused
@@ -24,19 +26,27 @@
 // -momit-leaf-frame-pointer omits leaf frame pointer.
 // -fno-omit-frame-pointer loses out to -momit-leaf-frame-pointer.
 // RUN: %clang -### --target=i386 -S -momit-leaf-frame-pointer %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=i386-linux -S -O1 -fno-omit-frame-pointer -momit-leaf-frame-pointer %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=i386-linux -S -O1 -momit-leaf-frame-pointer %s 2>&1 | \
 // RUN:   FileCheck --check-prefix=KEEP-NONE %s
 
+// -momit-leaf-frame-pointer -mreserve-frame-pointer-reg results in the frame pointer reg being reserved
+// RUN: %clang -### --target=i386 -S -momit-leaf-frame-pointer -mreserve-frame-pointer-reg %s 2>&1 | \
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+
+// -fomit-frame-pointer -mreserve-frame-pointer-reg results in the frame pointer reg being reserved
+// RUN: %clang -### --target=i386 -S -fomit-frame-pointer -mreserve-frame-pointer-reg %s 2>&1 | \
+// RUN:   FileCheck --check-prefix=KEEP-RESERVED %s
+
 // fno-omit-frame-pointer -momit-leaf-frame-pointer can be overwritten by
 // fomit-frame-pointer later on the command without warning
 // RUN: %clang -### --target=i386-linux -S -O1 -fno-omit-frame-pointer -momit-leaf-frame-pointer -fomit-frame-pointer %s 2>&1 | \
 // RUN:   FileCheck --check-prefix=KEEP-NONE %s
 
 // RUN: %clang -### --target=i386-linux -S -O1 -fno-omit-frame-pointer -momit-leaf-frame-pointer %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // Explicit or default -fomit-frame-pointer wins over -mno-omit-leaf-frame-pointer.
 // RUN: %clang -### --target=i386 -S %s -fomit-frame-pointer -mno-omit-leaf-frame-pointer 2>&1 | \
 // RUN:   FileCheck --check-prefix=KEEP-NONE %s
@@ -68,7 +78,7 @@
 // RUN:   FileCheck --check-prefix=KEEP-NONE %s
 
 // RUN: %clang -### --target=i386-darwin -S -momit-leaf-frame-pointer %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 
 // RUN: %clang -### -target armv7s-apple-ios -fomit-frame-pointer %s 2>&1 | \
 // RUN:   FileCheck --check-prefix=WARN-OMIT-7S %s
@@ -88,25 +98,25 @@
 // On AArch64, PS4, PS5, and VE, default to omitting the frame pointer on leaf
 // functions
 // RUN: %clang -### --target=aarch64 -S %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=x86_64-scei-ps4 -S %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=x86_64-scei-ps4 -S -O2 %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=x86_64-sie-ps5 -S %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=x86_64-sie-ps5 -S -O2 %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### -target aarch64-apple-darwin -arch arm64_32 -S %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=ve-unknown-linux-gnu -S %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=aarch64-linux-android -S %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=aarch64-linux-android -S -O2 %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=aarch64-linux-android -S -Os %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 
 // RUN: %clang -### --target=powerpc64 -S %s 2>&1 | \
 // RUN:   FileCheck --check-prefix=KEEP-ALL %s
@@ -161,9 +171,9 @@
 // RUN: %clang -### --target=armv7a-linux-androideabi- -mthumb -mbig-endian -O1 -S %s 2>&1 | \
 // RUN:   FileCheck --check-prefix=KEEP-ALL %s
 // RUN: %clang -### --target=riscv64-linux-android -O1 -S %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=riscv64-linux-android -mbig-endian -O1 -S %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 
 // On ARM backend bare metal targets, frame pointer is omitted
 // RUN: %clang -### --target=arm-arm-none-eabi -S %s 2>&1 | \
@@ -191,21 +201,21 @@
 
 // Check that for Apple bare metal targets, we're keeping frame pointers by default
 // RUN: %clang -### --target=armv6m-apple-none-macho -S %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=armv6m-apple-none-macho -S -fno-omit-frame-pointer %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=arm-apple-none-macho -S %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=arm-apple-none-macho -S -fno-omit-frame-pointer %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=armv6m-apple-none-macho -S -O1 %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=armv6m-apple-none-macho -S -O1 -fno-omit-frame-pointer %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=arm-apple-none-macho -S -O1 %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=arm-apple-none-macho -S -O1 -fno-omit-frame-pointer %s 2>&1 | \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 
 // RUN: %clang --target=armv7-apple-macho -### -S %s 2>&1	\
 // RUN:         -fomit-frame-pointer \
@@ -221,17 +231,21 @@
 
 // AArch64 bare metal targets behave like hosted targets
 // RUN: %clang -### --target=aarch64-none-elf -S %s 2>&1 |  \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=aarch64-none-elf -S -O1 %s 2>&1 |  \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=aarch64-none-elf -S -fno-omit-frame-pointer %s 2>&1 |  \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 // RUN: %clang -### --target=aarch64-none-elf -S -O1 -fno-omit-frame-pointer %s 2>&1 |  \
-// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF %s
+// RUN:   FileCheck --check-prefix=KEEP-NON-LEAF-NO-RESERVE %s
 
 // AArch64 Windows requires that the frame pointer be reserved
 // RUN: %clang -### --target=aarch64-pc-windows-msvc -S -fomit-frame-pointer %s 2>&1 |  \
 // RUN:   FileCheck --check-prefix=KEEP-RESERVED %s
 
+// AArch64 Windows requires that the frame pointer be reserved
+// RUN: %clang -### --target=aarch64-pc-windows-msvc -S -fomit-frame-pointer -mno-reserve-frame-pointer-reg %s 2>&1 |  \
+// RUN:   FileCheck --check-prefix=KEEP-RESERVED %s
+
 void f0() {}
 void f1() { f0(); }

>From 084c704f857caa72f1348d5645b98586a5e9568c Mon Sep 17 00:00:00 2001
From: Nabeel Omer <Nabeel.Omer at sony.com>
Date: Mon, 20 Oct 2025 16:54:42 +0100
Subject: [PATCH 4/6] Fix formatting

---
 clang/lib/Driver/ToolChains/CommonArgs.cpp | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index aa3095d310a56..e75edfa803526 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -274,11 +274,10 @@ getFramePointerKind(const llvm::opt::ArgList &Args,
       clang::driver::options::OPT_mno_omit_leaf_frame_pointer,
       clang::driver::options::OPT_momit_leaf_frame_pointer, DefaultLeafFP);
 
-  bool FPRegReserved =
-      Args.hasFlag(clang::driver::options::OPT_mreserve_frame_pointer_reg,
-                   clang::driver::options::OPT_mno_reserve_frame_pointer_reg,
-                   false);
-  
+  bool FPRegReserved = Args.hasFlag(
+      clang::driver::options::OPT_mreserve_frame_pointer_reg,
+      clang::driver::options::OPT_mno_reserve_frame_pointer_reg, false);
+
   FPRegReserved |= mustMaintainValidFrameChain(Args, Triple);
 
   if (EnableFP) {

>From ff4b02d305aa4394033d8d42974d7adec1326634 Mon Sep 17 00:00:00 2001
From: Nabeel Omer <Nabeel.Omer at sony.com>
Date: Fri, 24 Oct 2025 16:43:27 +0100
Subject: [PATCH 5/6] Address review comments

---
 clang/lib/Driver/ToolChains/CommonArgs.cpp | 5 ++---
 clang/test/Driver/frame-pointer-elim.c     | 1 +
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index e75edfa803526..e6f180bc10464 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -276,9 +276,8 @@ getFramePointerKind(const llvm::opt::ArgList &Args,
 
   bool FPRegReserved = Args.hasFlag(
       clang::driver::options::OPT_mreserve_frame_pointer_reg,
-      clang::driver::options::OPT_mno_reserve_frame_pointer_reg, false);
-
-  FPRegReserved |= mustMaintainValidFrameChain(Args, Triple);
+      clang::driver::options::OPT_mno_reserve_frame_pointer_reg,
+      mustMaintainValidFrameChain(Args, Triple));
 
   if (EnableFP) {
     if (EnableLeafFP)
diff --git a/clang/test/Driver/frame-pointer-elim.c b/clang/test/Driver/frame-pointer-elim.c
index d5e093a58599c..054d643781d20 100644
--- a/clang/test/Driver/frame-pointer-elim.c
+++ b/clang/test/Driver/frame-pointer-elim.c
@@ -244,6 +244,7 @@
 // RUN:   FileCheck --check-prefix=KEEP-RESERVED %s
 
 // AArch64 Windows requires that the frame pointer be reserved
+// But -mno-reserve-frame-pointer-reg should override the target platform default
 // RUN: %clang -### --target=aarch64-pc-windows-msvc -S -fomit-frame-pointer -mno-reserve-frame-pointer-reg %s 2>&1 |  \
 // RUN:   FileCheck --check-prefix=KEEP-RESERVED %s
 

>From edd81c63326cc9efce2f62b0eb77f3a64a49bfd8 Mon Sep 17 00:00:00 2001
From: Nabeel Omer <Nabeel.Omer at sony.com>
Date: Fri, 24 Oct 2025 17:28:21 +0100
Subject: [PATCH 6/6] Fix formatting

---
 clang/lib/Driver/ToolChains/CommonArgs.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index e6f180bc10464..a9b24844edc9b 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -274,10 +274,10 @@ getFramePointerKind(const llvm::opt::ArgList &Args,
       clang::driver::options::OPT_mno_omit_leaf_frame_pointer,
       clang::driver::options::OPT_momit_leaf_frame_pointer, DefaultLeafFP);
 
-  bool FPRegReserved = Args.hasFlag(
-      clang::driver::options::OPT_mreserve_frame_pointer_reg,
-      clang::driver::options::OPT_mno_reserve_frame_pointer_reg,
-      mustMaintainValidFrameChain(Args, Triple));
+  bool FPRegReserved =
+      Args.hasFlag(clang::driver::options::OPT_mreserve_frame_pointer_reg,
+                   clang::driver::options::OPT_mno_reserve_frame_pointer_reg,
+                   mustMaintainValidFrameChain(Args, Triple));
 
   if (EnableFP) {
     if (EnableLeafFP)



More information about the llvm-commits mailing list