diff --git a/include/swift/AST/IRGenOptions.h b/include/swift/AST/IRGenOptions.h index 813bae2befc0c..1fddbbcedaaec 100644 --- a/include/swift/AST/IRGenOptions.h +++ b/include/swift/AST/IRGenOptions.h @@ -484,6 +484,8 @@ class IRGenOptions { // (LLVM's 'frame-pointer=all'). unsigned AsyncFramePointerAll : 1; + unsigned UseProfilingMarkerThunks : 1; + /// The number of threads for multi-threaded code generation. unsigned NumThreads = 0; @@ -574,7 +576,7 @@ class IRGenOptions { ColocateTypeDescriptors(true), UseRelativeProtocolWitnessTables(false), UseFragileResilientProtocolWitnesses(false), EnableHotColdSplit(false), EmitAsyncFramePushPopMetadata(false), - AsyncFramePointerAll(false), + AsyncFramePointerAll(false), UseProfilingMarkerThunks(false), CmdArgs(), SanitizeCoverage(llvm::SanitizerCoverageOptions()), TypeInfoFilter(TypeInfoDumpFilter::All), PlatformCCallingConvention(llvm::CallingConv::C), UseCASBackend(false), diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 7d86f2c1fd582..50ebd98a38b4e 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -1349,6 +1349,13 @@ def disable_new_llvm_pass_manager : Flag<["-"], "disable-new-llvm-pass-manager">, HelpText<"Disable the new llvm pass manager">; +def enable_profiling_marker_thunks : + Flag<["-"], "enable-profiling-marker-thunks">, + HelpText<"Enable profiling marker thunks">; +def disable_profiling_marker_thunks : + Flag<["-"], "disable-profiling-marker-thunks">, + HelpText<"Disable profiling marker thunks">; + def enable_objective_c_protocol_symbolic_references : Flag<["-"], "enable-objective-c-protocol-symbolic-references">, HelpText<"Enable objective-c protocol symbolic references">; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 3104bd5cc441c..376ce16f38ad9 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -3469,6 +3469,10 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, Args.hasFlag(OPT_enable_fragile_resilient_protocol_witnesses, OPT_disable_fragile_resilient_protocol_witnesses, Opts.UseFragileResilientProtocolWitnesses); + Opts.UseProfilingMarkerThunks = + Args.hasFlag(OPT_enable_profiling_marker_thunks, + OPT_disable_profiling_marker_thunks, + Opts.UseProfilingMarkerThunks); Opts.EnableHotColdSplit = Args.hasFlag(OPT_enable_split_cold_code, OPT_disable_split_cold_code, diff --git a/lib/IRGen/CallEmission.h b/lib/IRGen/CallEmission.h index 5f189f4c4c705..51a8205c7bce4 100644 --- a/lib/IRGen/CallEmission.h +++ b/lib/IRGen/CallEmission.h @@ -77,6 +77,8 @@ class CallEmission { /// RemainingArgsForCallee, at least between calls. bool EmittedCall; + bool UseProfilingThunk = false; + /// The basic block to which the call to a potentially throwing foreign /// function should jump to continue normal execution of the program. llvm::BasicBlock *invokeNormalDest = nullptr; @@ -123,6 +125,10 @@ class CallEmission { return CurCallee.getSubstitutions(); } + void useProfilingThunk() { + UseProfilingThunk = true; + } + virtual void begin(); virtual void end(); virtual SILType getParameterType(unsigned index) = 0; diff --git a/lib/IRGen/Callee.h b/lib/IRGen/Callee.h index 66199d8819f6e..991bbe1f0f48a 100644 --- a/lib/IRGen/Callee.h +++ b/lib/IRGen/Callee.h @@ -374,6 +374,15 @@ namespace irgen { } public: + + FunctionPointer withProfilingThunk(llvm::Function *thunk) const { + auto res = FunctionPointer(kind, thunk, nullptr/*secondaryValue*/, + AuthInfo, Sig); + res.useSignature = useSignature; + return res; + } + + FunctionPointer() : kind(FunctionPointer::Kind::Function), Value(nullptr), SecondaryValue(nullptr) {} diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 660e8d22f9f33..e877f01f14304 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -52,6 +52,7 @@ #include "GenType.h" #include "IRGenFunction.h" #include "IRGenModule.h" +#include "IRGenMangler.h" #include "LoadableTypeInfo.h" #include "NativeConventionSchema.h" #include "Signature.h" @@ -3189,6 +3190,27 @@ void CallEmission::emitToUnmappedMemory(Address result) { } } } +static FunctionPointer getProfilingFuncFor(IRGenFunction &IGF, + FunctionPointer fnToCall, + Callee &callee) { + auto genericFn = cast(fnToCall.getRawPointer()); + auto replacementTypes = callee.getSubstitutions().getReplacementTypes(); + llvm::SmallString<64> name; + { + llvm::raw_svector_ostream os(name); + os << "__swift_prof_thunk__generic_func__"; + os << replacementTypes.size(); + os << "__"; + for (auto replTy : replacementTypes) { + IRGenMangler mangler; + os << mangler.mangleTypeMetadataFull(replTy->getCanonicalType()); + os << "___"; + } + os << "fun__"; + } + auto *thunk = IGF.IGM.getOrCreateProfilingThunk(genericFn, name); + return fnToCall.withProfilingThunk(thunk); +} /// The private routine to ultimately emit a call or invoke instruction. llvm::CallBase *CallEmission::emitCallSite() { @@ -3219,7 +3241,14 @@ llvm::CallBase *CallEmission::emitCallSite() { } else IGF.setCallsThunksWithForeignExceptionTraps(); } - auto call = createCall(fn, Args); + + auto fnToCall = fn; + if (UseProfilingThunk) { + assert(fnToCall.isConstant() && "Non constant function in profiling thunk"); + fnToCall = getProfilingFuncFor(IGF, fnToCall, CurCallee); + } + + auto call = createCall(fnToCall, Args); if (invokeNormalDest) IGF.Builder.emitBlock(invokeNormalDest); diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 6d5725f88e3e1..bd481a4b6710c 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -6278,3 +6278,62 @@ void IRGenModule::setColocateMetadataSection(llvm::Function *f) { break; } } + +llvm::Function *IRGenModule::getOrCreateProfilingThunk( + llvm::Function *f, + StringRef prefix) { + + llvm::SmallString<32> name; + { + llvm::raw_svector_ostream os(name); + os << prefix; + os << f->getName(); + } + + auto thunk = cast( + getOrCreateHelperFunction(name, f->getReturnType(), + f->getFunctionType()->params(), + [&](IRGenFunction &IGF) { + Explosion args = IGF.collectParameters(); + auto res = IGF.Builder.CreateCall(f->getFunctionType(), f, args.getAll()); + res->setAttributes(f->getAttributes()); + (void)args.claimAll(); + if (res->getType()->isVoidTy()) { + IGF.Builder.CreateRetVoid(); + } else { + IGF.Builder.CreateRet(res); + } + }, /*isNoInline*/ true)); + + thunk->setAttributes(f->getAttributes()); + thunk->setCallingConv(f->getCallingConv()); + thunk->setDLLStorageClass(f->getDLLStorageClass()); + if (f->getComdat()) + thunk->setComdat(f->getParent()->getOrInsertComdat(thunk->getName())); + setMustHaveFramePointer(thunk); + thunk->addFnAttr(llvm::Attribute::NoInline); + + return cast(thunk); +} + +llvm::Function* +IRGenModule::getAddrOfWitnessTableProfilingThunk( + llvm::Function *witness, + const NormalProtocolConformance &conformance) { + + assert( + conformance.getDeclContext()->getSelfNominalTypeDecl()->isGenericContext()); + + return getOrCreateProfilingThunk(witness, + "__swift_prof_thunk__generic_witness__"); +} + +llvm::Function * +IRGenModule::getAddrOfVTableProfilingThunk( + llvm::Function *vTableFun, ClassDecl *classDecl) { + + assert(classDecl->getSelfNominalTypeDecl()->isGenericContext()); + + return getOrCreateProfilingThunk(vTableFun, + "__swift_prof_thunk__generic_vtable__"); +} diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index b9af26e3425d8..e951337ba6ff6 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -287,7 +287,8 @@ static Flags getMethodDescriptorFlags(ValueDecl *fn) { static void buildMethodDescriptorFields(IRGenModule &IGM, const SILVTable *VTable, SILDeclRef fn, - ConstantStructBuilder &descriptor) { + ConstantStructBuilder &descriptor, + ClassDecl *classDecl) { auto *func = cast(fn.getDecl()); // Classify the method. using Flags = MethodDescriptorFlags; @@ -318,6 +319,13 @@ static void buildMethodDescriptorFields(IRGenModule &IGM, descriptor.addRelativeAddress(implFn); } else { llvm::Function *implFn = IGM.getAddrOfSILFunction(impl, NotForDefinition); + + if (IGM.getOptions().UseProfilingMarkerThunks && + classDecl->getSelfNominalTypeDecl()->isGenericContext() && + !impl->getLoweredFunctionType()->isCoroutine()) { + implFn = IGM.getAddrOfVTableProfilingThunk(implFn, classDecl); + } + descriptor.addCompactFunctionReference(implFn); } } else { @@ -328,7 +336,8 @@ static void buildMethodDescriptorFields(IRGenModule &IGM, } void IRGenModule::emitNonoverriddenMethodDescriptor(const SILVTable *VTable, - SILDeclRef declRef) { + SILDeclRef declRef, + ClassDecl *classDecl) { auto entity = LinkEntity::forMethodDescriptor(declRef); auto *var = cast( getAddrOfLLVMVariable(entity, ConstantInit(), DebugTypeInfo())); @@ -343,7 +352,7 @@ void IRGenModule::emitNonoverriddenMethodDescriptor(const SILVTable *VTable, ConstantInitBuilder ib(*this); ConstantStructBuilder sb(ib.beginStruct(MethodDescriptorStructTy)); - buildMethodDescriptorFields(*this, VTable, declRef, sb); + buildMethodDescriptorFields(*this, VTable, declRef, sb, classDecl); auto init = sb.finishAndCreateFuture(); @@ -2081,7 +2090,7 @@ namespace { // Actually build the descriptor. auto descriptor = B.beginStruct(IGM.MethodDescriptorStructTy); - buildMethodDescriptorFields(IGM, VTable, fn, descriptor); + buildMethodDescriptorFields(IGM, VTable, fn, descriptor, getType()); descriptor.finishAndAddTo(B); // Emit method dispatch thunk if the class is resilient. @@ -2129,7 +2138,7 @@ namespace { // exist in the table in the class's context descriptor since it isn't // in the vtable, but external clients need to be able to link against the // symbol. - IGM.emitNonoverriddenMethodDescriptor(VTable, fn); + IGM.emitNonoverriddenMethodDescriptor(VTable, fn, getType()); } void addOverrideTable() { diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index d56b625422572..fbc4c1619e3cd 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -1631,7 +1631,17 @@ class AccessorConformanceInfo : public ConformanceInfo { if (Func->isAsync()) { witness = IGM.getAddrOfAsyncFunctionPointer(Func); } else { - witness = IGM.getAddrOfSILFunction(Func, NotForDefinition); + + auto *conformance = dyn_cast(&Conformance); + auto f = IGM.getAddrOfSILFunction(Func, NotForDefinition); + if (IGM.getOptions().UseProfilingMarkerThunks && + conformance && + conformance->getDeclContext()-> + getSelfNominalTypeDecl()->isGenericContext() && + !Func->getLoweredFunctionType()->isCoroutine()) + witness = IGM.getAddrOfWitnessTableProfilingThunk(f, *conformance); + else witness = f; + } } else { // The method is removed by dead method elimination. @@ -1994,11 +2004,18 @@ void ResilientWitnessTableBuilder::collectResilientWitnesses( SILFunction *Func = entry.getMethodWitness().Witness; llvm::Constant *witness; + bool isGenericConformance = + conformance.getDeclContext()->getSelfNominalTypeDecl()->isGenericContext(); if (Func) { if (Func->isAsync()) witness = IGM.getAddrOfAsyncFunctionPointer(Func); - else - witness = IGM.getAddrOfSILFunction(Func, NotForDefinition); + else { + auto f = IGM.getAddrOfSILFunction(Func, NotForDefinition); + if (isGenericConformance && IGM.getOptions().UseProfilingMarkerThunks && + !Func->getLoweredFunctionType()->isCoroutine()) + witness = IGM.getAddrOfWitnessTableProfilingThunk(f, conformance); + else witness = f; + } } else { // The method is removed by dead method elimination. // It should be never called. We add a null pointer. diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index 92fcdf9f36445..c4d6ddb7fac14 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -1477,6 +1477,12 @@ void IRGenModule::setHasNoFramePointer(llvm::Function *F) { F->addFnAttrs(b); } +void IRGenModule::setMustHaveFramePointer(llvm::Function *F) { + llvm::AttrBuilder b(getLLVMContext()); + b.addAttribute("frame-pointer", "all"); + F->addFnAttrs(b); +} + /// Construct initial function attributes from options. void IRGenModule::constructInitialFnAttributes( llvm::AttrBuilder &Attrs, OptimizationMode FuncOptMode, diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 751f600e0cf9b..d93c00e0f6cfb 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -1586,6 +1586,7 @@ private: \ StackProtectorMode stackProtect = StackProtectorMode::NoStackProtector); void setHasNoFramePointer(llvm::AttrBuilder &Attrs); void setHasNoFramePointer(llvm::Function *F); + void setMustHaveFramePointer(llvm::Function *F); llvm::AttributeList constructInitialAttributes(); StackProtectorMode shouldEmitStackProtector(SILFunction *f); @@ -1660,7 +1661,8 @@ private: \ llvm::Constant *getAddrOfMethodDescriptor(SILDeclRef declRef, ForDefinition_t forDefinition); void emitNonoverriddenMethodDescriptor(const SILVTable *VTable, - SILDeclRef declRef); + SILDeclRef declRef, + ClassDecl *classDecl); Address getAddrOfEnumCase(EnumElementDecl *Case, ForDefinition_t forDefinition); @@ -1821,6 +1823,16 @@ private: \ bool isDynamicallyReplaceableImplementation = false, bool shouldCallPreviousImplementation = false); + llvm::Function * + getAddrOfWitnessTableProfilingThunk(llvm::Function *witness, + const NormalProtocolConformance &C); + + llvm::Function * + getAddrOfVTableProfilingThunk(llvm::Function *f, ClassDecl *decl); + + llvm::Function *getOrCreateProfilingThunk(llvm::Function *f, + StringRef prefix); + void emitDynamicReplacementOriginalFunctionThunk(SILFunction *f); llvm::Function *getAddrOfContinuationPrototype(CanSILFunctionType fnType); diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 31107b62cfda3..d5d4f74cb863c 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -3819,6 +3819,15 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) { SubstitutionMap subMap = site.getSubstitutionMap(); emitPolymorphicArguments(*this, origCalleeType, subMap, &witnessMetadata, llArgs); + + // We currently only support non-async calls with profiling thunks. + if (IGM.getOptions().UseProfilingMarkerThunks && + isa(site.getCallee()) && + !site.getOrigCalleeType()->isAsync() && + subMap.hasAnySubstitutableParams() && + !subMap.getRecursiveProperties().hasPrimaryArchetype()) { + emission->useProfilingThunk(); + } } if (calleeFP.shouldPassContinuationDirectly()) { diff --git a/test/IRGen/profiling_marker_thunks.swift b/test/IRGen/profiling_marker_thunks.swift new file mode 100644 index 0000000000000..e26670bb935bc --- /dev/null +++ b/test/IRGen/profiling_marker_thunks.swift @@ -0,0 +1,114 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/P.swiftmodule -module-name=P %S/../Inputs/resilient_protocol.swift +// RUN: %target-swift-frontend -module-name A -I %t -enable-profiling-marker-thunks -emit-ir %s | %FileCheck %s +// RUN: %target-swift-frontend -module-name A -I %t -enable-library-evolution -enable-profiling-marker-thunks -emit-ir %s | %FileCheck %s +// RUN: %target-swift-frontend -module-name A -I %t -enable-profiling-marker-thunks -emit-ir %s | %FileCheck %s --check-prefix=ATTRIBUTE +// RUN: %target-swift-frontend -module-name A -I %t -disable-profiling-marker-thunks -emit-ir %s | %FileCheck %s --check-prefix=NOTHUNK +// RUN: %target-swift-frontend -module-name A -I %t -emit-ir %s | %FileCheck %s --check-prefix=NOTHUNK + +// UNSUPPORTED: OS=windows-msvc +// UNSUPPORTED: OS=linux-gnu, OS=linux-android, OS=linux-androideabi + +// NOTHUNK-NOT: __swift_prof_thunk + +import P + +open class GenericClass { + var x: T + public init(_ t: T) { + x = t + } + + public func someMethod() -> Int { + return 20 + } +} + +open class PlainClass { + var x: Int + + public init() { + x = 10 + } + + public func someMethod() -> Int { + return 20 + } +} + +public protocol SomeProto { + associatedtype T + + func retAssoc() -> T + func plain() throws +} + +public struct Conformer : SomeProto { + var x : T + + public init( _ t: T) { + self.x = t + } + + public func retAssoc() -> T { + return x + } + + public func plain() throws { print(x) } +} + +struct SomeGenericThing { + init(_ t: T, _ v: V) {} +} + +public struct ResilientConformer : ResilientBaseProtocol { + var t : T + + public func requirement() -> Int { + return 5 + } +} + +public func generic(_ t: T) { + print(t) +} +public func test() { + generic(SomeGenericThing(1, 3.0)) +} + +// CHECK: @"$s1A9ConformerVyxGAA9SomeProtoAAWp" = {{.*}} @"__swift_prof_thunk__generic_witness__$s1A9ConformerVyxGAA9SomeProtoA2aEP8retAssoc1TQzyFTW" +// CHECK-SAME: @"__swift_prof_thunk__generic_witness__$s1A9ConformerVyxGAA9SomeProtoA2aEP5plainyyKFTW" + +// CHECK: @"$s1A18ResilientConformerVyxG1P0A12BaseProtocolAAMc" = constant {{.*}} @"__swift_prof_thunk__generic_witness__$s1A18ResilientConformerVyxG1P0A12BaseProtocolAaeFP11requirementSiyFTW" + +// CHECK: @"$s1A12GenericClassCMn" = constant {{.*}} @"__swift_prof_thunk__generic_vtable__$s1A12GenericClassC1xxvg" +// CHECK-SAME: @"__swift_prof_thunk__generic_vtable__$s1A12GenericClassC10someMethodSiyF" + +// CHECK: @"$s1A10PlainClassCMn" = {{.*}}@"$s1A10PlainClassC10someMethodSiyF" + +// CHECK: define swiftcc void @"$s1A4testyyF"() +// CHECK: call swiftcc void @"__swift_prof_thunk__generic_func__2__$sSiN___$sSdN___fun__$s1A16SomeGenericThingVyACyxq_Gx_q_tcfC" +// CHECK: call swiftcc void @"__swift_prof_thunk__generic_func__1__$s1A16SomeGenericThingVySiSdGN___fun__$s1A7genericyyxlF" +// CHECK: ret void + +// CHECK: define {{.*}} swiftcc void @"__swift_prof_thunk__generic_func__1__$s1A16SomeGenericThingVySiSdGN___fun__$s1A7genericyyxlF"(ptr noalias %0, ptr %1) #[[ATTR:[0-9]+]] +// CHECK: call swiftcc void @"$s1A7genericyyxlF"(ptr noalias %0, ptr %1) +// CHECK: ret + +// CHECK: define {{.*}} void @"__swift_prof_thunk__generic_witness__$s1A9ConformerVyxGAA9SomeProtoA2aEP5plainyyKFTW"({{.*}}) #[[ATTR]] { +// CHECK: define {{.*}} void @"__swift_prof_thunk__generic_vtable__$s1A12GenericClassC1xxvg"({{.*}}) #[[ATTR]] { + +// CHECK: attributes #[[ATTR]] = { noinline "frame-pointer"="all" + + +// ATTRIBUTE: define {{.*}} ptr @"__swift_prof_thunk__generic_func__1__$sypN___fun__$ss27_finalizeUninitializedArrayySayxGABnlF"({{.*}}) #[[ATTR:[0-9]+]] +// ATTRIBUTE: define {{.*}} void @"__swift_prof_thunk__generic_func__2__$sSiN___$sSdN___fun__$s1A16SomeGenericThingVyACyxq_Gx_q_tcfC"({{.*}}) #[[ATTR]] +// ATTRIBUTE: define {{.*}} void @"__swift_prof_thunk__generic_func__1__$s1A16SomeGenericThingVySiSdGN___fun__$s1A7genericyyxlF"({{.*}}) #[[ATTR]] +// ATTRIBUTE: define {{.*}} void @"__swift_prof_thunk__generic_witness__$s1A9ConformerVyxGAA9SomeProtoA2aEP8retAssoc1TQzyFTW"({{.*}}) #[[ATTR]] +// ATTRIBUTE: define {{.*}} void @"__swift_prof_thunk__generic_witness__$s1A9ConformerVyxGAA9SomeProtoA2aEP5plainyyKFTW"({{.*}}) #[[ATTR]] +// ATTRIBUTE: define {{.*}} i64 @"__swift_prof_thunk__generic_witness__$s1A18ResilientConformerVyxG1P0A12BaseProtocolAaeFP11requirementSiyFTW"({{.*}}) #[[ATTR]] +// ATTRIBUTE: define {{.*}} void @"__swift_prof_thunk__generic_vtable__$s1A12GenericClassC1xxvg"({{.*}}) #[[ATTR]] +// ATTRIBUTE: define {{.*}} void @"__swift_prof_thunk__generic_vtable__$s1A12GenericClassC1xxvs"({{.*}}) #[[ATTR]] +// ATTRIBUTE: define {{.*}} ptr @"__swift_prof_thunk__generic_vtable__$s1A12GenericClassCyACyxGxcfC"({{.*}}) #[[ATTR]] +// ATTRIBUTE: define {{.*}} i64 @"__swift_prof_thunk__generic_vtable__$s1A12GenericClassC10someMethodSiyF"({{.*}}) #[[ATTR]] +// ATTRIBUTE: #[[ATTR]] = { noinline "frame-pointer"="all"