https://github.com/kiranchandramohan updated https://github.com/llvm/llvm-project/pull/218870
>From 2610743a6a6889bda5d61a5f3cd7dcb6b299d433 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/Arch/ARM.cpp | 20 -- clang/lib/Driver/ToolChains/Arch/ARM.h | 2 - clang/lib/Driver/ToolChains/BareMetal.cpp | 4 +- clang/lib/Driver/ToolChains/Clang.cpp | 19 +- clang/lib/Driver/ToolChains/CommonArgs.cpp | 250 ++---------------- clang/lib/Driver/ToolChains/Flang.cpp | 14 +- .../llvm/Frontend/Driver/CodeGenOptions.h | 26 ++ .../llvm/TargetParser/ARMTargetParser.h | 3 + llvm/lib/Frontend/Driver/CodeGenOptions.cpp | 214 +++++++++++++++ llvm/lib/TargetParser/ARMTargetParser.cpp | 19 ++ llvm/unittests/Frontend/CMakeLists.txt | 2 + llvm/unittests/Frontend/FramePointerTest.cpp | 88 ++++++ 14 files changed, 399 insertions(+), 268 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/Arch/ARM.cpp b/clang/lib/Driver/ToolChains/Arch/ARM.cpp index 7d9c1f0bd3d40..64d342d1bffd2 100644 --- a/clang/lib/Driver/ToolChains/Arch/ARM.cpp +++ b/clang/lib/Driver/ToolChains/Arch/ARM.cpp @@ -51,26 +51,6 @@ bool arm::isARMAProfile(const llvm::Triple &Triple) { return llvm::ARM::parseArchProfile(Arch) == llvm::ARM::ProfileKind::A; } -/// Is the triple {arm,armeb,thumb,thumbeb}-none-none-{eabi,eabihf} ? -bool arm::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; -} - // Get Arch/CPU from args. void arm::getARMArchCPUFromArgs(const ArgList &Args, llvm::StringRef &Arch, llvm::StringRef &CPU, bool FromAs) { diff --git a/clang/lib/Driver/ToolChains/Arch/ARM.h b/clang/lib/Driver/ToolChains/Arch/ARM.h index a23a8793a89e2..13296be597275 100644 --- a/clang/lib/Driver/ToolChains/Arch/ARM.h +++ b/clang/lib/Driver/ToolChains/Arch/ARM.h @@ -75,8 +75,6 @@ int getARMSubArchVersionNumber(const llvm::Triple &Triple); bool isARMMProfile(const llvm::Triple &Triple); bool isARMAProfile(const llvm::Triple &Triple); bool isARMBigEndian(const llvm::Triple &Triple, const llvm::opt::ArgList &Args); -bool isARMEABIBareMetal(const llvm::Triple &Triple); - } // end namespace arm } // end namespace tools } // end namespace driver diff --git a/clang/lib/Driver/ToolChains/BareMetal.cpp b/clang/lib/Driver/ToolChains/BareMetal.cpp index ba454acbf755c..91de19845469b 100644 --- a/clang/lib/Driver/ToolChains/BareMetal.cpp +++ b/clang/lib/Driver/ToolChains/BareMetal.cpp @@ -279,7 +279,7 @@ void BareMetal::findMultilibs(const Driver &D, const llvm::Triple &Triple, } bool BareMetal::handlesTarget(const llvm::Triple &Triple) { - return arm::isARMEABIBareMetal(Triple) || + return llvm::ARM::isARMEABIBareMetal(Triple) || aarch64::isAArch64BareMetal(Triple) || isRISCVBareMetal(Triple) || isPPCBareMetal(Triple) || isX86BareMetal(Triple); } @@ -634,7 +634,7 @@ void baremetal::Linker::ConstructJob(Compilation &C, const JobAction &JA, // The R_ARM_TARGET2 relocation must be treated as R_ARM_REL32 on arm*-*-elf // and arm*-*-eabi (the default is R_ARM_GOT_PREL, used on arm*-*-linux and // arm*-*-*bsd). - if (arm::isARMEABIBareMetal(TC.getTriple())) + if (llvm::ARM::isARMEABIBareMetal(TC.getTriple())) CmdArgs.push_back("--target2=rel"); CmdArgs.push_back("-o"); 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..fc4fa692f7a4d 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,33 @@ 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; +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)) { + StringRef V = A->getValue(); + Opts.MaintainValidFrameChain = V != "none"; + Opts.FramePointerImpliesLeaf = V == "aapcs+leaf"; } - 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; + return llvm::driver::getFramePointerKind(Triple, Opts); } static void renderRpassOptions(const ArgList &Args, ArgStringList &CmdArgs, @@ -602,7 +406,7 @@ const char *tools::getLDMOption(const llvm::Triple &T, const ArgList &Args) { case llvm::Triple::armeb: case llvm::Triple::thumbeb: { bool IsBigEndian = tools::arm::isARMBigEndian(T, Args); - if (arm::isARMEABIBareMetal(T)) + if (llvm::ARM::isARMEABIBareMetal(T)) return IsBigEndian ? "armelfb" : "armelf"; return IsBigEndian ? "armelfb_linux_eabi" : "armelf_linux_eabi"; } 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..f61a36f5914bf 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; + + bool MaintainValidFrameChain = false; + bool FramePointerImpliesLeaf = false; +}; + +/// 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/include/llvm/TargetParser/ARMTargetParser.h b/llvm/include/llvm/TargetParser/ARMTargetParser.h index 919598c894cd5..defe9e569472d 100644 --- a/llvm/include/llvm/TargetParser/ARMTargetParser.h +++ b/llvm/include/llvm/TargetParser/ARMTargetParser.h @@ -274,6 +274,9 @@ LLVM_ABI LLVM_READONLY StringRef computeDefaultTargetABI(const Triple &TT); LLVM_ABI LLVM_READONLY ARMABI computeTargetABI(const Triple &TT, StringRef ABIName = ""); +/// Is the triple {arm,armeb,thumb,thumbeb}-none-none-{eabi,eabihf}? +LLVM_ABI bool isARMEABIBareMetal(const Triple &TT); + /// Get the (LLVM) name of the minimum ARM CPU for the arch we are targeting. /// /// \param Arch the architecture name (e.g., "armv7s"). If it is an empty diff --git a/llvm/lib/Frontend/Driver/CodeGenOptions.cpp b/llvm/lib/Frontend/Driver/CodeGenOptions.cpp index d22202598a28d..d616f980ed4e8 100644 --- a/llvm/lib/Frontend/Driver/CodeGenOptions.cpp +++ b/llvm/lib/Frontend/Driver/CodeGenOptions.cpp @@ -10,6 +10,7 @@ #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/IR/SystemLibraries.h" #include "llvm/ProfileData/InstrProfCorrelator.h" +#include "llvm/TargetParser/ARMTargetParser.h" #include "llvm/TargetParser/Triple.h" namespace llvm { @@ -19,6 +20,219 @@ extern llvm::cl::opt<llvm::InstrProfCorrelator::ProfCorrelatorKind> namespace llvm::driver { +/// Is the triple {arm,armeb,thumb,thumbeb}-none-none-{eabi,eabihf} ? +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 (llvm::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 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.MaintainValidFrameChain; + + 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.FramePointerImpliesLeaf; + } + 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/lib/TargetParser/ARMTargetParser.cpp b/llvm/lib/TargetParser/ARMTargetParser.cpp index 709e5f05e3bb9..59421135ef03b 100644 --- a/llvm/lib/TargetParser/ARMTargetParser.cpp +++ b/llvm/lib/TargetParser/ARMTargetParser.cpp @@ -21,6 +21,25 @@ using namespace llvm; +bool ARM::isARMEABIBareMetal(const Triple &TT) { + auto Arch = TT.getArch(); + if (Arch != Triple::arm && Arch != Triple::thumb && Arch != Triple::armeb && + Arch != Triple::thumbeb) + return false; + + if (TT.getVendor() != Triple::UnknownVendor) + return false; + + if (TT.getOS() != Triple::UnknownOS) + return false; + + if (TT.getEnvironment() != Triple::EABI && + TT.getEnvironment() != Triple::EABIHF) + return false; + + return true; +} + static StringRef getHWDivSynonym(StringRef HWDiv) { return StringSwitch<StringRef>(HWDiv) .Case("thumb,arm", "arm,thumb") 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..25b2c1d2def33 --- /dev/null +++ b/llvm/unittests/Frontend/FramePointerTest.cpp @@ -0,0 +1,88 @@ +//===- 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.MaintainValidFrameChain = true; + EXPECT_EQ(FramePointerKind::Reserved, + getFramePointerKind(Triple("arm-arm-none-eabi"), Opts)); + + Opts.FramePointerImpliesLeaf = true; + 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
