Skip to content

[5.3] IRGen: Always eliminate frame pointers of leaf functions #31968

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/swift/AST/IRGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ class IRGenOptions {
unsigned DisableLLVMSLPVectorizer : 1;

/// Disable frame pointer elimination?
unsigned DisableFPElimLeaf : 1;
unsigned DisableFPElim : 1;

/// Special codegen for playgrounds.
Expand Down Expand Up @@ -322,6 +323,7 @@ class IRGenOptions {
DisableClangModuleSkeletonCUs(false), UseJIT(false),
IntegratedREPL(false), DisableLLVMOptzns(false),
DisableSwiftSpecificLLVMOptzns(false), DisableLLVMSLPVectorizer(false),
DisableFPElimLeaf(false),
DisableFPElim(true), Playground(false), EmitStackPromotionChecks(false),
FunctionSections(false), PrintInlineTree(false), EmbedMode(IRGenEmbedMode::None),
HasValueNamesSetting(false), ValueNames(false),
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Option/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,10 @@ def enable_private_imports : Flag<["-"], "enable-private-imports">,
Flags<[FrontendOption, NoInteractiveOption, HelpHidden]>,
HelpText<"Allows this module's internal and private API to be accessed">;

def disable_leaf_frame_pointer_elim : Flag<["-"], "no-omit-leaf-frame-pointer">,
Flags<[FrontendOption, NoInteractiveOption, HelpHidden]>,
HelpText<"Don't omit the frame pointer for leaf functions">;

def sanitize_EQ : CommaJoined<["-"], "sanitize=">,
Flags<[FrontendOption, NoInteractiveOption]>, MetaVarName<"<check>">,
HelpText<"Turn on runtime checks for erroneous behavior.">;
Expand Down
2 changes: 2 additions & 0 deletions lib/Driver/ToolChains.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,8 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
arguments.push_back("-enable-anonymous-context-mangled-names");
}

inputArgs.AddLastArg(arguments, options::OPT_disable_leaf_frame_pointer_elim);

// Pass through any subsystem flags.
inputArgs.AddAllArgs(arguments, options::OPT_Xllvm);
inputArgs.AddAllArgs(arguments, options::OPT_Xcc);
Expand Down
3 changes: 3 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1477,6 +1477,9 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
getRuntimeCompatVersion();
}

if (Args.hasArg(OPT_disable_leaf_frame_pointer_elim))
Opts.DisableFPElimLeaf = true;

return false;
}

Expand Down
58 changes: 42 additions & 16 deletions lib/IRGen/IRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,21 +89,34 @@ static llvm::PointerType *createStructPointerType(IRGenModule &IGM,
return createStructType(IGM, name, types)->getPointerTo(DefaultAS);
};

static clang::CodeGenerator *createClangCodeGenerator(ASTContext &Context,
llvm::LLVMContext &LLVMContext,
const IRGenOptions &Opts,
StringRef ModuleName,
StringRef PD) {
static clang::CodeGenOptions::FramePointerKind
shouldUseFramePointer(const IRGenOptions &Opts, const llvm::Triple &triple) {
if (Opts.DisableFPElim) {
// General frame pointer elimination is disabled.
// Should we at least eliminate in leaf functions?
// Currently we only do that on arm64 (this matches the behavior of clang).
return Opts.DisableFPElimLeaf
? clang::CodeGenOptions::FramePointerKind::All
: triple.isAArch64()
? clang::CodeGenOptions::FramePointerKind::NonLeaf
: clang::CodeGenOptions::FramePointerKind::All;
}

return clang::CodeGenOptions::FramePointerKind::None;
}

static clang::CodeGenerator *
createClangCodeGenerator(ASTContext &Context, llvm::LLVMContext &LLVMContext,
const IRGenOptions &Opts, StringRef ModuleName,
StringRef PD, const llvm::Triple &triple) {
auto Loader = Context.getClangModuleLoader();
auto *Importer = static_cast<ClangImporter*>(&*Loader);
assert(Importer && "No clang module loader!");
auto &ClangContext = Importer->getClangASTContext();

auto &CGO = Importer->getClangCodeGenOpts();
CGO.OptimizationLevel = Opts.shouldOptimize() ? 3 : 0;
CGO.setFramePointer(Opts.DisableFPElim
? clang::CodeGenOptions::FramePointerKind::All
: clang::CodeGenOptions::FramePointerKind::None);
CGO.setFramePointer(shouldUseFramePointer(Opts, triple));
CGO.DiscardValueNames = !Opts.shouldProvideValueNames();
switch (Opts.DebugInfoLevel) {
case IRGenDebugInfoLevel::None:
Expand Down Expand Up @@ -193,17 +206,17 @@ static void sanityCheckStdlib(IRGenModule &IGM) {

IRGenModule::IRGenModule(IRGenerator &irgen,
std::unique_ptr<llvm::TargetMachine> &&target,
SourceFile *SF,
StringRef ModuleName, StringRef OutputFilename,
SourceFile *SF, StringRef ModuleName,
StringRef OutputFilename,
StringRef MainInputFilenameForDebugInfo,
StringRef PrivateDiscriminator)
: LLVMContext(new llvm::LLVMContext()),
IRGen(irgen), Context(irgen.SIL.getASTContext()),
: LLVMContext(new llvm::LLVMContext()), IRGen(irgen),
Context(irgen.SIL.getASTContext()),
// The LLVMContext (and the IGM itself) will get deleted by the IGMDeleter
// as long as the IGM is registered with the IRGenerator.
ClangCodeGen(createClangCodeGenerator(Context, *LLVMContext,
irgen.Opts,
ModuleName, PrivateDiscriminator)),
ClangCodeGen(createClangCodeGenerator(Context, *LLVMContext, irgen.Opts,
ModuleName, PrivateDiscriminator,
irgen.getEffectiveClangTriple())),
Module(*ClangCodeGen->GetModule()),
DataLayout(irgen.getClangDataLayout()),
Triple(irgen.getEffectiveClangTriple()), TargetMachine(std::move(target)),
Expand Down Expand Up @@ -983,7 +996,20 @@ bool swift::irgen::shouldRemoveTargetFeature(StringRef feature) {

void IRGenModule::setHasFramePointer(llvm::AttrBuilder &Attrs,
bool HasFramePointer) {
Attrs.addAttribute("frame-pointer", HasFramePointer ? "all" : "none");
if (!HasFramePointer) {
Attrs.addAttribute("frame-pointer", "none");
return;
}
if (IRGen.Opts.DisableFPElimLeaf) {
Attrs.addAttribute("frame-pointer", "all");
return;
}

// We omit frame pointers for leaf functions only for arm64 for now (matching
// clang's behavior).
auto framePointer =
IRGen.getEffectiveClangTriple().isAArch64() ? "non-leaf" : "all";
Attrs.addAttribute("frame-pointer", framePointer);
}

void IRGenModule::setHasFramePointer(llvm::Function *F,
Expand Down
2 changes: 1 addition & 1 deletion test/IRGen/c_globals.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ public func testCaptureGlobal() {
}

// CHECK-DAG: attributes [[CLANG_FUNC_ATTR]] = { noinline nounwind {{.*}}"frame-pointer"="all"{{.*}}
// CHECK-DAG: attributes [[SWIFT_FUNC_ATTR]] = { "frame-pointer"="all" {{.*}}"target-cpu"
// CHECK-DAG: attributes [[SWIFT_FUNC_ATTR]] = { {{.*}}"frame-pointer"="all" {{.*}}"target-cpu"
64 changes: 64 additions & 0 deletions test/IRGen/framepointer.sil
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// RUN: %target-swift-frontend -primary-file %s -emit-ir | %FileCheck %s --check-prefix=CHECK
// RUN: %target-swift-frontend -primary-file %s -emit-ir -no-omit-leaf-frame-pointer| %FileCheck %s --check-prefix=CHECK-ALL
// RUN: %target-swift-frontend -primary-file %s -S | %FileCheck %s --check-prefix=CHECKASM --check-prefix=CHECKASM-%target-os-%target-cpu

// REQUIRES: CPU=x86_64

sil_stage canonical

import Swift

sil @leaf_function_no_frame_pointer : $@convention(thin) (Int32) -> Int32 {
entry(%i : $Int32):
return %i : $Int32
}

sil @non_leaf_function_with_frame_pointer : $@convention(thin) (Int32) -> Int32 {
entry(%i : $Int32):
%f = function_ref @leaf_function_no_frame_pointer : $@convention(thin) (Int32) -> Int32
%r = apply %f(%i) : $@convention(thin) (Int32) -> Int32
return %r : $Int32
}

// CHECK: define{{.*}} swiftcc i32 @leaf_function_no_frame_pointer(i32 %0) [[ATTR:#.*]] {
// CHECK: entry:
// CHECK: ret i32 %0
// CHECK: }

// CHECK: define{{.*}} swiftcc i32 @non_leaf_function_with_frame_pointer(i32 %0) [[ATTR]] {
// CHECK: entry:
// CHECK: %1 = call swiftcc i32 @leaf_function_no_frame_pointer(i32 %0)
// CHECK: ret i32 %1
// CHECK: }

// CHECK: attributes [[ATTR]] = {{{.*}}"frame-pointer"="all"

// CHECK-ALL: define{{.*}} swiftcc i32 @leaf_function_no_frame_pointer(i32 %0) [[ATTR:#.*]] {
// CHECK-ALL: entry:
// CHECK-ALL: ret i32 %0
// CHECK-ALL: }

// CHECK-ALL: define{{.*}} swiftcc i32 @non_leaf_function_with_frame_pointer(i32 %0) [[ATTR]] {
// CHECK-ALL: entry:
// CHECK-ALL: %1 = call swiftcc i32 @leaf_function_no_frame_pointer(i32 %0)
// CHECK-ALL: ret i32 %1
// CHECK-ALL: }

// CHECK-ALL: attributes [[ATTR]] = {{{.*}}"frame-pointer"="all"

// Silence other os-archs.
// CHECKASM: {{.*}}

// CHECKASM-macosx-x86_64-LABEL: _leaf_function_no_frame_pointer:
// CHECKASM-macosx-x86_64: push
// CHECKASM-macosx-x86_64: movl %edi, %eax
// CHECKASM-macosx-x86_64: pop
// CHECKASM-macosx-x86_64: ret


// CHECKASM-macosx-x86_64-LABEL: _non_leaf_function_with_frame_pointer:
// CHECKASM-macosx-x86_64: pushq %rbp
// CHECKASM-macosx-x86_64: movq %rsp, %rbp
// CHECKASM-macosx-x86_64: callq _leaf_function_no_frame_pointer
// CHECKASM-macosx-x86_64: popq %rbp
// CHECKASM-macosx-x86_64: ret
73 changes: 73 additions & 0 deletions test/IRGen/framepointer_arm64.sil
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// RUN: %target-swift-frontend -target arm64-apple-ios8.0 -primary-file %s -emit-ir | %FileCheck %s --check-prefix=CHECK
// RUN: %target-swift-frontend -target arm64-apple-ios8.0 -primary-file %s -emit-ir -no-omit-leaf-frame-pointer| %FileCheck %s --check-prefix=CHECK-ALL
// RUN: %target-swift-frontend -target arm64-apple-ios8.0 -primary-file %s -S | %FileCheck %s --check-prefix=CHECKASM
// RUN: %target-swift-frontend -target arm64-apple-ios8.0 -primary-file %s -S -no-omit-leaf-frame-pointer | %FileCheck %s --check-prefix=CHECKASM-ALL

// REQUIRES: CODEGENERATOR=AArch64

// UNSUPPORTED: OS=linux-gnu
// UNSUPPORTED: OS=windows

sil_stage canonical

import Swift

sil @leaf_function_no_frame_pointer : $@convention(thin) (Int32) -> Int32 {
entry(%i : $Int32):
return %i : $Int32
}

sil @non_leaf_function_with_frame_pointer : $@convention(thin) (Int32) -> Int32 {
entry(%i : $Int32):
%f = function_ref @leaf_function_no_frame_pointer : $@convention(thin) (Int32) -> Int32
%r = apply %f(%i) : $@convention(thin) (Int32) -> Int32
return %r : $Int32
}

// CHECK: define{{.*}} swiftcc i32 @leaf_function_no_frame_pointer(i32 %0) [[ATTR:#.*]] {
// CHECK: entry:
// CHECK: ret i32 %0
// CHECK: }

// CHECK: define{{.*}} swiftcc i32 @non_leaf_function_with_frame_pointer(i32 %0) [[ATTR]] {
// CHECK: entry:
// CHECK: %1 = call swiftcc i32 @leaf_function_no_frame_pointer(i32 %0)
// CHECK: ret i32 %1
// CHECK: }

// CHECK: attributes [[ATTR]] = {{{.*}}"frame-pointer"="non-leaf"

// CHECK-ALL: define{{.*}} swiftcc i32 @leaf_function_no_frame_pointer(i32 %0) [[ATTR:#.*]] {
// CHECK-ALL: entry:
// CHECK-ALL: ret i32 %0
// CHECK-ALL: }

// CHECK-ALL: define{{.*}} swiftcc i32 @non_leaf_function_with_frame_pointer(i32 %0) [[ATTR]] {
// CHECK-ALL: entry:
// CHECK-ALL: %1 = call swiftcc i32 @leaf_function_no_frame_pointer(i32 %0)
// CHECK-ALL: ret i32 %1
// CHECK-ALL: }

// CHECK-ALL: attributes [[ATTR]] = {{{.*}}"frame-pointer"="all"

// CHECKASM-LABEL: _leaf_function_no_frame_pointer:
// CHECKASM-NOT: stp
// CHECKASM-NOT: ldp
// CHECKASM: ret

// CHECKASM-LABEL: _non_leaf_function_with_frame_pointer:
// CHECKASM: stp
// CHECKASM: _leaf_function_no_frame_pointer
// CHECKASM: ldp
// CHECKASM: ret

// CHECKASM-ALL-LABEL: _leaf_function_no_frame_pointer:
// CHECKASM-ALL: stp
// CHECKASM-ALL: ldp
// CHECKASM-ALL: ret

// CHECKASM-ALL-LABEL: _non_leaf_function_with_frame_pointer:
// CHECKASM-ALL: stp
// CHECKASM-ALL: _leaf_function_no_frame_pointer
// CHECKASM-ALL: ldp
// CHECKASM-ALL: ret
6 changes: 0 additions & 6 deletions test/Misc/tbi.sil
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,13 @@

// NO_TBI-LABEL: .globl _testTBI
// NO_TBI: _testTBI
// NO_TBI-NEXT: stp
// NO_TBI-NEXT: mov
// NO_TBI-NEXT: and
// NO_TBI-NEXT: ldr
// NO_TBI-NEXT: ldp
// NO_TBI-NEXT: ret

// TBI-LABEL: .globl _testTBI
// TBI: _testTBI:
// TBI-NEXT: stp
// TBI-NEXT: mov
// TBI-NEXT: ldr
// TBI-NEXT: ldp
// TBI-NEXT: ret

sil_stage canonical
Expand Down