Skip to content

Commit 410bf3c

Browse files
committed
IRGen: Add the ability to mark certain generic entry points in back traces
Mark generic function calls with concrete parameters, generic v-table calls and generic witness table calls where self is generic.
1 parent 79a0afb commit 410bf3c

File tree

13 files changed

+287
-10
lines changed

13 files changed

+287
-10
lines changed

include/swift/AST/IRGenOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,8 @@ class IRGenOptions {
484484
// (LLVM's 'frame-pointer=all').
485485
unsigned AsyncFramePointerAll : 1;
486486

487+
unsigned UseProfilingMarkerThunks : 1;
488+
487489
/// The number of threads for multi-threaded code generation.
488490
unsigned NumThreads = 0;
489491

@@ -573,6 +575,7 @@ class IRGenOptions {
573575
DisableReadonlyStaticObjects(false), CollocatedMetadataFunctions(false),
574576
ColocateTypeDescriptors(true), UseRelativeProtocolWitnessTables(false),
575577
UseFragileResilientProtocolWitnesses(false),
578+
UseProfilingMarkerThunks(false),
576579
EnableHotColdSplit(false), EmitAsyncFramePushPopMetadata(false),
577580
AsyncFramePointerAll(false),
578581
CmdArgs(), SanitizeCoverage(llvm::SanitizerCoverageOptions()),

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
@@ -3469,6 +3469,10 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
34693469
Args.hasFlag(OPT_enable_fragile_resilient_protocol_witnesses,
34703470
OPT_disable_fragile_resilient_protocol_witnesses,
34713471
Opts.UseFragileResilientProtocolWitnesses);
3472+
Opts.UseProfilingMarkerThunks =
3473+
Args.hasFlag(OPT_enable_profiling_marker_thunks,
3474+
OPT_disable_profiling_marker_thunks,
3475+
Opts.UseProfilingMarkerThunks);
34723476
Opts.EnableHotColdSplit =
34733477
Args.hasFlag(OPT_enable_split_cold_code,
34743478
OPT_disable_split_cold_code,

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: 26 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"
@@ -3219,7 +3220,31 @@ llvm::CallBase *CallEmission::emitCallSite() {
32193220
} else
32203221
IGF.setCallsThunksWithForeignExceptionTraps();
32213222
}
3222-
auto call = createCall(fn, Args);
3223+
3224+
auto fnToCall = fn;
3225+
if (UseProfilingThunk) {
3226+
assert(fnToCall.isConstant() && "Non constant function in profiling thunk");
3227+
auto genericFn = cast<llvm::Function>(fnToCall.getRawPointer());
3228+
auto replacementTypes = CurCallee.getSubstitutions().getReplacementTypes();
3229+
llvm::SmallString<64> name;
3230+
{
3231+
llvm::raw_svector_ostream os(name);
3232+
os << "__swift_prof_thunk__generic_func__";
3233+
os << replacementTypes.size();
3234+
os << "__";
3235+
for (auto replTy : replacementTypes) {
3236+
IRGenMangler mangler;
3237+
os << mangler.mangleTypeMetadataFull(replTy->getCanonicalType());
3238+
os << "___";
3239+
}
3240+
os << "fun__";
3241+
}
3242+
auto *thunk = IGF.IGM.getOrCreateProfilingThunk(genericFn, name);
3243+
fnToCall = fnToCall.withProfilingThunk(thunk);
3244+
3245+
}
3246+
3247+
auto call = createCall(fnToCall, Args);
32233248
if (invokeNormalDest)
32243249
IGF.Builder.emitBlock(invokeNormalDest);
32253250

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
@@ -1631,7 +1631,17 @@ class AccessorConformanceInfo : public ConformanceInfo {
16311631
if (Func->isAsync()) {
16321632
witness = IGM.getAddrOfAsyncFunctionPointer(Func);
16331633
} else {
1634-
witness = IGM.getAddrOfSILFunction(Func, NotForDefinition);
1634+
1635+
auto *conformance = dyn_cast<NormalProtocolConformance>(&Conformance);
1636+
auto f = IGM.getAddrOfSILFunction(Func, NotForDefinition);
1637+
if (IGM.getOptions().UseProfilingMarkerThunks &&
1638+
conformance &&
1639+
conformance->getDeclContext()->
1640+
getSelfNominalTypeDecl()->isGenericContext() &&
1641+
!Func->getLoweredFunctionType()->isCoroutine())
1642+
witness = IGM.getAddrOfWitnessTableProfilingThunk(f, *conformance);
1643+
else witness = f;
1644+
16351645
}
16361646
} else {
16371647
// The method is removed by dead method elimination.
@@ -1994,11 +2004,18 @@ void ResilientWitnessTableBuilder::collectResilientWitnesses(
19942004

19952005
SILFunction *Func = entry.getMethodWitness().Witness;
19962006
llvm::Constant *witness;
2007+
bool isGenericConformance =
2008+
conformance.getDeclContext()->getSelfNominalTypeDecl()->isGenericContext();
19972009
if (Func) {
19982010
if (Func->isAsync())
19992011
witness = IGM.getAddrOfAsyncFunctionPointer(Func);
2000-
else
2001-
witness = IGM.getAddrOfSILFunction(Func, NotForDefinition);
2012+
else {
2013+
auto f = IGM.getAddrOfSILFunction(Func, NotForDefinition);
2014+
if (isGenericConformance && IGM.getOptions().UseProfilingMarkerThunks &&
2015+
!Func->getLoweredFunctionType()->isCoroutine())
2016+
witness = IGM.getAddrOfWitnessTableProfilingThunk(f, conformance);
2017+
else witness = f;
2018+
}
20022019
} else {
20032020
// The method is removed by dead method elimination.
20042021
// 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
@@ -3819,6 +3819,15 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) {
38193819
SubstitutionMap subMap = site.getSubstitutionMap();
38203820
emitPolymorphicArguments(*this, origCalleeType,
38213821
subMap, &witnessMetadata, llArgs);
3822+
3823+
// We currently only support non-async calls with profiling thunks.
3824+
if (IGM.getOptions().UseProfilingMarkerThunks &&
3825+
isa<FunctionRefInst>(site.getCallee()) &&
3826+
!site.getOrigCalleeType()->isAsync() &&
3827+
subMap.hasAnySubstitutableParams() &&
3828+
!subMap.hasArchetypes()) {
3829+
emission->useProfilingThunk();
3830+
}
38223831
}
38233832

38243833
if (calleeFP.shouldPassContinuationDirectly()) {

0 commit comments

Comments
 (0)