diff --git a/include/swift/AST/IRGenOptions.h b/include/swift/AST/IRGenOptions.h index 1a90d3572ba82..52ffcc34aca36 100644 --- a/include/swift/AST/IRGenOptions.h +++ b/include/swift/AST/IRGenOptions.h @@ -191,6 +191,7 @@ class IRGenOptions { unsigned DisableLLVMSLPVectorizer : 1; /// Disable frame pointer elimination? + unsigned DisableFPElimLeaf : 1; unsigned DisableFPElim : 1; /// Special codegen for playgrounds. @@ -319,6 +320,7 @@ class IRGenOptions { DisableClangModuleSkeletonCUs(false), UseJIT(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), diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index a430124cc7f27..8a9971367dd69 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -1010,6 +1010,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<"">, HelpText<"Turn on runtime checks for erroneous behavior.">; diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 89d2efb1771d4..95fc7f52effb3 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -287,6 +287,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); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index b1e9916376c5c..9db77ec33b219 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1511,6 +1511,9 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, getRuntimeCompatVersion(); } + if (Args.hasArg(OPT_disable_leaf_frame_pointer_elim)) + Opts.DisableFPElimLeaf = true; + return false; } diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 7eca86628b80c..f982a848754de 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -177,7 +177,7 @@ static void emitMetadataCompletionFunction(IRGenModule &IGM, IGM.getAddrOfTypeMetadataCompletionFunction(typeDecl, ForDefinition); f->setAttributes(IGM.constructInitialAttributes()); f->setDoesNotThrow(); - IGM.setHasFramePointer(f, false); + IGM.setHasNoFramePointer(f); IRGenFunction IGF(IGM, f); @@ -2234,7 +2234,7 @@ namespace { IGM.getAddrOfTypeMetadataInstantiationFunction(Target, ForDefinition); f->setAttributes(IGM.constructInitialAttributes()); f->setDoesNotThrow(); - IGM.setHasFramePointer(f, false); + IGM.setHasNoFramePointer(f); IRGenFunction IGF(IGM, f); diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index ae6e55167360a..43af31883550a 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -90,6 +90,20 @@ static llvm::PointerType *createStructPointerType(IRGenModule &IGM, return createStructType(IGM, name, types)->getPointerTo(DefaultAS); }; +static clang::CodeGenOptions::FramePointerKind +shouldUseFramePointer(const IRGenOptions &Opts) { + if (Opts.DisableFPElim) { + // General frame pointer elimination is disabled. + // Should we at least eliminate in leaf functions? + return Opts.DisableFPElimLeaf + ? clang::CodeGenOptions::FramePointerKind::All + : clang::CodeGenOptions::FramePointerKind::NonLeaf; + } + + return clang::CodeGenOptions::FramePointerKind::None; + +} + static clang::CodeGenerator *createClangCodeGenerator(ASTContext &Context, llvm::LLVMContext &LLVMContext, const IRGenOptions &Opts, @@ -102,9 +116,7 @@ static clang::CodeGenerator *createClangCodeGenerator(ASTContext &Context, 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)); CGO.DiscardValueNames = !Opts.shouldProvideValueNames(); switch (Opts.DebugInfoLevel) { case IRGenDebugInfoLevel::None: @@ -986,15 +998,13 @@ bool swift::irgen::shouldRemoveTargetFeature(StringRef feature) { return feature == "+thumb-mode"; } -void IRGenModule::setHasFramePointer(llvm::AttrBuilder &Attrs, - bool HasFramePointer) { - Attrs.addAttribute("frame-pointer", HasFramePointer ? "all" : "none"); +void IRGenModule::setHasNoFramePointer(llvm::AttrBuilder &Attrs) { + Attrs.addAttribute("frame-pointer", "none"); } -void IRGenModule::setHasFramePointer(llvm::Function *F, - bool HasFramePointer) { +void IRGenModule::setHasNoFramePointer(llvm::Function *F) { llvm::AttrBuilder b; - setHasFramePointer(b, HasFramePointer); + setHasNoFramePointer(b); F->addAttributes(llvm::AttributeList::FunctionIndex, b); } @@ -1004,10 +1014,6 @@ void IRGenModule::constructInitialFnAttributes(llvm::AttrBuilder &Attrs, // Add the default attributes for the Clang configuration. clang::CodeGen::addDefaultFunctionDefinitionAttributes(getClangCGM(), Attrs); - // Add frame pointer attributes. - // FIXME: why are we doing this? - setHasFramePointer(Attrs, IRGen.Opts.DisableFPElim); - // Add/remove MinSize based on the appropriate setting. if (FuncOptMode == OptimizationMode::NotSet) FuncOptMode = IRGen.Opts.OptMode; diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index a04212ba9b958..37e6e734ed175 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -1305,8 +1305,8 @@ private: \ void constructInitialFnAttributes(llvm::AttrBuilder &Attrs, OptimizationMode FuncOptMode = OptimizationMode::NotSet); - void setHasFramePointer(llvm::AttrBuilder &Attrs, bool HasFP); - void setHasFramePointer(llvm::Function *F, bool HasFP); + void setHasNoFramePointer(llvm::AttrBuilder &Attrs); + void setHasNoFramePointer(llvm::Function *F); llvm::AttributeList constructInitialAttributes(); void emitProtocolDecl(ProtocolDecl *D); diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 5ab92e9dbd286..168df201611b4 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -1608,7 +1608,7 @@ void irgen::emitCacheAccessFunction(IRGenModule &IGM, accessor->addAttribute(llvm::AttributeList::FunctionIndex, llvm::Attribute::NoInline); // Accessor functions don't need frame pointers. - IGM.setHasFramePointer(accessor, false); + IGM.setHasNoFramePointer(accessor); // This function is logically 'readnone': the caller does not need // to reason about any side effects or stores it might perform. @@ -1990,8 +1990,8 @@ MetadataResponse irgen::emitGenericTypeMetadataAccessFunction( thunkFn->setCallingConv(IGM.SwiftCC); thunkFn->addAttribute(llvm::AttributeList::FunctionIndex, llvm::Attribute::NoInline); - IGM.setHasFramePointer(thunkFn, false); - + IGM.setHasNoFramePointer(thunkFn); + [&IGM, thunkFn]{ IRGenFunction subIGF(IGM, thunkFn); @@ -2462,7 +2462,7 @@ emitMetadataAccessByMangledName(IRGenFunction &IGF, CanType type, instantiationFn->setDoesNotThrow(); instantiationFn->addAttribute(llvm::AttributeList::FunctionIndex, llvm::Attribute::NoInline); - IGM.setHasFramePointer(instantiationFn, false); + IGM.setHasNoFramePointer(instantiationFn); [&IGM, instantiationFn, request]{ IRGenFunction subIGF(IGM, instantiationFn); diff --git a/test/IRGen/c_globals.swift b/test/IRGen/c_globals.swift index 0f55f9bb4d173..81245bdd129b1 100644 --- a/test/IRGen/c_globals.swift +++ b/test/IRGen/c_globals.swift @@ -31,5 +31,5 @@ public func testCaptureGlobal() { }) // CHECK: {{^}$}} } -// 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 [[CLANG_FUNC_ATTR]] = { noinline nounwind {{.*}}"frame-pointer"="non-leaf"{{.*}} +// CHECK-DAG: attributes [[SWIFT_FUNC_ATTR]] = { {{.*}}"frame-pointer"="non-leaf" {{.*}}"target-cpu" diff --git a/test/IRGen/framepointer.sil b/test/IRGen/framepointer.sil new file mode 100644 index 0000000000000..7443f4022b5e4 --- /dev/null +++ b/test/IRGen/framepointer.sil @@ -0,0 +1,62 @@ +// 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 + +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" + +// Silence other os-archs. +// CHECKASM: {{.*}} + +// CHECKASM-macosx-x86_64-LABEL: _leaf_function_no_frame_pointer: +// CHECKASM-macosx-x86_64-NOT: push +// CHECKASM-macosx-x86_64: movl %edi, %eax +// CHECKASM-macosx-x86_64-NOT: 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 diff --git a/test/Misc/tbi.sil b/test/Misc/tbi.sil index 0fd2274c7ff40..f22555d8c0aad 100644 --- a/test/Misc/tbi.sil +++ b/test/Misc/tbi.sil @@ -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