Skip to content

Commit d831670

Browse files
[5.3] IRGen: Always eliminate frame pointers of leaf functions (#31968)
* [5.3] IRGen: Always eliminate frame pointers of leaf functions Explanation: This change makes it such that we don't emit frame pointers in leaf functions leading to decreased code size and improved performance. Scope: Should not visibly affect Swift programs other than making them faster. Risk: Medium. There might be unknown dependencies on having the frame pointer available in leaf functions. Testing: Unit test added Reviewer: Andrew Trick rdar://20933449 * Fix test * Only use omit the frame pointer on arm64, x86_64 darwin needs it for some its tools * Fix test on linux
1 parent 1c00f54 commit d831670

File tree

9 files changed

+191
-23
lines changed

9 files changed

+191
-23
lines changed

include/swift/AST/IRGenOptions.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ class IRGenOptions {
194194
unsigned DisableLLVMSLPVectorizer : 1;
195195

196196
/// Disable frame pointer elimination?
197+
unsigned DisableFPElimLeaf : 1;
197198
unsigned DisableFPElim : 1;
198199

199200
/// Special codegen for playgrounds.
@@ -322,6 +323,7 @@ class IRGenOptions {
322323
DisableClangModuleSkeletonCUs(false), UseJIT(false),
323324
IntegratedREPL(false), DisableLLVMOptzns(false),
324325
DisableSwiftSpecificLLVMOptzns(false), DisableLLVMSLPVectorizer(false),
326+
DisableFPElimLeaf(false),
325327
DisableFPElim(true), Playground(false), EmitStackPromotionChecks(false),
326328
FunctionSections(false), PrintInlineTree(false), EmbedMode(IRGenEmbedMode::None),
327329
HasValueNamesSetting(false), ValueNames(false),

include/swift/Option/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,10 @@ def enable_private_imports : Flag<["-"], "enable-private-imports">,
10121012
Flags<[FrontendOption, NoInteractiveOption, HelpHidden]>,
10131013
HelpText<"Allows this module's internal and private API to be accessed">;
10141014

1015+
def disable_leaf_frame_pointer_elim : Flag<["-"], "no-omit-leaf-frame-pointer">,
1016+
Flags<[FrontendOption, NoInteractiveOption, HelpHidden]>,
1017+
HelpText<"Don't omit the frame pointer for leaf functions">;
1018+
10151019
def sanitize_EQ : CommaJoined<["-"], "sanitize=">,
10161020
Flags<[FrontendOption, NoInteractiveOption]>, MetaVarName<"<check>">,
10171021
HelpText<"Turn on runtime checks for erroneous behavior.">;

lib/Driver/ToolChains.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,8 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
289289
arguments.push_back("-enable-anonymous-context-mangled-names");
290290
}
291291

292+
inputArgs.AddLastArg(arguments, options::OPT_disable_leaf_frame_pointer_elim);
293+
292294
// Pass through any subsystem flags.
293295
inputArgs.AddAllArgs(arguments, options::OPT_Xllvm);
294296
inputArgs.AddAllArgs(arguments, options::OPT_Xcc);

lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1477,6 +1477,9 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
14771477
getRuntimeCompatVersion();
14781478
}
14791479

1480+
if (Args.hasArg(OPT_disable_leaf_frame_pointer_elim))
1481+
Opts.DisableFPElimLeaf = true;
1482+
14801483
return false;
14811484
}
14821485

lib/IRGen/IRGenModule.cpp

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -89,21 +89,34 @@ static llvm::PointerType *createStructPointerType(IRGenModule &IGM,
8989
return createStructType(IGM, name, types)->getPointerTo(DefaultAS);
9090
};
9191

92-
static clang::CodeGenerator *createClangCodeGenerator(ASTContext &Context,
93-
llvm::LLVMContext &LLVMContext,
94-
const IRGenOptions &Opts,
95-
StringRef ModuleName,
96-
StringRef PD) {
92+
static clang::CodeGenOptions::FramePointerKind
93+
shouldUseFramePointer(const IRGenOptions &Opts, const llvm::Triple &triple) {
94+
if (Opts.DisableFPElim) {
95+
// General frame pointer elimination is disabled.
96+
// Should we at least eliminate in leaf functions?
97+
// Currently we only do that on arm64 (this matches the behavior of clang).
98+
return Opts.DisableFPElimLeaf
99+
? clang::CodeGenOptions::FramePointerKind::All
100+
: triple.isAArch64()
101+
? clang::CodeGenOptions::FramePointerKind::NonLeaf
102+
: clang::CodeGenOptions::FramePointerKind::All;
103+
}
104+
105+
return clang::CodeGenOptions::FramePointerKind::None;
106+
}
107+
108+
static clang::CodeGenerator *
109+
createClangCodeGenerator(ASTContext &Context, llvm::LLVMContext &LLVMContext,
110+
const IRGenOptions &Opts, StringRef ModuleName,
111+
StringRef PD, const llvm::Triple &triple) {
97112
auto Loader = Context.getClangModuleLoader();
98113
auto *Importer = static_cast<ClangImporter*>(&*Loader);
99114
assert(Importer && "No clang module loader!");
100115
auto &ClangContext = Importer->getClangASTContext();
101116

102117
auto &CGO = Importer->getClangCodeGenOpts();
103118
CGO.OptimizationLevel = Opts.shouldOptimize() ? 3 : 0;
104-
CGO.setFramePointer(Opts.DisableFPElim
105-
? clang::CodeGenOptions::FramePointerKind::All
106-
: clang::CodeGenOptions::FramePointerKind::None);
119+
CGO.setFramePointer(shouldUseFramePointer(Opts, triple));
107120
CGO.DiscardValueNames = !Opts.shouldProvideValueNames();
108121
switch (Opts.DebugInfoLevel) {
109122
case IRGenDebugInfoLevel::None:
@@ -193,17 +206,17 @@ static void sanityCheckStdlib(IRGenModule &IGM) {
193206

194207
IRGenModule::IRGenModule(IRGenerator &irgen,
195208
std::unique_ptr<llvm::TargetMachine> &&target,
196-
SourceFile *SF,
197-
StringRef ModuleName, StringRef OutputFilename,
209+
SourceFile *SF, StringRef ModuleName,
210+
StringRef OutputFilename,
198211
StringRef MainInputFilenameForDebugInfo,
199212
StringRef PrivateDiscriminator)
200-
: LLVMContext(new llvm::LLVMContext()),
201-
IRGen(irgen), Context(irgen.SIL.getASTContext()),
213+
: LLVMContext(new llvm::LLVMContext()), IRGen(irgen),
214+
Context(irgen.SIL.getASTContext()),
202215
// The LLVMContext (and the IGM itself) will get deleted by the IGMDeleter
203216
// as long as the IGM is registered with the IRGenerator.
204-
ClangCodeGen(createClangCodeGenerator(Context, *LLVMContext,
205-
irgen.Opts,
206-
ModuleName, PrivateDiscriminator)),
217+
ClangCodeGen(createClangCodeGenerator(Context, *LLVMContext, irgen.Opts,
218+
ModuleName, PrivateDiscriminator,
219+
irgen.getEffectiveClangTriple())),
207220
Module(*ClangCodeGen->GetModule()),
208221
DataLayout(irgen.getClangDataLayout()),
209222
Triple(irgen.getEffectiveClangTriple()), TargetMachine(std::move(target)),
@@ -983,7 +996,20 @@ bool swift::irgen::shouldRemoveTargetFeature(StringRef feature) {
983996

984997
void IRGenModule::setHasFramePointer(llvm::AttrBuilder &Attrs,
985998
bool HasFramePointer) {
986-
Attrs.addAttribute("frame-pointer", HasFramePointer ? "all" : "none");
999+
if (!HasFramePointer) {
1000+
Attrs.addAttribute("frame-pointer", "none");
1001+
return;
1002+
}
1003+
if (IRGen.Opts.DisableFPElimLeaf) {
1004+
Attrs.addAttribute("frame-pointer", "all");
1005+
return;
1006+
}
1007+
1008+
// We omit frame pointers for leaf functions only for arm64 for now (matching
1009+
// clang's behavior).
1010+
auto framePointer =
1011+
IRGen.getEffectiveClangTriple().isAArch64() ? "non-leaf" : "all";
1012+
Attrs.addAttribute("frame-pointer", framePointer);
9871013
}
9881014

9891015
void IRGenModule::setHasFramePointer(llvm::Function *F,

test/IRGen/c_globals.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ public func testCaptureGlobal() {
3232
}
3333

3434
// CHECK-DAG: attributes [[CLANG_FUNC_ATTR]] = { noinline nounwind {{.*}}"frame-pointer"="all"{{.*}}
35-
// CHECK-DAG: attributes [[SWIFT_FUNC_ATTR]] = { "frame-pointer"="all" {{.*}}"target-cpu"
35+
// CHECK-DAG: attributes [[SWIFT_FUNC_ATTR]] = { {{.*}}"frame-pointer"="all" {{.*}}"target-cpu"

test/IRGen/framepointer.sil

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// RUN: %target-swift-frontend -primary-file %s -emit-ir | %FileCheck %s --check-prefix=CHECK
2+
// RUN: %target-swift-frontend -primary-file %s -emit-ir -no-omit-leaf-frame-pointer| %FileCheck %s --check-prefix=CHECK-ALL
3+
// RUN: %target-swift-frontend -primary-file %s -S | %FileCheck %s --check-prefix=CHECKASM --check-prefix=CHECKASM-%target-os-%target-cpu
4+
5+
// REQUIRES: CPU=x86_64
6+
7+
sil_stage canonical
8+
9+
import Swift
10+
11+
sil @leaf_function_no_frame_pointer : $@convention(thin) (Int32) -> Int32 {
12+
entry(%i : $Int32):
13+
return %i : $Int32
14+
}
15+
16+
sil @non_leaf_function_with_frame_pointer : $@convention(thin) (Int32) -> Int32 {
17+
entry(%i : $Int32):
18+
%f = function_ref @leaf_function_no_frame_pointer : $@convention(thin) (Int32) -> Int32
19+
%r = apply %f(%i) : $@convention(thin) (Int32) -> Int32
20+
return %r : $Int32
21+
}
22+
23+
// CHECK: define{{.*}} swiftcc i32 @leaf_function_no_frame_pointer(i32 %0) [[ATTR:#.*]] {
24+
// CHECK: entry:
25+
// CHECK: ret i32 %0
26+
// CHECK: }
27+
28+
// CHECK: define{{.*}} swiftcc i32 @non_leaf_function_with_frame_pointer(i32 %0) [[ATTR]] {
29+
// CHECK: entry:
30+
// CHECK: %1 = call swiftcc i32 @leaf_function_no_frame_pointer(i32 %0)
31+
// CHECK: ret i32 %1
32+
// CHECK: }
33+
34+
// CHECK: attributes [[ATTR]] = {{{.*}}"frame-pointer"="all"
35+
36+
// CHECK-ALL: define{{.*}} swiftcc i32 @leaf_function_no_frame_pointer(i32 %0) [[ATTR:#.*]] {
37+
// CHECK-ALL: entry:
38+
// CHECK-ALL: ret i32 %0
39+
// CHECK-ALL: }
40+
41+
// CHECK-ALL: define{{.*}} swiftcc i32 @non_leaf_function_with_frame_pointer(i32 %0) [[ATTR]] {
42+
// CHECK-ALL: entry:
43+
// CHECK-ALL: %1 = call swiftcc i32 @leaf_function_no_frame_pointer(i32 %0)
44+
// CHECK-ALL: ret i32 %1
45+
// CHECK-ALL: }
46+
47+
// CHECK-ALL: attributes [[ATTR]] = {{{.*}}"frame-pointer"="all"
48+
49+
// Silence other os-archs.
50+
// CHECKASM: {{.*}}
51+
52+
// CHECKASM-macosx-x86_64-LABEL: _leaf_function_no_frame_pointer:
53+
// CHECKASM-macosx-x86_64: push
54+
// CHECKASM-macosx-x86_64: movl %edi, %eax
55+
// CHECKASM-macosx-x86_64: pop
56+
// CHECKASM-macosx-x86_64: ret
57+
58+
59+
// CHECKASM-macosx-x86_64-LABEL: _non_leaf_function_with_frame_pointer:
60+
// CHECKASM-macosx-x86_64: pushq %rbp
61+
// CHECKASM-macosx-x86_64: movq %rsp, %rbp
62+
// CHECKASM-macosx-x86_64: callq _leaf_function_no_frame_pointer
63+
// CHECKASM-macosx-x86_64: popq %rbp
64+
// CHECKASM-macosx-x86_64: ret

test/IRGen/framepointer_arm64.sil

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// RUN: %target-swift-frontend -target arm64-apple-ios8.0 -primary-file %s -emit-ir | %FileCheck %s --check-prefix=CHECK
2+
// 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
3+
// RUN: %target-swift-frontend -target arm64-apple-ios8.0 -primary-file %s -S | %FileCheck %s --check-prefix=CHECKASM
4+
// RUN: %target-swift-frontend -target arm64-apple-ios8.0 -primary-file %s -S -no-omit-leaf-frame-pointer | %FileCheck %s --check-prefix=CHECKASM-ALL
5+
6+
// REQUIRES: CODEGENERATOR=AArch64
7+
8+
// UNSUPPORTED: OS=linux-gnu
9+
// UNSUPPORTED: OS=windows
10+
11+
sil_stage canonical
12+
13+
import Swift
14+
15+
sil @leaf_function_no_frame_pointer : $@convention(thin) (Int32) -> Int32 {
16+
entry(%i : $Int32):
17+
return %i : $Int32
18+
}
19+
20+
sil @non_leaf_function_with_frame_pointer : $@convention(thin) (Int32) -> Int32 {
21+
entry(%i : $Int32):
22+
%f = function_ref @leaf_function_no_frame_pointer : $@convention(thin) (Int32) -> Int32
23+
%r = apply %f(%i) : $@convention(thin) (Int32) -> Int32
24+
return %r : $Int32
25+
}
26+
27+
// CHECK: define{{.*}} swiftcc i32 @leaf_function_no_frame_pointer(i32 %0) [[ATTR:#.*]] {
28+
// CHECK: entry:
29+
// CHECK: ret i32 %0
30+
// CHECK: }
31+
32+
// CHECK: define{{.*}} swiftcc i32 @non_leaf_function_with_frame_pointer(i32 %0) [[ATTR]] {
33+
// CHECK: entry:
34+
// CHECK: %1 = call swiftcc i32 @leaf_function_no_frame_pointer(i32 %0)
35+
// CHECK: ret i32 %1
36+
// CHECK: }
37+
38+
// CHECK: attributes [[ATTR]] = {{{.*}}"frame-pointer"="non-leaf"
39+
40+
// CHECK-ALL: define{{.*}} swiftcc i32 @leaf_function_no_frame_pointer(i32 %0) [[ATTR:#.*]] {
41+
// CHECK-ALL: entry:
42+
// CHECK-ALL: ret i32 %0
43+
// CHECK-ALL: }
44+
45+
// CHECK-ALL: define{{.*}} swiftcc i32 @non_leaf_function_with_frame_pointer(i32 %0) [[ATTR]] {
46+
// CHECK-ALL: entry:
47+
// CHECK-ALL: %1 = call swiftcc i32 @leaf_function_no_frame_pointer(i32 %0)
48+
// CHECK-ALL: ret i32 %1
49+
// CHECK-ALL: }
50+
51+
// CHECK-ALL: attributes [[ATTR]] = {{{.*}}"frame-pointer"="all"
52+
53+
// CHECKASM-LABEL: _leaf_function_no_frame_pointer:
54+
// CHECKASM-NOT: stp
55+
// CHECKASM-NOT: ldp
56+
// CHECKASM: ret
57+
58+
// CHECKASM-LABEL: _non_leaf_function_with_frame_pointer:
59+
// CHECKASM: stp
60+
// CHECKASM: _leaf_function_no_frame_pointer
61+
// CHECKASM: ldp
62+
// CHECKASM: ret
63+
64+
// CHECKASM-ALL-LABEL: _leaf_function_no_frame_pointer:
65+
// CHECKASM-ALL: stp
66+
// CHECKASM-ALL: ldp
67+
// CHECKASM-ALL: ret
68+
69+
// CHECKASM-ALL-LABEL: _non_leaf_function_with_frame_pointer:
70+
// CHECKASM-ALL: stp
71+
// CHECKASM-ALL: _leaf_function_no_frame_pointer
72+
// CHECKASM-ALL: ldp
73+
// CHECKASM-ALL: ret

test/Misc/tbi.sil

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,13 @@
1919

2020
// NO_TBI-LABEL: .globl _testTBI
2121
// NO_TBI: _testTBI
22-
// NO_TBI-NEXT: stp
23-
// NO_TBI-NEXT: mov
2422
// NO_TBI-NEXT: and
2523
// NO_TBI-NEXT: ldr
26-
// NO_TBI-NEXT: ldp
2724
// NO_TBI-NEXT: ret
2825

2926
// TBI-LABEL: .globl _testTBI
3027
// TBI: _testTBI:
31-
// TBI-NEXT: stp
32-
// TBI-NEXT: mov
3328
// TBI-NEXT: ldr
34-
// TBI-NEXT: ldp
3529
// TBI-NEXT: ret
3630

3731
sil_stage canonical

0 commit comments

Comments
 (0)