Skip to content

Commit 4e931e7

Browse files
Merge pull request #76849 from aschwaighofer/prof_thunks
IRGen: Add the ability to mark certain generic entry points in back traces
2 parents 04d055a + 990fd3f commit 4e931e7

File tree

13 files changed

+294
-11
lines changed

13 files changed

+294
-11
lines changed

include/swift/AST/IRGenOptions.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,8 @@ class IRGenOptions {
492492
// (LLVM's 'frame-pointer=all').
493493
unsigned AsyncFramePointerAll : 1;
494494

495+
unsigned UseProfilingMarkerThunks : 1;
496+
495497
/// The number of threads for multi-threaded code generation.
496498
unsigned NumThreads = 0;
497499

@@ -582,7 +584,7 @@ class IRGenOptions {
582584
ColocateTypeDescriptors(true), UseRelativeProtocolWitnessTables(false),
583585
UseFragileResilientProtocolWitnesses(false), EnableHotColdSplit(false),
584586
EmitAsyncFramePushPopMetadata(false), EmitYieldOnce2AsYieldOnce(true),
585-
AsyncFramePointerAll(false), CmdArgs(),
587+
UseProfilingMarkerThunks(false), AsyncFramePointerAll(false), CmdArgs(),
586588
SanitizeCoverage(llvm::SanitizerCoverageOptions()),
587589
TypeInfoFilter(TypeInfoDumpFilter::All),
588590
PlatformCCallingConvention(llvm::CallingConv::C), UseCASBackend(false),

include/swift/Option/FrontendOptions.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,6 +1349,13 @@ def disable_new_llvm_pass_manager :
13491349
Flag<["-"], "disable-new-llvm-pass-manager">,
13501350
HelpText<"Disable the new llvm pass manager">;
13511351

1352+
def enable_profiling_marker_thunks :
1353+
Flag<["-"], "enable-profiling-marker-thunks">,
1354+
HelpText<"Enable profiling marker thunks">;
1355+
def disable_profiling_marker_thunks :
1356+
Flag<["-"], "disable-profiling-marker-thunks">,
1357+
HelpText<"Disable profiling marker thunks">;
1358+
13521359
def enable_objective_c_protocol_symbolic_references :
13531360
Flag<["-"], "enable-objective-c-protocol-symbolic-references">,
13541361
HelpText<"Enable objective-c protocol symbolic references">;

lib/Frontend/CompilerInvocation.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3473,6 +3473,10 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
34733473
Args.hasFlag(OPT_enable_fragile_resilient_protocol_witnesses,
34743474
OPT_disable_fragile_resilient_protocol_witnesses,
34753475
Opts.UseFragileResilientProtocolWitnesses);
3476+
Opts.UseProfilingMarkerThunks =
3477+
Args.hasFlag(OPT_enable_profiling_marker_thunks,
3478+
OPT_disable_profiling_marker_thunks,
3479+
Opts.UseProfilingMarkerThunks);
34763480
Opts.EmitYieldOnce2AsYieldOnce =
34773481
!LangOpts.hasFeature(Feature::CoroutineAccessorsAllocateInCallee);
34783482
Opts.EnableHotColdSplit =

lib/IRGen/CallEmission.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ class CallEmission {
7777
/// RemainingArgsForCallee, at least between calls.
7878
bool EmittedCall;
7979

80+
bool UseProfilingThunk = false;
81+
8082
/// The basic block to which the call to a potentially throwing foreign
8183
/// function should jump to continue normal execution of the program.
8284
llvm::BasicBlock *invokeNormalDest = nullptr;
@@ -123,6 +125,10 @@ class CallEmission {
123125
return CurCallee.getSubstitutions();
124126
}
125127

128+
void useProfilingThunk() {
129+
UseProfilingThunk = true;
130+
}
131+
126132
virtual void begin();
127133
virtual void end();
128134
virtual SILType getParameterType(unsigned index) = 0;

lib/IRGen/Callee.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,15 @@ namespace irgen {
374374
}
375375

376376
public:
377+
378+
FunctionPointer withProfilingThunk(llvm::Function *thunk) const {
379+
auto res = FunctionPointer(kind, thunk, nullptr/*secondaryValue*/,
380+
AuthInfo, Sig);
381+
res.useSignature = useSignature;
382+
return res;
383+
}
384+
385+
377386
FunctionPointer()
378387
: kind(FunctionPointer::Kind::Function), Value(nullptr),
379388
SecondaryValue(nullptr) {}

lib/IRGen/GenCall.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
#include "GenType.h"
5353
#include "IRGenFunction.h"
5454
#include "IRGenModule.h"
55+
#include "IRGenMangler.h"
5556
#include "LoadableTypeInfo.h"
5657
#include "NativeConventionSchema.h"
5758
#include "Signature.h"
@@ -3211,6 +3212,27 @@ void CallEmission::emitToUnmappedMemory(Address result) {
32113212
}
32123213
}
32133214
}
3215+
static FunctionPointer getProfilingFuncFor(IRGenFunction &IGF,
3216+
FunctionPointer fnToCall,
3217+
Callee &callee) {
3218+
auto genericFn = cast<llvm::Function>(fnToCall.getRawPointer());
3219+
auto replacementTypes = callee.getSubstitutions().getReplacementTypes();
3220+
llvm::SmallString<64> name;
3221+
{
3222+
llvm::raw_svector_ostream os(name);
3223+
os << "__swift_prof_thunk__generic_func__";
3224+
os << replacementTypes.size();
3225+
os << "__";
3226+
for (auto replTy : replacementTypes) {
3227+
IRGenMangler mangler;
3228+
os << mangler.mangleTypeMetadataFull(replTy->getCanonicalType());
3229+
os << "___";
3230+
}
3231+
os << "fun__";
3232+
}
3233+
auto *thunk = IGF.IGM.getOrCreateProfilingThunk(genericFn, name);
3234+
return fnToCall.withProfilingThunk(thunk);
3235+
}
32143236

32153237
/// The private routine to ultimately emit a call or invoke instruction.
32163238
llvm::CallBase *CallEmission::emitCallSite() {
@@ -3241,7 +3263,14 @@ llvm::CallBase *CallEmission::emitCallSite() {
32413263
} else
32423264
IGF.setCallsThunksWithForeignExceptionTraps();
32433265
}
3244-
auto call = createCall(fn, Args);
3266+
3267+
auto fnToCall = fn;
3268+
if (UseProfilingThunk) {
3269+
assert(fnToCall.isConstant() && "Non constant function in profiling thunk");
3270+
fnToCall = getProfilingFuncFor(IGF, fnToCall, CurCallee);
3271+
}
3272+
3273+
auto call = createCall(fnToCall, Args);
32453274
if (invokeNormalDest)
32463275
IGF.Builder.emitBlock(invokeNormalDest);
32473276

lib/IRGen/GenDecl.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6278,3 +6278,62 @@ void IRGenModule::setColocateMetadataSection(llvm::Function *f) {
62786278
break;
62796279
}
62806280
}
6281+
6282+
llvm::Function *IRGenModule::getOrCreateProfilingThunk(
6283+
llvm::Function *f,
6284+
StringRef prefix) {
6285+
6286+
llvm::SmallString<32> name;
6287+
{
6288+
llvm::raw_svector_ostream os(name);
6289+
os << prefix;
6290+
os << f->getName();
6291+
}
6292+
6293+
auto thunk = cast<llvm::Function>(
6294+
getOrCreateHelperFunction(name, f->getReturnType(),
6295+
f->getFunctionType()->params(),
6296+
[&](IRGenFunction &IGF) {
6297+
Explosion args = IGF.collectParameters();
6298+
auto res = IGF.Builder.CreateCall(f->getFunctionType(), f, args.getAll());
6299+
res->setAttributes(f->getAttributes());
6300+
(void)args.claimAll();
6301+
if (res->getType()->isVoidTy()) {
6302+
IGF.Builder.CreateRetVoid();
6303+
} else {
6304+
IGF.Builder.CreateRet(res);
6305+
}
6306+
}, /*isNoInline*/ true));
6307+
6308+
thunk->setAttributes(f->getAttributes());
6309+
thunk->setCallingConv(f->getCallingConv());
6310+
thunk->setDLLStorageClass(f->getDLLStorageClass());
6311+
if (f->getComdat())
6312+
thunk->setComdat(f->getParent()->getOrInsertComdat(thunk->getName()));
6313+
setMustHaveFramePointer(thunk);
6314+
thunk->addFnAttr(llvm::Attribute::NoInline);
6315+
6316+
return cast<llvm::Function>(thunk);
6317+
}
6318+
6319+
llvm::Function*
6320+
IRGenModule::getAddrOfWitnessTableProfilingThunk(
6321+
llvm::Function *witness,
6322+
const NormalProtocolConformance &conformance) {
6323+
6324+
assert(
6325+
conformance.getDeclContext()->getSelfNominalTypeDecl()->isGenericContext());
6326+
6327+
return getOrCreateProfilingThunk(witness,
6328+
"__swift_prof_thunk__generic_witness__");
6329+
}
6330+
6331+
llvm::Function *
6332+
IRGenModule::getAddrOfVTableProfilingThunk(
6333+
llvm::Function *vTableFun, ClassDecl *classDecl) {
6334+
6335+
assert(classDecl->getSelfNominalTypeDecl()->isGenericContext());
6336+
6337+
return getOrCreateProfilingThunk(vTableFun,
6338+
"__swift_prof_thunk__generic_vtable__");
6339+
}

lib/IRGen/GenMeta.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,8 @@ static Flags getMethodDescriptorFlags(ValueDecl *fn) {
287287
static void buildMethodDescriptorFields(IRGenModule &IGM,
288288
const SILVTable *VTable,
289289
SILDeclRef fn,
290-
ConstantStructBuilder &descriptor) {
290+
ConstantStructBuilder &descriptor,
291+
ClassDecl *classDecl) {
291292
auto *func = cast<AbstractFunctionDecl>(fn.getDecl());
292293
// Classify the method.
293294
using Flags = MethodDescriptorFlags;
@@ -318,6 +319,13 @@ static void buildMethodDescriptorFields(IRGenModule &IGM,
318319
descriptor.addRelativeAddress(implFn);
319320
} else {
320321
llvm::Function *implFn = IGM.getAddrOfSILFunction(impl, NotForDefinition);
322+
323+
if (IGM.getOptions().UseProfilingMarkerThunks &&
324+
classDecl->getSelfNominalTypeDecl()->isGenericContext() &&
325+
!impl->getLoweredFunctionType()->isCoroutine()) {
326+
implFn = IGM.getAddrOfVTableProfilingThunk(implFn, classDecl);
327+
}
328+
321329
descriptor.addCompactFunctionReference(implFn);
322330
}
323331
} else {
@@ -328,7 +336,8 @@ static void buildMethodDescriptorFields(IRGenModule &IGM,
328336
}
329337

330338
void IRGenModule::emitNonoverriddenMethodDescriptor(const SILVTable *VTable,
331-
SILDeclRef declRef) {
339+
SILDeclRef declRef,
340+
ClassDecl *classDecl) {
332341
auto entity = LinkEntity::forMethodDescriptor(declRef);
333342
auto *var = cast<llvm::GlobalVariable>(
334343
getAddrOfLLVMVariable(entity, ConstantInit(), DebugTypeInfo()));
@@ -343,7 +352,7 @@ void IRGenModule::emitNonoverriddenMethodDescriptor(const SILVTable *VTable,
343352
ConstantInitBuilder ib(*this);
344353
ConstantStructBuilder sb(ib.beginStruct(MethodDescriptorStructTy));
345354

346-
buildMethodDescriptorFields(*this, VTable, declRef, sb);
355+
buildMethodDescriptorFields(*this, VTable, declRef, sb, classDecl);
347356

348357
auto init = sb.finishAndCreateFuture();
349358

@@ -2081,7 +2090,7 @@ namespace {
20812090

20822091
// Actually build the descriptor.
20832092
auto descriptor = B.beginStruct(IGM.MethodDescriptorStructTy);
2084-
buildMethodDescriptorFields(IGM, VTable, fn, descriptor);
2093+
buildMethodDescriptorFields(IGM, VTable, fn, descriptor, getType());
20852094
descriptor.finishAndAddTo(B);
20862095

20872096
// Emit method dispatch thunk if the class is resilient.
@@ -2129,7 +2138,7 @@ namespace {
21292138
// exist in the table in the class's context descriptor since it isn't
21302139
// in the vtable, but external clients need to be able to link against the
21312140
// symbol.
2132-
IGM.emitNonoverriddenMethodDescriptor(VTable, fn);
2141+
IGM.emitNonoverriddenMethodDescriptor(VTable, fn, getType());
21332142
}
21342143

21352144
void addOverrideTable() {

lib/IRGen/GenProto.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1636,7 +1636,17 @@ class AccessorConformanceInfo : public ConformanceInfo {
16361636
if (Func->isAsync()) {
16371637
witness = IGM.getAddrOfAsyncFunctionPointer(Func);
16381638
} else {
1639-
witness = IGM.getAddrOfSILFunction(Func, NotForDefinition);
1639+
1640+
auto *conformance = dyn_cast<NormalProtocolConformance>(&Conformance);
1641+
auto f = IGM.getAddrOfSILFunction(Func, NotForDefinition);
1642+
if (IGM.getOptions().UseProfilingMarkerThunks &&
1643+
conformance &&
1644+
conformance->getDeclContext()->
1645+
getSelfNominalTypeDecl()->isGenericContext() &&
1646+
!Func->getLoweredFunctionType()->isCoroutine())
1647+
witness = IGM.getAddrOfWitnessTableProfilingThunk(f, *conformance);
1648+
else witness = f;
1649+
16401650
}
16411651
} else {
16421652
// The method is removed by dead method elimination.
@@ -2013,11 +2023,18 @@ void ResilientWitnessTableBuilder::collectResilientWitnesses(
20132023

20142024
SILFunction *Func = entry.getMethodWitness().Witness;
20152025
llvm::Constant *witness;
2026+
bool isGenericConformance =
2027+
conformance.getDeclContext()->getSelfNominalTypeDecl()->isGenericContext();
20162028
if (Func) {
20172029
if (Func->isAsync())
20182030
witness = IGM.getAddrOfAsyncFunctionPointer(Func);
2019-
else
2020-
witness = IGM.getAddrOfSILFunction(Func, NotForDefinition);
2031+
else {
2032+
auto f = IGM.getAddrOfSILFunction(Func, NotForDefinition);
2033+
if (isGenericConformance && IGM.getOptions().UseProfilingMarkerThunks &&
2034+
!Func->getLoweredFunctionType()->isCoroutine())
2035+
witness = IGM.getAddrOfWitnessTableProfilingThunk(f, conformance);
2036+
else witness = f;
2037+
}
20212038
} else {
20222039
// The method is removed by dead method elimination.
20232040
// It should be never called. We add a null pointer.

lib/IRGen/IRGenModule.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1477,6 +1477,12 @@ void IRGenModule::setHasNoFramePointer(llvm::Function *F) {
14771477
F->addFnAttrs(b);
14781478
}
14791479

1480+
void IRGenModule::setMustHaveFramePointer(llvm::Function *F) {
1481+
llvm::AttrBuilder b(getLLVMContext());
1482+
b.addAttribute("frame-pointer", "all");
1483+
F->addFnAttrs(b);
1484+
}
1485+
14801486
/// Construct initial function attributes from options.
14811487
void IRGenModule::constructInitialFnAttributes(
14821488
llvm::AttrBuilder &Attrs, OptimizationMode FuncOptMode,

lib/IRGen/IRGenModule.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1586,6 +1586,7 @@ private: \
15861586
StackProtectorMode stackProtect = StackProtectorMode::NoStackProtector);
15871587
void setHasNoFramePointer(llvm::AttrBuilder &Attrs);
15881588
void setHasNoFramePointer(llvm::Function *F);
1589+
void setMustHaveFramePointer(llvm::Function *F);
15891590
llvm::AttributeList constructInitialAttributes();
15901591
StackProtectorMode shouldEmitStackProtector(SILFunction *f);
15911592

@@ -1660,7 +1661,8 @@ private: \
16601661
llvm::Constant *getAddrOfMethodDescriptor(SILDeclRef declRef,
16611662
ForDefinition_t forDefinition);
16621663
void emitNonoverriddenMethodDescriptor(const SILVTable *VTable,
1663-
SILDeclRef declRef);
1664+
SILDeclRef declRef,
1665+
ClassDecl *classDecl);
16641666

16651667
Address getAddrOfEnumCase(EnumElementDecl *Case,
16661668
ForDefinition_t forDefinition);
@@ -1821,6 +1823,16 @@ private: \
18211823
bool isDynamicallyReplaceableImplementation = false,
18221824
bool shouldCallPreviousImplementation = false);
18231825

1826+
llvm::Function *
1827+
getAddrOfWitnessTableProfilingThunk(llvm::Function *witness,
1828+
const NormalProtocolConformance &C);
1829+
1830+
llvm::Function *
1831+
getAddrOfVTableProfilingThunk(llvm::Function *f, ClassDecl *decl);
1832+
1833+
llvm::Function *getOrCreateProfilingThunk(llvm::Function *f,
1834+
StringRef prefix);
1835+
18241836
void emitDynamicReplacementOriginalFunctionThunk(SILFunction *f);
18251837

18261838
llvm::Function *getAddrOfContinuationPrototype(CanSILFunctionType fnType);

lib/IRGen/IRGenSIL.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3835,6 +3835,15 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) {
38353835
SubstitutionMap subMap = site.getSubstitutionMap();
38363836
emitPolymorphicArguments(*this, origCalleeType,
38373837
subMap, &witnessMetadata, llArgs);
3838+
3839+
// We currently only support non-async calls with profiling thunks.
3840+
if (IGM.getOptions().UseProfilingMarkerThunks &&
3841+
isa<FunctionRefInst>(site.getCallee()) &&
3842+
!site.getOrigCalleeType()->isAsync() &&
3843+
subMap.hasAnySubstitutableParams() &&
3844+
!subMap.getRecursiveProperties().hasPrimaryArchetype()) {
3845+
emission->useProfilingThunk();
3846+
}
38383847
}
38393848

38403849
if (calleeFP.shouldPassContinuationDirectly()) {

0 commit comments

Comments
 (0)