Skip to content

Commit

Permalink
[Flang][Clang] Add support for frame pointers in Flang
Browse files Browse the repository at this point in the history
  • Loading branch information
Radu2k authored and tblah committed Dec 1, 2023
1 parent 7832a85 commit ea4eb69
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 138 deletions.
13 changes: 8 additions & 5 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -3172,7 +3172,8 @@ def fno_ms_compatibility : Flag<["-"], "fno-ms-compatibility">, Group<f_Group>,
def fno_objc_legacy_dispatch : Flag<["-"], "fno-objc-legacy-dispatch">, Group<f_Group>;
def fno_objc_weak : Flag<["-"], "fno-objc-weak">, Group<f_Group>,
Visibility<[ClangOption, CC1Option]>;
def fno_omit_frame_pointer : Flag<["-"], "fno-omit-frame-pointer">, Group<f_Group>;
def fno_omit_frame_pointer : Flag<["-"], "fno-omit-frame-pointer">, Group<f_Group>,
Visibility<[ClangOption, FlangOption]>;
defm operator_names : BoolFOption<"operator-names",
LangOpts<"CXXOperatorNames">, Default<cplusplus.KeyPath>,
NegFlag<SetFalse, [], [ClangOption, CC1Option],
Expand Down Expand Up @@ -3298,6 +3299,7 @@ defm objc_avoid_heapify_local_blocks : BoolFOption<"objc-avoid-heapify-local-blo
BothFlags<[], [CC1Option], " to avoid heapifying local blocks">>;

def fomit_frame_pointer : Flag<["-"], "fomit-frame-pointer">, Group<f_Group>,
Visibility<[ClangOption, FlangOption]>,
HelpText<"Omit the frame pointer from functions that don't need it. "
"Some stack unwinding cases, such as profilers and sanitizers, may prefer specifying -fno-omit-frame-pointer. "
"On many targets, -O1 and higher omit the frame pointer by default. "
Expand Down Expand Up @@ -6786,10 +6788,6 @@ def new_struct_path_tbaa : Flag<["-"], "new-struct-path-tbaa">,
def mdebug_pass : Separate<["-"], "mdebug-pass">,
HelpText<"Enable additional debug output">,
MarshallingInfoString<CodeGenOpts<"DebugPass">>;
def mframe_pointer_EQ : Joined<["-"], "mframe-pointer=">,
HelpText<"Specify which frame pointers to retain.">, Values<"all,non-leaf,none">,
NormalizedValuesScope<"CodeGenOptions::FramePointerKind">, NormalizedValues<["All", "NonLeaf", "None"]>,
MarshallingInfoEnum<CodeGenOpts<"FramePointer">, "None">;
def mabi_EQ_ieeelongdouble : Flag<["-"], "mabi=ieeelongdouble">,
HelpText<"Use IEEE 754 quadruple-precision for long double">,
MarshallingInfoFlag<LangOpts<"PPCIEEELongDouble">>;
Expand Down Expand Up @@ -7400,6 +7398,11 @@ def pic_is_pie : Flag<["-"], "pic-is-pie">,
HelpText<"File is for a position independent executable">,
MarshallingInfoFlag<LangOpts<"PIE">>;

def mframe_pointer_EQ : Joined<["-"], "mframe-pointer=">,
HelpText<"Specify which frame pointers to retain.">, Values<"all,non-leaf,none">,
NormalizedValuesScope<"CodeGenOptions::FramePointerKind">, NormalizedValues<["All", "NonLeaf", "None"]>,
MarshallingInfoEnum<CodeGenOpts<"FramePointer">, "None">;


def dependent_lib : Joined<["--"], "dependent-lib=">,
HelpText<"Add dependent library">,
Expand Down
133 changes: 0 additions & 133 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -409,139 +409,6 @@ static bool ShouldEnableAutolink(const ArgList &Args, const ToolChain &TC,
Default);
}

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();
}
}

static bool useFramePointerForTargetByDefault(const ArgList &Args,
const llvm::Triple &Triple) {
if (Args.hasArg(options::OPT_pg) && !Args.hasArg(options::OPT_mfentry))
return true;

if (Triple.isAndroid()) {
switch (Triple.getArch()) {
case llvm::Triple::aarch64:
case llvm::Triple::arm:
case llvm::Triple::armeb:
case llvm::Triple::thumb:
case llvm::Triple::thumbeb:
case llvm::Triple::riscv64:
return true;
default:
break;
}
}

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::sparc:
case llvm::Triple::sparcel:
case llvm::Triple::sparcv9:
case llvm::Triple::amdgcn:
case llvm::Triple::r600:
case llvm::Triple::csky:
case llvm::Triple::loongarch32:
case llvm::Triple::loongarch64:
return !areOptimizationsEnabled(Args);
default:
break;
}

if (Triple.isOSFuchsia() || Triple.isOSNetBSD()) {
return !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::mips64:
case llvm::Triple::mips64el:
case llvm::Triple::mips:
case llvm::Triple::mipsel:
case llvm::Triple::systemz:
case llvm::Triple::x86:
case llvm::Triple::x86_64:
return !areOptimizationsEnabled(Args);
default:
return true;
}
}

if (Triple.isOSWindows()) {
switch (Triple.getArch()) {
case llvm::Triple::x86:
return !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;
}
}

return true;
}

static CodeGenOptions::FramePointerKind
getFramePointerKind(const ArgList &Args, const llvm::Triple &Triple) {
// We have 4 states:
//
// 00) leaf retained, non-leaf retained
// 01) leaf retained, non-leaf omitted (this is invalid)
// 10) leaf omitted, non-leaf retained
// (what -momit-leaf-frame-pointer was designed for)
// 11) leaf omitted, non-leaf omitted
//
// "omit" options taking precedence over "no-omit" options is the only way
// to make 3 valid states representable
Arg *A = Args.getLastArg(options::OPT_fomit_frame_pointer,
options::OPT_fno_omit_frame_pointer);
bool OmitFP = A && A->getOption().matches(options::OPT_fomit_frame_pointer);
bool NoOmitFP =
A && A->getOption().matches(options::OPT_fno_omit_frame_pointer);
bool OmitLeafFP =
Args.hasFlag(options::OPT_momit_leaf_frame_pointer,
options::OPT_mno_omit_leaf_frame_pointer,
Triple.isAArch64() || Triple.isPS() || Triple.isVE() ||
(Triple.isAndroid() && Triple.isRISCV64()));
if (NoOmitFP || mustUseNonLeafFramePointerForTarget(Triple) ||
(!OmitFP && useFramePointerForTargetByDefault(Args, Triple))) {
if (OmitLeafFP)
return CodeGenOptions::FramePointerKind::NonLeaf;
return CodeGenOptions::FramePointerKind::All;
}
return CodeGenOptions::FramePointerKind::None;
}

/// Add a CC1 option to specify the debug compilation directory.
static const char *addDebugCompDirArg(const ArgList &Args,
ArgStringList &CmdArgs,
Expand Down
139 changes: 139 additions & 0 deletions clang/lib/Driver/ToolChains/CommonArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "MSP430.h"
#include "Solaris.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/CodeGenOptions.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/ObjCRuntime.h"
#include "clang/Basic/Version.h"
Expand Down Expand Up @@ -71,6 +72,144 @@ using namespace clang::driver::tools;
using namespace clang;
using namespace llvm::opt;

static bool useFramePointerForTargetByDefault(const llvm::opt::ArgList &Args,
const llvm::Triple &Triple) {
if (Args.hasArg(clang::driver::options::OPT_pg) &&
!Args.hasArg(clang::driver::options::OPT_mfentry))
return true;

if (Triple.isAndroid()) {
switch (Triple.getArch()) {
case llvm::Triple::aarch64:
case llvm::Triple::arm:
case llvm::Triple::armeb:
case llvm::Triple::thumb:
case llvm::Triple::thumbeb:
case llvm::Triple::riscv64:
return true;
default:
break;
}
}

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::sparc:
case llvm::Triple::sparcel:
case llvm::Triple::sparcv9:
case llvm::Triple::amdgcn:
case llvm::Triple::r600:
case llvm::Triple::csky:
case llvm::Triple::loongarch32:
case llvm::Triple::loongarch64:
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::mips64:
case llvm::Triple::mips64el:
case llvm::Triple::mips:
case llvm::Triple::mipsel:
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;
}
}

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();
}
}

clang::CodeGenOptions::FramePointerKind
getFramePointerKind(const llvm::opt::ArgList &Args,
const llvm::Triple &Triple) {
// We have 4 states:
//
// 00) leaf retained, non-leaf retained
// 01) leaf retained, non-leaf omitted (this is invalid)
// 10) leaf omitted, non-leaf retained
// (what -momit-leaf-frame-pointer was designed for)
// 11) leaf omitted, non-leaf omitted
//
// "omit" options taking precedence over "no-omit" options is the only way
// to make 3 valid states representable
llvm::opt::Arg *A =
Args.getLastArg(clang::driver::options::OPT_fomit_frame_pointer,
clang::driver::options::OPT_fno_omit_frame_pointer);

bool OmitFP = A && A->getOption().matches(
clang::driver::options::OPT_fomit_frame_pointer);
bool NoOmitFP = A && A->getOption().matches(
clang::driver::options::OPT_fno_omit_frame_pointer);
bool OmitLeafFP =
Args.hasFlag(clang::driver::options::OPT_momit_leaf_frame_pointer,
clang::driver::options::OPT_mno_omit_leaf_frame_pointer,
Triple.isAArch64() || Triple.isPS() || Triple.isVE() ||
(Triple.isAndroid() && Triple.isRISCV64()));
if (NoOmitFP || mustUseNonLeafFramePointerForTarget(Triple) ||
(!OmitFP && useFramePointerForTargetByDefault(Args, Triple))) {
if (OmitLeafFP)
return clang::CodeGenOptions::FramePointerKind::NonLeaf;
return clang::CodeGenOptions::FramePointerKind::All;
}
return clang::CodeGenOptions::FramePointerKind::None;
}

static void renderRpassOptions(const ArgList &Args, ArgStringList &CmdArgs,
const StringRef PluginOptPrefix) {
if (const Arg *A = Args.getLastArg(options::OPT_Rpass_EQ))
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Driver/ToolChains/CommonArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#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"
Expand Down Expand Up @@ -215,4 +216,7 @@ void addOpenMPDeviceRTL(const Driver &D, const llvm::opt::ArgList &DriverArgs,
} // end namespace driver
} // end namespace clang

clang::CodeGenOptions::FramePointerKind
getFramePointerKind(const llvm::opt::ArgList &Args, const llvm::Triple &Triple);

#endif // LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_COMMONARGS_H
19 changes: 19 additions & 0 deletions clang/lib/Driver/ToolChains/Flang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "Flang.h"
#include "CommonArgs.h"

#include "clang/Basic/CodeGenOptions.h"
#include "clang/Driver/Options.h"
#include "llvm/Frontend/Debug/Options.h"
#include "llvm/Support/FileSystem.h"
Expand Down Expand Up @@ -674,6 +675,24 @@ 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);

const char *FPKeepKindStr = nullptr;
switch (FPKeepKind) {
case CodeGenOptions::FramePointerKind::None:
FPKeepKindStr = "-mframe-pointer=none";
break;
case CodeGenOptions::FramePointerKind::NonLeaf:
FPKeepKindStr = "-mframe-pointer=non-leaf";
break;
case CodeGenOptions::FramePointerKind::All:
FPKeepKindStr = "-mframe-pointer=all";
break;
}
assert(FPKeepKindStr && "unknown FramePointerKind");
CmdArgs.push_back(FPKeepKindStr);

// Forward -mllvm options to the LLVM option parser. In practice, this means
// forwarding to `-fc1` as that's where the LLVM parser is run.
for (const Arg *A : Args.filtered(options::OPT_mllvm)) {
Expand Down
1 change: 1 addition & 0 deletions flang/test/Driver/driver-help-hidden.f90
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
! CHECK-NEXT: -fno-stack-arrays Allocate array temporaries on the heap (default)
! CHECK-NEXT: -fno-version-loops-for-stride
! CHECK-NEXT: Do not create unit-strided loops (default)
! CHECK-NEXT: -fomit-frame-pointer Omit the frame pointer from functions that don't need it. Some stack unwinding cases, such as profilers and sanitizers, may prefer specifying -fno-omit-frame-pointer. On many targets, -O1 and higher omit the frame pointer by default. -m[no-]omit-leaf-frame-pointer takes precedence for leaf functions
! CHECK-NEXT: -fopenacc Enable OpenACC
! CHECK-NEXT: -fopenmp-assume-no-nested-parallelism
! CHECK-NEXT: Assert no nested parallel regions in the GPU
Expand Down

0 comments on commit ea4eb69

Please sign in to comment.