https://github.com/kiranchandramohan updated https://github.com/llvm/llvm-project/pull/218870
>From 5fe5f27c71b244dbaf0a76b263503b423a7ebe78 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 | 248 ++---------------- 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 | 88 +++++++ 9 files changed, 392 insertions(+), 243 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..7f2d1eabe5e94 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, 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/lib/Frontend/Driver/CodeGenOptions.cpp b/llvm/lib/Frontend/Driver/CodeGenOptions.cpp index d22202598a28d..f181f4cd0f7f1 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.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/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
