[IR][sanitizer] Add module flag "frame-pointer" and set it for cc1 -mframe-pointer={non-leaf,all}

The Linux kernel objtool diagnostic `call without frame pointer save/setup`
arise in multiple instrumentation passes (asan/tsan/gcov). With the mechanism
introduced in D100251, it's trivial to respect the command line
-m[no-]omit-leaf-frame-pointer/-f[no-]omit-frame-pointer, so let's do it.

Fix: https://github.com/ClangBuiltLinux/linux/issues/1236 (tsan)
Fix: https://github.com/ClangBuiltLinux/linux/issues/1238 (asan)

Also document the function attribute "frame-pointer" which is long overdue.

Differential Revision: https://reviews.llvm.org/D101016
This commit is contained in:
Fangrui Song 2021-04-22 18:07:29 -07:00
parent 879cbac08b
commit 2786e673c7
10 changed files with 105 additions and 17 deletions

View File

@ -744,6 +744,18 @@ void CodeGenModule::Release() {
if (CodeGenOpts.UnwindTables) if (CodeGenOpts.UnwindTables)
getModule().setUwtable(); getModule().setUwtable();
switch (CodeGenOpts.getFramePointer()) {
case CodeGenOptions::FramePointerKind::None:
// 0 ("none") is the default.
break;
case CodeGenOptions::FramePointerKind::NonLeaf:
getModule().setFramePointer(llvm::FramePointerKind::NonLeaf);
break;
case CodeGenOptions::FramePointerKind::All:
getModule().setFramePointer(llvm::FramePointerKind::All);
break;
}
SimplifyPersonality(); SimplifyPersonality();
if (getCodeGenOpts().EmitDeclMetadata) if (getCodeGenOpts().EmitDeclMetadata)

View File

@ -0,0 +1,19 @@
/// -mframe-pointer=none sets the module flag "frame-pointer" (merge behavior: max).
/// asan synthesized ctor/dtor get the "frame-pointer" function attribute if not zero (default).
// RUN: %clang_cc1 -emit-llvm -fsanitize=address -mframe-pointer=none %s -o - | FileCheck %s --check-prefix=NONE
// RUN: %clang_cc1 -emit-llvm -fsanitize=address -mframe-pointer=non-leaf %s -o - | FileCheck %s --check-prefix=NONLEAF
// RUN: %clang_cc1 -emit-llvm -fsanitize=address -mframe-pointer=all %s -o - | FileCheck %s --check-prefix=ALL
int global;
// NONE: define internal void @asan.module_ctor() #[[#ATTR:]] {
// NONE: define internal void @asan.module_dtor() #[[#ATTR]] {
// NONE: attributes #[[#ATTR]] = { nounwind }
// NONLEAF: define internal void @asan.module_ctor() #[[#ATTR:]] {
// NONLEAF: define internal void @asan.module_dtor() #[[#ATTR]] {
// NONLEAF: attributes #[[#ATTR]] = { nounwind "frame-pointer"="non-leaf" }
// ALL: define internal void @asan.module_ctor() #[[#ATTR:]] {
// ALL: define internal void @asan.module_dtor() #[[#ATTR]] {
// ALL: attributes #[[#ATTR]] = { nounwind "frame-pointer"="all" }

View File

@ -1529,6 +1529,16 @@ example:
can prove that the function does not execute any convergent operations. can prove that the function does not execute any convergent operations.
Similarly, the optimizer may remove ``convergent`` on calls/invokes when it Similarly, the optimizer may remove ``convergent`` on calls/invokes when it
can prove that the call/invoke cannot call a convergent function. can prove that the call/invoke cannot call a convergent function.
``"frame-pointer"``
This attribute tells the code generator whether the function
should keep the frame pointer. The code generator may emit the frame pointer
even if this attribute says the frame pointer can be eliminated.
The allowed string values are:
* ``"none"`` (default) - the frame pointer can be eliminated.
* ``"non-leaf"`` - the frame pointer should be kept if the function calls
other functions.
* ``"all"`` - the frame pointer should be kept.
``hot`` ``hot``
This attribute indicates that this function is a hot spot of the program This attribute indicates that this function is a hot spot of the program
execution. The function will be optimized more aggressively and will be execution. The function will be optimized more aggressively and will be
@ -6994,6 +7004,24 @@ An example of module flags:
contain a flag with the ID ``!"foo"`` that has the value '1' after linking is contain a flag with the ID ``!"foo"`` that has the value '1' after linking is
performed. performed.
Synthesized Functions Module Flags Metadata
-------------------------------------------
These metadata specify the default attributes synthesized functions should have.
These metadata are currently respected by a few instrumentation passes, such as
sanitizers.
These metadata correspond to a few function attributes with significant code
generation behaviors. Function attributes with just optimization purposes
should not be listed because the performance impact of these synthesized
functions is small.
- "frame-pointer": **Max**. The value can be 0, 1, or 2. A synthesized function
will get the "frame-pointer" function attribute, with value being "none",
"non-leaf", or "all", respectively.
- "uwtable": **Max**. The value can be 0 or 1. If the value is 1, a synthesized
function will get the ``uwtable`` function attribute.
Objective-C Garbage Collection Module Flags Metadata Objective-C Garbage Collection Module Flags Metadata
---------------------------------------------------- ----------------------------------------------------

View File

@ -53,7 +53,7 @@ Optional<CodeGenFileType> getExplicitFileType();
CodeGenFileType getFileType(); CodeGenFileType getFileType();
llvm::FramePointer::FP getFramePointerUsage(); FramePointerKind getFramePointerUsage();
bool getEnableUnsafeFPMath(); bool getEnableUnsafeFPMath();

View File

@ -890,6 +890,11 @@ public:
bool getUwtable() const; bool getUwtable() const;
void setUwtable(); void setUwtable();
/// Get/set whether synthesized functions should get the "frame-pointer"
/// attribute.
FramePointerKind getFramePointer() const;
void setFramePointer(FramePointerKind Kind);
/// @name Utility functions for querying and setting the build SDK version /// @name Utility functions for querying and setting the build SDK version
/// @{ /// @{

View File

@ -66,10 +66,8 @@ namespace llvm {
CGFT_Null // Do not emit any output. CGFT_Null // Do not emit any output.
}; };
// Specify effect of frame pointer elimination optimization. // Specify what functions should keep the frame pointer.
namespace FramePointer { enum class FramePointerKind { None, NonLeaf, All };
enum FP {All, NonLeaf, None};
}
} // end llvm namespace } // end llvm namespace

View File

@ -53,7 +53,7 @@ CGOPT(ThreadModel::Model, ThreadModel)
CGOPT_EXP(CodeModel::Model, CodeModel) CGOPT_EXP(CodeModel::Model, CodeModel)
CGOPT(ExceptionHandling, ExceptionModel) CGOPT(ExceptionHandling, ExceptionModel)
CGOPT_EXP(CodeGenFileType, FileType) CGOPT_EXP(CodeGenFileType, FileType)
CGOPT(FramePointer::FP, FramePointerUsage) CGOPT(FramePointerKind, FramePointerUsage)
CGOPT(bool, EnableUnsafeFPMath) CGOPT(bool, EnableUnsafeFPMath)
CGOPT(bool, EnableNoInfsFPMath) CGOPT(bool, EnableNoInfsFPMath)
CGOPT(bool, EnableNoNaNsFPMath) CGOPT(bool, EnableNoNaNsFPMath)
@ -183,16 +183,16 @@ codegen::RegisterCodeGenFlags::RegisterCodeGenFlags() {
"Emit nothing, for performance testing"))); "Emit nothing, for performance testing")));
CGBINDOPT(FileType); CGBINDOPT(FileType);
static cl::opt<FramePointer::FP> FramePointerUsage( static cl::opt<FramePointerKind> FramePointerUsage(
"frame-pointer", "frame-pointer",
cl::desc("Specify frame pointer elimination optimization"), cl::desc("Specify frame pointer elimination optimization"),
cl::init(FramePointer::None), cl::init(FramePointerKind::None),
cl::values( cl::values(
clEnumValN(FramePointer::All, "all", clEnumValN(FramePointerKind::All, "all",
"Disable frame pointer elimination"), "Disable frame pointer elimination"),
clEnumValN(FramePointer::NonLeaf, "non-leaf", clEnumValN(FramePointerKind::NonLeaf, "non-leaf",
"Disable frame pointer elimination for non-leaf frame"), "Disable frame pointer elimination for non-leaf frame"),
clEnumValN(FramePointer::None, "none", clEnumValN(FramePointerKind::None, "none",
"Enable frame pointer elimination"))); "Enable frame pointer elimination")));
CGBINDOPT(FramePointerUsage); CGBINDOPT(FramePointerUsage);
@ -662,11 +662,11 @@ void codegen::setFunctionAttributes(StringRef CPU, StringRef Features,
} }
if (FramePointerUsageView->getNumOccurrences() > 0 && if (FramePointerUsageView->getNumOccurrences() > 0 &&
!F.hasFnAttribute("frame-pointer")) { !F.hasFnAttribute("frame-pointer")) {
if (getFramePointerUsage() == FramePointer::All) if (getFramePointerUsage() == FramePointerKind::All)
NewAttrs.addAttribute("frame-pointer", "all"); NewAttrs.addAttribute("frame-pointer", "all");
else if (getFramePointerUsage() == FramePointer::NonLeaf) else if (getFramePointerUsage() == FramePointerKind::NonLeaf)
NewAttrs.addAttribute("frame-pointer", "non-leaf"); NewAttrs.addAttribute("frame-pointer", "non-leaf");
else if (getFramePointerUsage() == FramePointer::None) else if (getFramePointerUsage() == FramePointerKind::None)
NewAttrs.addAttribute("frame-pointer", "none"); NewAttrs.addAttribute("frame-pointer", "none");
} }
if (DisableTailCallsView->getNumOccurrences() > 0) if (DisableTailCallsView->getNumOccurrences() > 0)

View File

@ -335,8 +335,21 @@ Function *Function::createWithDefaultAttr(FunctionType *Ty,
unsigned AddrSpace, const Twine &N, unsigned AddrSpace, const Twine &N,
Module *M) { Module *M) {
auto *F = new Function(Ty, Linkage, AddrSpace, N, M); auto *F = new Function(Ty, Linkage, AddrSpace, N, M);
AttrBuilder B;
if (M->getUwtable()) if (M->getUwtable())
F->addAttribute(AttributeList::FunctionIndex, Attribute::UWTable); B.addAttribute(Attribute::UWTable);
switch (M->getFramePointer()) {
case FramePointerKind::None:
// 0 ("none") is the default.
break;
case FramePointerKind::NonLeaf:
B.addAttribute("frame-pointer", "non-leaf");
break;
case FramePointerKind::All:
B.addAttribute("frame-pointer", "all");
break;
}
F->addAttributes(AttributeList::FunctionIndex, B);
return F; return F;
} }

View File

@ -676,6 +676,16 @@ bool Module::getUwtable() const {
void Module::setUwtable() { addModuleFlag(ModFlagBehavior::Max, "uwtable", 1); } void Module::setUwtable() { addModuleFlag(ModFlagBehavior::Max, "uwtable", 1); }
FramePointerKind Module::getFramePointer() const {
auto *Val = cast_or_null<ConstantAsMetadata>(getModuleFlag("frame-pointer"));
return static_cast<FramePointerKind>(
Val ? cast<ConstantInt>(Val->getValue())->getZExtValue() : 0);
}
void Module::setFramePointer(FramePointerKind Kind) {
addModuleFlag(ModFlagBehavior::Max, "frame-pointer", static_cast<int>(Kind));
}
void Module::setSDKVersion(const VersionTuple &V) { void Module::setSDKVersion(const VersionTuple &V) {
SmallVector<unsigned, 3> Entries; SmallVector<unsigned, 3> Entries;
Entries.push_back(V.getMajor()); Entries.push_back(V.getMajor());

View File

@ -10,12 +10,15 @@ entry:
ret i32 %tmp ret i32 %tmp
} }
!llvm.module.flags = !{!0} !llvm.module.flags = !{!0, !1}
;; Due to -fasynchronous-unwind-tables. ;; Due to -fasynchronous-unwind-tables.
!0 = !{i32 7, !"uwtable", i32 1} !0 = !{i32 7, !"uwtable", i32 1}
;; Due to -fno-omit-frame-pointer.
!1 = !{i32 7, !"frame-pointer", i32 2}
;; Set the uwtable attribute on ctor/dtor. ;; Set the uwtable attribute on ctor/dtor.
; CHECK: define internal void @asan.module_ctor() #[[#ATTR:]] ; CHECK: define internal void @asan.module_ctor() #[[#ATTR:]]
; CHECK: define internal void @asan.module_dtor() #[[#ATTR]] ; CHECK: define internal void @asan.module_dtor() #[[#ATTR]]
; CHECK: attributes #[[#ATTR]] = { nounwind uwtable } ; CHECK: attributes #[[#ATTR]] = { nounwind uwtable "frame-pointer"="all" }