https://github.com/kiranchandramohan updated 
https://github.com/llvm/llvm-project/pull/218870

>From f0080fbdcd073413d3e1b842ea5ed8e5e5240b29 Mon Sep 17 00:00:00 2001
From: Kiran Chandramohan <[email protected]>
Date: Wed, 26 Aug 2026 11:21:20 +0200
Subject: [PATCH] [LLVM][Clang][Flang] Move framepointer kind selection to
 LLVMFrontend

Move getFramePointerKind and its target-specific helpers from clangDriver
to LLVMFrontendDriver so they can be shared by the Clang and Flang
drivers.

Keep Clang-specific option parsing in clangDriver and pass normalized
options to the shared implementation.
---
 clang/include/clang/Driver/CommonArgs.h       |   5 +-
 clang/lib/Driver/CMakeLists.txt               |   1 +
 clang/lib/Driver/ToolChains/Clang.cpp         |  19 +-
 clang/lib/Driver/ToolChains/CommonArgs.cpp    | 260 +++---------------
 clang/lib/Driver/ToolChains/Flang.cpp         |  14 +-
 .../llvm/Frontend/Driver/CodeGenOptions.h     |  26 ++
 llvm/lib/Frontend/Driver/CodeGenOptions.cpp   | 232 ++++++++++++++++
 llvm/unittests/Frontend/CMakeLists.txt        |   2 +
 llvm/unittests/Frontend/FramePointerTest.cpp  |  94 +++++++
 9 files changed, 408 insertions(+), 245 deletions(-)
 create mode 100644 llvm/unittests/Frontend/FramePointerTest.cpp

diff --git a/clang/include/clang/Driver/CommonArgs.h 
b/clang/include/clang/Driver/CommonArgs.h
index be15d15a1661e..72041b4dc1c35 100644
--- a/clang/include/clang/Driver/CommonArgs.h
+++ b/clang/include/clang/Driver/CommonArgs.h
@@ -9,7 +9,6 @@
 #ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_COMMONARGS_H
 #define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_COMMONARGS_H
 
-#include "clang/Basic/CodeGenOptions.h"
 #include "clang/Driver/Driver.h"
 #include "clang/Driver/InputInfo.h"
 #include "clang/Driver/Multilib.h"
@@ -364,7 +363,7 @@ void constructLLVMLinkCommand(Compilation &C, const Tool &T,
 } // end namespace driver
 } // end namespace clang
 
-clang::CodeGenOptions::FramePointerKind
-getFramePointerKind(const llvm::opt::ArgList &Args, const llvm::Triple 
&Triple);
+llvm::FramePointerKind getFramePointerKind(const llvm::opt::ArgList &Args,
+                                           const llvm::Triple &Triple);
 
 #endif // LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_COMMONARGS_H
diff --git a/clang/lib/Driver/CMakeLists.txt b/clang/lib/Driver/CMakeLists.txt
index 506536cdc04f5..85817735de80b 100644
--- a/clang/lib/Driver/CMakeLists.txt
+++ b/clang/lib/Driver/CMakeLists.txt
@@ -1,5 +1,6 @@
 set(LLVM_LINK_COMPONENTS
   BinaryFormat
+  FrontendDriver
   MC
   Object
   Option
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index dabc8c8d964d6..5688baca40333 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6144,23 +6144,22 @@ void Clang::ConstructJob(Compilation &C, const 
JobAction &JA,
     }
   }
 
-  CodeGenOptions::FramePointerKind FPKeepKind =
-                  getFramePointerKind(Args, RawTriple);
+  llvm::FramePointerKind FPKeepKind = getFramePointerKind(Args, RawTriple);
   const char *FPKeepKindStr = nullptr;
   switch (FPKeepKind) {
-  case CodeGenOptions::FramePointerKind::None:
+  case llvm::FramePointerKind::None:
     FPKeepKindStr = "-mframe-pointer=none";
     break;
-  case CodeGenOptions::FramePointerKind::Reserved:
+  case llvm::FramePointerKind::Reserved:
     FPKeepKindStr = "-mframe-pointer=reserved";
     break;
-  case CodeGenOptions::FramePointerKind::NonLeafNoReserve:
+  case llvm::FramePointerKind::NonLeafNoReserve:
     FPKeepKindStr = "-mframe-pointer=non-leaf-no-reserve";
     break;
-  case CodeGenOptions::FramePointerKind::NonLeaf:
+  case llvm::FramePointerKind::NonLeaf:
     FPKeepKindStr = "-mframe-pointer=non-leaf";
     break;
-  case CodeGenOptions::FramePointerKind::All:
+  case llvm::FramePointerKind::All:
     FPKeepKindStr = "-mframe-pointer=all";
     break;
   }
@@ -8635,10 +8634,10 @@ void Clang::ConstructJob(Compilation &C, const 
JobAction &JA,
   }
 
   if (Arg *A = Args.getLastArg(options::OPT_pg))
-    if (FPKeepKind == CodeGenOptions::FramePointerKind::None &&
+    if (FPKeepKind == llvm::FramePointerKind::None &&
         !Args.hasArg(options::OPT_mfentry))
-      D.Diag(diag::err_drv_argument_not_allowed_with) << "-fomit-frame-pointer"
-                                                      << A->getAsString(Args);
+      D.Diag(diag::err_drv_argument_not_allowed_with)
+          << "-fomit-frame-pointer" << A->getAsString(Args);
 
   // Claim some arguments which clang supports automatically.
 
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp 
b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 74e27bf8b9cde..c8f2906b26a23 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -24,7 +24,6 @@
 #include "MSP430.h"
 #include "Solaris.h"
 #include "ToolChains/Cuda.h"
-#include "clang/Basic/CodeGenOptions.h"
 #include "clang/Config/config.h"
 #include "clang/Driver/Action.h"
 #include "clang/Driver/Compilation.h"
@@ -45,6 +44,7 @@
 #include "llvm/ADT/Twine.h"
 #include "llvm/BinaryFormat/Magic.h"
 #include "llvm/Config/llvm-config.h"
+#include "llvm/Frontend/Driver/CodeGenOptions.h"
 #include "llvm/Option/Arg.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/Option.h"
@@ -84,229 +84,41 @@ OffloadJobsOpt tools::parseOffloadJobs(const ArgList 
&Args) {
   return {OffloadJobsOpt::Kind::Fixed, A, Val, unsigned(NumThreads)};
 }
 
-static bool useFramePointerForTargetByDefault(const llvm::opt::ArgList &Args,
-                                              const llvm::Triple &Triple) {
-  if (Args.hasArg(options::OPT_pg) && !Args.hasArg(options::OPT_mfentry))
-    return true;
-
-  if (Triple.isAndroid())
-    return true;
-
-  switch (Triple.getArch()) {
-  case llvm::Triple::xcore:
-  case llvm::Triple::wasm32:
-  case llvm::Triple::wasm64:
-  case llvm::Triple::msp430:
-    // XCore never wants frame pointers, regardless of OS.
-    // WebAssembly never wants frame pointers.
-    return false;
-  case llvm::Triple::ppc:
-  case llvm::Triple::ppcle:
-  case llvm::Triple::ppc64:
-  case llvm::Triple::ppc64le:
-  case llvm::Triple::riscv32:
-  case llvm::Triple::riscv64:
-  case llvm::Triple::riscv32be:
-  case llvm::Triple::riscv64be:
-  case llvm::Triple::sparc:
-  case llvm::Triple::sparcel:
-  case llvm::Triple::sparcv9:
-  case llvm::Triple::amdgpu:
-  case llvm::Triple::r600:
-  case llvm::Triple::csky:
-  case llvm::Triple::loongarch32:
-  case llvm::Triple::loongarch64:
-  case llvm::Triple::m68k:
-  case llvm::Triple::mips64:
-  case llvm::Triple::mips64el:
-  case llvm::Triple::mips:
-  case llvm::Triple::mipsel:
-    return !clang::driver::tools::areOptimizationsEnabled(Args);
-  default:
-    break;
-  }
-
-  if (Triple.isOSFuchsia() || Triple.isOSNetBSD()) {
-    return !clang::driver::tools::areOptimizationsEnabled(Args);
-  }
-
-  if (Triple.isOSLinux() || Triple.isOSHurd()) {
-    switch (Triple.getArch()) {
-    // Don't use a frame pointer on linux if optimizing for certain targets.
-    case llvm::Triple::arm:
-    case llvm::Triple::armeb:
-    case llvm::Triple::thumb:
-    case llvm::Triple::thumbeb:
-    case llvm::Triple::systemz:
-    case llvm::Triple::x86:
-    case llvm::Triple::x86_64:
-      return !clang::driver::tools::areOptimizationsEnabled(Args);
-    default:
-      return true;
-    }
-  }
-
-  if (Triple.isOSWindows()) {
-    switch (Triple.getArch()) {
-    case llvm::Triple::x86:
-      return !clang::driver::tools::areOptimizationsEnabled(Args);
-    case llvm::Triple::x86_64:
-      return Triple.isOSBinFormatMachO();
-    case llvm::Triple::arm:
-    case llvm::Triple::thumb:
-      // Windows on ARM builds with FPO disabled to aid fast stack walking
-      return true;
-    default:
-      // All other supported Windows ISAs use xdata unwind information, so 
frame
-      // pointers are not generally useful.
-      return false;
-    }
-  }
-
-  if (arm::isARMEABIBareMetal(Triple))
-    return false;
-
-  return true;
-}
-
-static bool useLeafFramePointerForTargetByDefault(const llvm::Triple &Triple) {
-  if (Triple.isAArch64() || Triple.isPS() || Triple.isVE() ||
-      (Triple.isAndroid() && !Triple.isARM()))
-    return false;
-
-  if ((Triple.isARM() || Triple.isThumb()) && Triple.isOSBinFormatMachO())
-    return false;
-
-  return true;
-}
-
-static bool mustUseNonLeafFramePointerForTarget(const llvm::Triple &Triple) {
-  switch (Triple.getArch()) {
-  default:
-    return false;
-  case llvm::Triple::arm:
-  case llvm::Triple::thumb:
-    // ARM Darwin targets require a frame pointer to be always present to aid
-    // offline debugging via backtraces.
-    return Triple.isOSDarwin();
-  }
-}
-
-// True if a target-specific option requires the frame chain to be preserved,
-// even if new frame records are not created.
-static bool mustMaintainValidFrameChain(const llvm::opt::ArgList &Args,
-                                        const llvm::Triple &Triple) {
-  switch (Triple.getArch()) {
-  default:
-    return false;
-  case llvm::Triple::arm:
-  case llvm::Triple::armeb:
-  case llvm::Triple::thumb:
-  case llvm::Triple::thumbeb:
-    // For 32-bit Arm, the -mframe-chain=aapcs and -mframe-chain=aapcs+leaf
-    // options require the frame pointer register to be reserved (or point to a
-    // new AAPCS-compilant frame record), even with    -fno-omit-frame-pointer.
-    if (Arg *A = Args.getLastArg(options::OPT_mframe_chain)) {
-      StringRef V = A->getValue();
-      return V != "none";
-    }
-    return false;
-
-  case llvm::Triple::aarch64:
-    // Arm64 Windows requires that the frame chain is valid, as there is no
-    // way to indicate during a stack walk that a frame has used the frame
-    // pointer as a general purpose register.
-    return Triple.isOSWindows();
-  }
-}
-
-// True if a target-specific option causes -fno-omit-frame-pointer to also
-// cause frame records to be created in leaf functions.
-static bool framePointerImpliesLeafFramePointer(const llvm::opt::ArgList &Args,
-                                                const llvm::Triple &Triple) {
-  if (Triple.isARM() || Triple.isThumb()) {
-    // For 32-bit Arm, the -mframe-chain=aapcs+leaf option causes the
-    // -fno-omit-frame-pointer optiion to imply -mno-omit-leaf-frame-pointer,
-    // but does not by itself imply either option.
-    if (Arg *A = Args.getLastArg(options::OPT_mframe_chain)) {
-      StringRef V = A->getValue();
-      return V == "aapcs+leaf";
-    }
-    return false;
-  }
-  return false;
-}
-
-clang::CodeGenOptions::FramePointerKind
-getFramePointerKind(const llvm::opt::ArgList &Args,
-                    const llvm::Triple &Triple) {
-  // 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 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.
-  //
-  // | 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
-  // -fno-omit-frame-pointer) specify that the frame chain must be valid,
-  // without requiring new frame records to be created.
-
-  bool DefaultFP = useFramePointerForTargetByDefault(Args, Triple);
-  bool EnableFP = mustUseNonLeafFramePointerForTarget(Triple) ||
-                  Args.hasFlag(options::OPT_fno_omit_frame_pointer,
-                               options::OPT_fomit_frame_pointer, DefaultFP);
-
-  bool DefaultLeafFP =
-      useLeafFramePointerForTargetByDefault(Triple) ||
-      (EnableFP && framePointerImpliesLeafFramePointer(Args, Triple));
-  bool EnableLeafFP =
-      Args.hasFlag(options::OPT_mno_omit_leaf_frame_pointer,
-                   options::OPT_momit_leaf_frame_pointer, DefaultLeafFP);
-
-  bool FPRegReserved = Args.hasFlag(options::OPT_mreserve_frame_pointer_reg,
-                                    options::OPT_mno_reserve_frame_pointer_reg,
-                                    mustMaintainValidFrameChain(Args, Triple));
-
-  if (EnableFP) {
-    if (EnableLeafFP)
-      return clang::CodeGenOptions::FramePointerKind::All;
-
-    if (FPRegReserved)
-      return clang::CodeGenOptions::FramePointerKind::NonLeaf;
-
-    return clang::CodeGenOptions::FramePointerKind::NonLeafNoReserve;
-  }
-  if (FPRegReserved)
-    return clang::CodeGenOptions::FramePointerKind::Reserved;
-  return clang::CodeGenOptions::FramePointerKind::None;
+llvm::FramePointerKind getFramePointerKind(const llvm::opt::ArgList &Args,
+                                           const llvm::Triple &Triple) {
+  llvm::driver::FramePointerOptions Opts;
+  Opts.Optimized = tools::areOptimizationsEnabled(Args);
+  Opts.InstrumentationRequiresFramePointer =
+      Args.hasArg(options::OPT_pg) && !Args.hasArg(options::OPT_mfentry);
+
+  if (Arg *A = Args.getLastArg(options::OPT_fno_omit_frame_pointer,
+                               options::OPT_fomit_frame_pointer))
+    Opts.EnableFramePointer =
+        A->getOption().matches(options::OPT_fno_omit_frame_pointer);
+  if (Arg *A = Args.getLastArg(options::OPT_mno_omit_leaf_frame_pointer,
+                               options::OPT_momit_leaf_frame_pointer))
+    Opts.EnableLeafFramePointer =
+        A->getOption().matches(options::OPT_mno_omit_leaf_frame_pointer);
+  if (Arg *A = Args.getLastArg(options::OPT_mreserve_frame_pointer_reg,
+                               options::OPT_mno_reserve_frame_pointer_reg))
+    Opts.ReserveFramePointerRegister =
+        A->getOption().matches(options::OPT_mreserve_frame_pointer_reg);
+
+  if (Arg *A = Args.getLastArg(options::OPT_mframe_chain)) {
+    Opts.FrameChain =
+        llvm::StringSwitch<llvm::driver::FramePointerOptions::FrameChainKind>(
+            A->getValue())
+            .Case("aapcs",
+                  llvm::driver::FramePointerOptions::FrameChainKind::AAPCS)
+            .Case("aapcs+leaf",
+                  llvm::driver::FramePointerOptions::FrameChainKind::AAPCSLeaf)
+            .Case("none",
+                  llvm::driver::FramePointerOptions::FrameChainKind::None)
+            .Default(
+                llvm::driver::FramePointerOptions::FrameChainKind::Invalid);
+  }
+
+  return llvm::driver::getFramePointerKind(Triple, Opts);
 }
 
 static void renderRpassOptions(const ArgList &Args, ArgStringList &CmdArgs,
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp 
b/clang/lib/Driver/ToolChains/Flang.cpp
index 5824f59400323..38bc33410b1d6 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -10,7 +10,6 @@
 #include "Arch/RISCV.h"
 #include "Cuda.h"
 
-#include "clang/Basic/CodeGenOptions.h"
 #include "clang/Basic/MakeSupport.h"
 #include "clang/Driver/CommonArgs.h"
 #include "clang/Options/OptionUtils.h"
@@ -1452,24 +1451,23 @@ void Flang::ConstructJob(Compilation &C, const 
JobAction &JA,
   // Forward -Xflang arguments to -fc1
   Args.AddAllArgValues(CmdArgs, options::OPT_Xflang);
 
-  CodeGenOptions::FramePointerKind FPKeepKind =
-      getFramePointerKind(Args, Triple);
+  llvm::FramePointerKind FPKeepKind = getFramePointerKind(Args, Triple);
 
   const char *FPKeepKindStr = nullptr;
   switch (FPKeepKind) {
-  case CodeGenOptions::FramePointerKind::None:
+  case llvm::FramePointerKind::None:
     FPKeepKindStr = "-mframe-pointer=none";
     break;
-  case CodeGenOptions::FramePointerKind::Reserved:
+  case llvm::FramePointerKind::Reserved:
     FPKeepKindStr = "-mframe-pointer=reserved";
     break;
-  case CodeGenOptions::FramePointerKind::NonLeafNoReserve:
+  case llvm::FramePointerKind::NonLeafNoReserve:
     FPKeepKindStr = "-mframe-pointer=non-leaf-no-reserve";
     break;
-  case CodeGenOptions::FramePointerKind::NonLeaf:
+  case llvm::FramePointerKind::NonLeaf:
     FPKeepKindStr = "-mframe-pointer=non-leaf";
     break;
-  case CodeGenOptions::FramePointerKind::All:
+  case llvm::FramePointerKind::All:
     FPKeepKindStr = "-mframe-pointer=all";
     break;
   }
diff --git a/llvm/include/llvm/Frontend/Driver/CodeGenOptions.h 
b/llvm/include/llvm/Frontend/Driver/CodeGenOptions.h
index 77ab477986d31..9586beb1157d7 100644
--- a/llvm/include/llvm/Frontend/Driver/CodeGenOptions.h
+++ b/llvm/include/llvm/Frontend/Driver/CodeGenOptions.h
@@ -13,7 +13,9 @@
 #ifndef LLVM_FRONTEND_DRIVER_CODEGENOPTIONS_H
 #define LLVM_FRONTEND_DRIVER_CODEGENOPTIONS_H
 
+#include "llvm/Support/CodeGen.h"
 #include "llvm/Support/Compiler.h"
+#include <optional>
 #include <string>
 
 namespace llvm {
@@ -23,6 +25,30 @@ enum class VectorLibrary;
 } // namespace llvm
 
 namespace llvm::driver {
+/// Driver options which affect the target's frame pointer policy. Frontends
+/// are responsible for translating their option table into this structure.
+struct FramePointerOptions {
+  /// Whether an optimization level other than -O0 is enabled.
+  bool Optimized = false;
+
+  /// Whether instrumentation such as -pg requires a frame pointer.
+  bool InstrumentationRequiresFramePointer = false;
+
+  /// Explicit overrides for non-leaf frame records, leaf frame records, and
+  /// reserving the frame pointer register, respectively.
+  std::optional<bool> EnableFramePointer;
+  std::optional<bool> EnableLeafFramePointer;
+  std::optional<bool> ReserveFramePointerRegister;
+
+  enum class FrameChainKind { None, AAPCS, AAPCSLeaf, Invalid };
+  FrameChainKind FrameChain = FrameChainKind::None;
+};
+
+/// Determine the frame pointer policy for \p TargetTriple.
+LLVM_ABI llvm::FramePointerKind
+getFramePointerKind(const llvm::Triple &TargetTriple,
+                    const FramePointerOptions &Opts);
+
 // The current supported vector libraries in enum \VectorLibrary are 
9(including
 // the NoLibrary). Changing the bitcount from 3 to 4 so that more than 8 values
 // can be supported. Now the maximum number of vector libraries supported
diff --git a/llvm/lib/Frontend/Driver/CodeGenOptions.cpp 
b/llvm/lib/Frontend/Driver/CodeGenOptions.cpp
index d22202598a28d..b691f4b5f2ea8 100644
--- a/llvm/lib/Frontend/Driver/CodeGenOptions.cpp
+++ b/llvm/lib/Frontend/Driver/CodeGenOptions.cpp
@@ -19,6 +19,238 @@ extern 
llvm::cl::opt<llvm::InstrProfCorrelator::ProfCorrelatorKind>
 
 namespace llvm::driver {
 
+/// Is the triple {arm,armeb,thumb,thumbeb}-none-none-{eabi,eabihf} ?
+static bool isARMEABIBareMetal(const llvm::Triple &Triple) {
+  auto Arch = Triple.getArch();
+  if (Arch != llvm::Triple::arm && Arch != llvm::Triple::thumb &&
+      Arch != llvm::Triple::armeb && Arch != llvm::Triple::thumbeb)
+    return false;
+
+  if (Triple.getVendor() != llvm::Triple::UnknownVendor)
+    return false;
+
+  if (Triple.getOS() != llvm::Triple::UnknownOS)
+    return false;
+
+  if (Triple.getEnvironment() != llvm::Triple::EABI &&
+      Triple.getEnvironment() != llvm::Triple::EABIHF)
+    return false;
+
+  return true;
+}
+
+static bool useFramePointerForTargetByDefault(const llvm::Triple &Triple,
+                                              const FramePointerOptions &Opts) 
{
+  if (Opts.InstrumentationRequiresFramePointer)
+    return true;
+
+  if (Triple.isAndroid())
+    return true;
+
+  switch (Triple.getArch()) {
+  case llvm::Triple::xcore:
+  case llvm::Triple::wasm32:
+  case llvm::Triple::wasm64:
+  case llvm::Triple::msp430:
+    // XCore never wants frame pointers, regardless of OS.
+    // WebAssembly never wants frame pointers.
+    return false;
+  case llvm::Triple::ppc:
+  case llvm::Triple::ppcle:
+  case llvm::Triple::ppc64:
+  case llvm::Triple::ppc64le:
+  case llvm::Triple::riscv32:
+  case llvm::Triple::riscv64:
+  case llvm::Triple::riscv32be:
+  case llvm::Triple::riscv64be:
+  case llvm::Triple::sparc:
+  case llvm::Triple::sparcel:
+  case llvm::Triple::sparcv9:
+  case llvm::Triple::amdgpu:
+  case llvm::Triple::r600:
+  case llvm::Triple::csky:
+  case llvm::Triple::loongarch32:
+  case llvm::Triple::loongarch64:
+  case llvm::Triple::m68k:
+  case llvm::Triple::mips64:
+  case llvm::Triple::mips64el:
+  case llvm::Triple::mips:
+  case llvm::Triple::mipsel:
+    return !Opts.Optimized;
+  default:
+    break;
+  }
+
+  if (Triple.isOSFuchsia() || Triple.isOSNetBSD()) {
+    return !Opts.Optimized;
+  }
+
+  if (Triple.isOSLinux() || Triple.isOSHurd()) {
+    switch (Triple.getArch()) {
+    // Don't use a frame pointer on linux if optimizing for certain targets.
+    case llvm::Triple::arm:
+    case llvm::Triple::armeb:
+    case llvm::Triple::thumb:
+    case llvm::Triple::thumbeb:
+    case llvm::Triple::systemz:
+    case llvm::Triple::x86:
+    case llvm::Triple::x86_64:
+      return !Opts.Optimized;
+    default:
+      return true;
+    }
+  }
+
+  if (Triple.isOSWindows()) {
+    switch (Triple.getArch()) {
+    case llvm::Triple::x86:
+      return !Opts.Optimized;
+    case llvm::Triple::x86_64:
+      return Triple.isOSBinFormatMachO();
+    case llvm::Triple::arm:
+    case llvm::Triple::thumb:
+      // Windows on ARM builds with FPO disabled to aid fast stack walking
+      return true;
+    default:
+      // All other supported Windows ISAs use xdata unwind information, so 
frame
+      // pointers are not generally useful.
+      return false;
+    }
+  }
+
+  if (isARMEABIBareMetal(Triple))
+    return false;
+
+  return true;
+}
+
+static bool useLeafFramePointerForTargetByDefault(const llvm::Triple &Triple) {
+  if (Triple.isAArch64() || Triple.isPS() || Triple.isVE() ||
+      (Triple.isAndroid() && !Triple.isARM()))
+    return false;
+
+  if ((Triple.isARM() || Triple.isThumb()) && Triple.isOSBinFormatMachO())
+    return false;
+
+  return true;
+}
+
+static bool mustUseNonLeafFramePointerForTarget(const llvm::Triple &Triple) {
+  switch (Triple.getArch()) {
+  default:
+    return false;
+  case llvm::Triple::arm:
+  case llvm::Triple::thumb:
+    // ARM Darwin targets require a frame pointer to be always present to aid
+    // offline debugging via backtraces.
+    return Triple.isOSDarwin();
+  }
+}
+
+// True if a target-specific option requires the frame chain to be preserved,
+// even if new frame records are not created.
+static bool mustMaintainValidFrameChain(const FramePointerOptions &Opts,
+                                        const llvm::Triple &Triple) {
+  switch (Triple.getArch()) {
+  default:
+    return false;
+  case llvm::Triple::arm:
+  case llvm::Triple::armeb:
+  case llvm::Triple::thumb:
+  case llvm::Triple::thumbeb:
+    // For 32-bit Arm, the -mframe-chain=aapcs and -mframe-chain=aapcs+leaf
+    // options require the frame pointer register to be reserved (or point to a
+    // new AAPCS-compilant frame record), even with -fno-omit-frame-pointer.
+    return Opts.FrameChain != FramePointerOptions::FrameChainKind::None;
+
+  case llvm::Triple::aarch64:
+    // Arm64 Windows requires that the frame chain is valid, as there is no
+    // way to indicate during a stack walk that a frame has used the frame
+    // pointer as a general purpose register.
+    return Triple.isOSWindows();
+  }
+}
+
+// True if a target-specific option causes -fno-omit-frame-pointer to also
+// cause frame records to be created in leaf functions.
+static bool framePointerImpliesLeafFramePointer(const FramePointerOptions 
&Opts,
+                                                const llvm::Triple &Triple) {
+  if (Triple.isARM() || Triple.isThumb()) {
+    // For 32-bit Arm, the -mframe-chain=aapcs+leaf option causes the
+    // -fno-omit-frame-pointer optiion to imply -mno-omit-leaf-frame-pointer,
+    // but does not by itself imply either option.
+    return Opts.FrameChain == FramePointerOptions::FrameChainKind::AAPCSLeaf;
+  }
+  return false;
+}
+
+llvm::FramePointerKind getFramePointerKind(const llvm::Triple &Triple,
+                                           const FramePointerOptions &Opts) {
+  // 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 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.
+  //
+  // | 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
+  // -fno-omit-frame-pointer) specify that the frame chain must be valid,
+  // without requiring new frame records to be created.
+
+  bool DefaultFP = useFramePointerForTargetByDefault(Triple, Opts);
+  bool EnableFP = mustUseNonLeafFramePointerForTarget(Triple) ||
+                  Opts.EnableFramePointer.value_or(DefaultFP);
+
+  bool DefaultLeafFP =
+      useLeafFramePointerForTargetByDefault(Triple) ||
+      (EnableFP && framePointerImpliesLeafFramePointer(Opts, Triple));
+  bool EnableLeafFP = Opts.EnableLeafFramePointer.value_or(DefaultLeafFP);
+
+  bool FPRegReserved = Opts.ReserveFramePointerRegister.value_or(
+      mustMaintainValidFrameChain(Opts, Triple));
+
+  if (EnableFP) {
+    if (EnableLeafFP)
+      return llvm::FramePointerKind::All;
+
+    if (FPRegReserved)
+      return llvm::FramePointerKind::NonLeaf;
+
+    return llvm::FramePointerKind::NonLeafNoReserve;
+  }
+  if (FPRegReserved)
+    return llvm::FramePointerKind::Reserved;
+  return llvm::FramePointerKind::None;
+}
+
 llvm::VectorLibrary
 convertDriverVectorLibraryToVectorLibrary(llvm::driver::VectorLibrary VecLib) {
   switch (VecLib) {
diff --git a/llvm/unittests/Frontend/CMakeLists.txt 
b/llvm/unittests/Frontend/CMakeLists.txt
index 8976dd1b2f737..901c2a855524a 100644
--- a/llvm/unittests/Frontend/CMakeLists.txt
+++ b/llvm/unittests/Frontend/CMakeLists.txt
@@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS
   BinaryFormat
   Core
   FrontendHLSL
+  FrontendDriver
   FrontendOffloading
   FrontendOpenACC
   FrontendOpenMP
@@ -14,6 +15,7 @@ set(LLVM_LINK_COMPONENTS
 
 add_llvm_unittest(LLVMFrontendTests
   EnumSetTest.cpp
+  FramePointerTest.cpp
   HLSLBindingTest.cpp
   HLSLRootSignatureDumpTest.cpp
   HLSLSemanticSignatureMetadataTest.cpp
diff --git a/llvm/unittests/Frontend/FramePointerTest.cpp 
b/llvm/unittests/Frontend/FramePointerTest.cpp
new file mode 100644
index 0000000000000..5b6e1750bee20
--- /dev/null
+++ b/llvm/unittests/Frontend/FramePointerTest.cpp
@@ -0,0 +1,94 @@
+//===- llvm/unittests/Frontend/FramePointerTest.cpp - FP tests -----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Frontend/Driver/CodeGenOptions.h"
+#include "llvm/TargetParser/Triple.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::driver;
+
+namespace {
+
+TEST(FramePointerTest, TargetDefaults) {
+  struct TestCase {
+    const char *Triple;
+    bool Optimized;
+    FramePointerKind Expected;
+  };
+  const TestCase Cases[] = {
+      {"i386-unknown-linux", false, FramePointerKind::All},
+      {"i386-unknown-linux", true, FramePointerKind::None},
+      {"thumb-arm-none-eabi", false, FramePointerKind::None},
+      {"thumbv6m-apple-none-macho", false, FramePointerKind::NonLeafNoReserve},
+      {"riscv64-unknown-linux-android", true,
+       FramePointerKind::NonLeafNoReserve},
+  };
+
+  for (const TestCase &Case : Cases) {
+    FramePointerOptions Opts;
+    Opts.Optimized = Case.Optimized;
+    EXPECT_EQ(Case.Expected, getFramePointerKind(Triple(Case.Triple), Opts))
+        << Case.Triple;
+  }
+}
+
+TEST(FramePointerTest, ExplicitOptions) {
+  FramePointerOptions Opts;
+  Opts.Optimized = true;
+  Opts.EnableFramePointer = true;
+  EXPECT_EQ(FramePointerKind::All,
+            getFramePointerKind(Triple("i386-unknown-linux"), Opts));
+
+  Opts.EnableLeafFramePointer = false;
+  EXPECT_EQ(FramePointerKind::NonLeafNoReserve,
+            getFramePointerKind(Triple("i386-unknown-linux"), Opts));
+
+  Opts.ReserveFramePointerRegister = true;
+  EXPECT_EQ(FramePointerKind::NonLeaf,
+            getFramePointerKind(Triple("i386-unknown-linux"), Opts));
+
+  Opts.EnableFramePointer = false;
+  EXPECT_EQ(FramePointerKind::Reserved,
+            getFramePointerKind(Triple("i386-unknown-linux"), Opts));
+}
+
+TEST(FramePointerTest, InstrumentationRequiresFramePointer) {
+  FramePointerOptions Opts;
+  Opts.Optimized = true;
+  Opts.InstrumentationRequiresFramePointer = true;
+  EXPECT_EQ(FramePointerKind::All,
+            getFramePointerKind(Triple("i386-unknown-linux"), Opts));
+}
+
+TEST(FramePointerTest, FrameChain) {
+  FramePointerOptions Opts;
+  Opts.FrameChain = FramePointerOptions::FrameChainKind::AAPCS;
+  EXPECT_EQ(FramePointerKind::Reserved,
+            getFramePointerKind(Triple("arm-arm-none-eabi"), Opts));
+
+  // Preserve the previous driver behavior for an invalid -mframe-chain=
+  // value: every value other than "none" reserves the frame pointer register.
+  Opts.FrameChain = FramePointerOptions::FrameChainKind::Invalid;
+  EXPECT_EQ(FramePointerKind::Reserved,
+            getFramePointerKind(Triple("arm-arm-none-eabi"), Opts));
+
+  Opts.FrameChain = FramePointerOptions::FrameChainKind::AAPCSLeaf;
+  Opts.EnableFramePointer = true;
+  EXPECT_EQ(FramePointerKind::All,
+            getFramePointerKind(Triple("arm-arm-none-eabi"), Opts));
+}
+
+TEST(FramePointerTest, AArch64WindowsMaintainsFrameChain) {
+  FramePointerOptions Opts;
+  Opts.EnableFramePointer = false;
+  EXPECT_EQ(FramePointerKind::Reserved,
+            getFramePointerKind(Triple("aarch64-pc-windows-msvc"), Opts));
+}
+
+} // namespace

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to