Skip to content

Commit 350e28b

Browse files
committed
[Profiler] Rework profiling of top-level code
Instead of creating and destroying a SILProfiler per TopLevelCodeDecl, setup a single profiler for the top-level entry point function, and visit all the TopLevelCodeDecls when mapping regions.
1 parent 337cbef commit 350e28b

File tree

9 files changed

+168
-81
lines changed

9 files changed

+168
-81
lines changed

include/swift/SIL/SILFunction.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -576,9 +576,7 @@ class SILFunction
576576
Profiler = InheritedProfiler;
577577
}
578578

579-
void createProfiler(ASTNode Root, SILDeclRef Ref);
580-
581-
void discardProfiler() { Profiler = nullptr; }
579+
void createProfiler(SILDeclRef Ref);
582580

583581
ProfileCounter getEntryCount() const { return EntryCount; }
584582

include/swift/SIL/SILProfiler.h

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,6 @@ class SILProfiler : public SILAllocated<SILProfiler> {
8383
private:
8484
SILModule &M;
8585

86-
ASTNode Root;
87-
8886
SILDeclRef forDecl;
8987

9088
bool EmitCoverageMapping;
@@ -107,13 +105,11 @@ class SILProfiler : public SILAllocated<SILProfiler> {
107105

108106
std::vector<std::tuple<std::string, uint64_t, std::string>> CoverageData;
109107

110-
SILProfiler(SILModule &M, ASTNode Root, SILDeclRef forDecl,
111-
bool EmitCoverageMapping)
112-
: M(M), Root(Root), forDecl(forDecl),
113-
EmitCoverageMapping(EmitCoverageMapping) {}
108+
SILProfiler(SILModule &M, SILDeclRef forDecl, bool EmitCoverageMapping)
109+
: M(M), forDecl(forDecl), EmitCoverageMapping(EmitCoverageMapping) {}
114110

115111
public:
116-
static SILProfiler *create(SILModule &M, ASTNode N, SILDeclRef Ref);
112+
static SILProfiler *create(SILModule &M, SILDeclRef Ref);
117113

118114
/// Check if the function is set up for profiling.
119115
bool hasRegionCounters() const { return NumRegionCounters != 0; }

lib/SIL/IR/SILFunction.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -332,12 +332,11 @@ void SILFunction::deleteSnapshot(int ID) {
332332
} while ((f = f->snapshots) != nullptr);
333333
}
334334

335-
void SILFunction::createProfiler(ASTNode Root, SILDeclRef Ref) {
335+
void SILFunction::createProfiler(SILDeclRef Ref) {
336336
assert(!Profiler && "Function already has a profiler");
337-
assert(Root && "Cannot profile a null ASTNode");
338337
assert(Ref && "Must have non-null SILDeclRef");
339338

340-
Profiler = SILProfiler::create(Module, Root, Ref);
339+
Profiler = SILProfiler::create(Module, Ref);
341340
if (!Profiler)
342341
return;
343342

lib/SIL/IR/SILProfiler.cpp

Lines changed: 93 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,75 @@
2525

2626
using namespace swift;
2727

28-
/// Check whether a root AST node should be profiled.
29-
static bool shouldProfile(ASTNode N, SILDeclRef Constant) {
28+
/// Unfortunately this is needed as ASTNode can't currently represent a
29+
/// SourceFile.
30+
class NodeToProfile final {
31+
/// For a direct ASTNode, this stores the node itself. For a main SourceFile,
32+
/// it stores the corresponding ModuleDecl.
33+
ASTNode Storage;
34+
35+
explicit NodeToProfile(ASTNode Node) : Storage(Node) {}
36+
37+
public:
38+
static NodeToProfile node(ASTNode Node) {
39+
assert(!isa_and_nonnull<ModuleDecl>(Node.dyn_cast<Decl *>()));
40+
return NodeToProfile(Node);
41+
}
42+
static NodeToProfile mainSourceFile(SourceFile *SF) {
43+
assert(SF->isScriptMode());
44+
auto N = NodeToProfile(SF->getParentModule());
45+
assert(N.getAsSourceFile() == SF);
46+
return N;
47+
}
48+
49+
/// If an ASTNode is being stored, returns it, otherwise \c nullptr.
50+
ASTNode getAsNode() const {
51+
return isSourceFile() ? nullptr : Storage;
52+
}
53+
54+
/// Whether this is storing a main SourceFile.
55+
bool isSourceFile() const {
56+
return getAsSourceFile();
57+
}
58+
59+
/// If a main SourceFile is being stored, returns it, otherwise \c nullptr.
60+
SourceFile *getAsSourceFile() const {
61+
auto *M = dyn_cast_or_null<ModuleDecl>(Storage.dyn_cast<Decl *>());
62+
return M ? &M->getMainSourceFile() : nullptr;
63+
}
64+
};
65+
66+
static NodeToProfile getNodeToProfile(SILDeclRef Constant) {
67+
// If we have an initialization expression, walk that instead of the variable.
68+
if (auto *E = Constant.getInitializationExpr())
69+
return NodeToProfile::node(E);
70+
71+
// Otherwise, we walk the SILDeclRef's node directly.
72+
using LocKind = SILDeclRef::LocKind;
73+
switch (Constant.getLocKind()) {
74+
case LocKind::Decl:
75+
return NodeToProfile::node(Constant.getDecl());
76+
case LocKind::Closure:
77+
return NodeToProfile::node(Constant.getAbstractClosureExpr());
78+
case LocKind::File: {
79+
auto *SF = cast<SourceFile>(Constant.getFileUnit());
80+
return NodeToProfile::mainSourceFile(SF);
81+
}
82+
}
83+
llvm_unreachable("Unhandled case in switch!");
84+
}
85+
86+
/// Check whether we should profile a given SILDeclRef.
87+
static bool shouldProfile(SILDeclRef Constant) {
88+
auto Root = getNodeToProfile(Constant);
89+
3090
// Do not profile AST nodes with invalid source locations.
31-
if (N.getStartLoc().isInvalid() || N.getEndLoc().isInvalid()) {
32-
LLVM_DEBUG(llvm::dbgs()
33-
<< "Skipping ASTNode: invalid start/end locations\n");
34-
return false;
91+
if (auto N = Root.getAsNode()) {
92+
if (N.getStartLoc().isInvalid() || N.getEndLoc().isInvalid()) {
93+
LLVM_DEBUG(llvm::dbgs()
94+
<< "Skipping ASTNode: invalid start/end locations\n");
95+
return false;
96+
}
3597
}
3698

3799
// Do not profile AST nodes in unavailable contexts.
@@ -62,18 +124,17 @@ static Stmt *getProfilerStmtForCase(CaseStmt *caseStmt) {
62124
llvm_unreachable("invalid parent kind");
63125
}
64126

65-
SILProfiler *SILProfiler::create(SILModule &M, ASTNode N, SILDeclRef Ref) {
127+
SILProfiler *SILProfiler::create(SILModule &M, SILDeclRef Ref) {
66128
// If profiling isn't enabled, don't profile anything.
67129
const auto &Opts = M.getOptions();
68130
if (!Opts.GenerateProfile && Opts.UseProfile.empty())
69131
return nullptr;
70132

71-
if (!shouldProfile(N, Ref))
133+
if (!shouldProfile(Ref))
72134
return nullptr;
73135

74136
auto *Buf = M.allocate<SILProfiler>(1);
75-
auto *SP =
76-
::new (Buf) SILProfiler(M, N, Ref, Opts.EmitProfileCoverageMapping);
137+
auto *SP = ::new (Buf) SILProfiler(M, Ref, Opts.EmitProfileCoverageMapping);
77138
SP->assignRegionCounters();
78139
return SP;
79140
}
@@ -1277,29 +1338,33 @@ static StringRef getCurrentFileName(SILDeclRef forDecl) {
12771338
return {};
12781339
}
12791340

1341+
static void walkNode(NodeToProfile Node, ASTWalker &Walker) {
1342+
if (auto N = Node.getAsNode()) {
1343+
N.walk(Walker);
1344+
} else {
1345+
// We want to walk the SourceFile for a top-level entry point. We will only
1346+
// assign regions to TopLevelCodeDecls.
1347+
Node.getAsSourceFile()->walk(Walker);
1348+
}
1349+
}
1350+
12801351
void SILProfiler::assignRegionCounters() {
12811352
const auto &SM = M.getASTContext().SourceMgr;
12821353

12831354
CurrentFileName = getCurrentFileName(forDecl);
12841355

12851356
MapRegionCounters Mapper(forDecl, RegionCounterMap);
12861357

1287-
std::string CurrentFuncName;
1288-
FormalLinkage CurrentFuncLinkage;
1289-
if (auto *D = Root.dyn_cast<Decl *>()) {
1290-
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
1291-
CurrentFuncName = forDecl.mangle();
1292-
CurrentFuncLinkage = getDeclLinkage(AFD);
1293-
} else {
1294-
auto *TLCD = cast<TopLevelCodeDecl>(D);
1295-
llvm::raw_string_ostream OS{CurrentFuncName};
1296-
OS << "__tlcd_";
1297-
TLCD->getStartLoc().printLineAndColumn(OS, SM);
1298-
CurrentFuncLinkage = FormalLinkage::HiddenUnique;
1358+
auto Root = getNodeToProfile(forDecl);
1359+
1360+
auto CurrentFuncName = forDecl.mangle();
1361+
auto CurrentFuncLinkage = FormalLinkage::HiddenUnique;
1362+
1363+
if (auto N = Root.getAsNode()) {
1364+
if (auto *D = N.dyn_cast<Decl *>()) {
1365+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D))
1366+
CurrentFuncLinkage = getDeclLinkage(AFD);
12991367
}
1300-
} else {
1301-
CurrentFuncName = forDecl.mangle();
1302-
CurrentFuncLinkage = FormalLinkage::HiddenUnique;
13031368
}
13041369

13051370
PGOFuncName = llvm::getPGOFuncName(
@@ -1311,15 +1376,15 @@ void SILProfiler::assignRegionCounters() {
13111376

13121377
LLVM_DEBUG(llvm::dbgs() << "Assigning counters to: " << CurrentFuncName
13131378
<< "\n");
1314-
Root.walk(Mapper);
1379+
walkNode(Root, Mapper);
13151380

13161381
NumRegionCounters = Mapper.NextCounter;
13171382
// TODO: Mapper needs to calculate a function hash as it goes.
13181383
PGOFuncHash = 0x0;
13191384

13201385
if (EmitCoverageMapping) {
13211386
CoverageMapping Coverage(SM, forDecl);
1322-
Root.walk(Coverage);
1387+
walkNode(Root, Coverage);
13231388
CovMap =
13241389
Coverage.emitSourceRegions(M, CurrentFuncName, PGOFuncName, PGOFuncHash,
13251390
RegionCounterMap, CurrentFileName);
@@ -1337,7 +1402,7 @@ void SILProfiler::assignRegionCounters() {
13371402
}
13381403
PGOMapping pgoMapper(forDecl, RegionCounterMap, LoadedCounts.get(),
13391404
RegionLoadedCounterMap, RegionCondToParentMap);
1340-
Root.walk(pgoMapper);
1405+
walkNode(Root, pgoMapper);
13411406
}
13421407
}
13431408

lib/SILGen/SILGen.cpp

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -856,7 +856,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
856856
if (auto *ce = constant.getAbstractClosureExpr()) {
857857
preEmitFunction(constant, f, ce);
858858
PrettyStackTraceSILFunction X("silgen closureexpr", f);
859-
f->createProfiler(ce, constant);
859+
f->createProfiler(constant);
860860
SILGenFunction(*this, *f, ce).emitClosure(ce);
861861
postEmitFunction(constant, f);
862862
break;
@@ -866,7 +866,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
866866

867867
preEmitFunction(constant, f, fd);
868868
PrettyStackTraceSILFunction X("silgen emitFunction", f);
869-
f->createProfiler(fd, constant);
869+
f->createProfiler(constant);
870870
SILGenFunction(*this, *f, fd).emitFunction(fd);
871871
postEmitFunction(constant, f);
872872
break;
@@ -885,7 +885,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
885885
} else {
886886
preEmitFunction(constant, f, decl);
887887
PrettyStackTraceSILFunction X("silgen emitConstructor", f);
888-
f->createProfiler(decl, constant);
888+
f->createProfiler(constant);
889889
SILGenFunction(*this, *f, decl).emitValueConstructor(decl);
890890
postEmitFunction(constant, f);
891891
}
@@ -898,7 +898,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
898898

899899
preEmitFunction(constant, f, decl);
900900
PrettyStackTraceSILFunction X("silgen constructor initializer", f);
901-
f->createProfiler(decl, constant);
901+
f->createProfiler(constant);
902902
SILGenFunction(*this, *f, decl).emitClassConstructorInitializer(decl);
903903
postEmitFunction(constant, f);
904904
break;
@@ -952,7 +952,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
952952
auto loc = RegularLocation::getAutoGeneratedLocation(init);
953953
preEmitFunction(constant, f, loc);
954954
PrettyStackTraceSILFunction X("silgen emitStoredPropertyInitialization", f);
955-
f->createProfiler(init, constant);
955+
f->createProfiler(constant);
956956
SILGenFunction SGF(*this, *f, initDC);
957957

958958
// If this is a stored property initializer inside a type at global scope,
@@ -981,7 +981,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
981981
auto *init = constant.getInitializationExpr();
982982
assert(init);
983983

984-
f->createProfiler(init, constant);
984+
f->createProfiler(constant);
985985
auto varDC = var->getInnermostDeclContext();
986986
SILGenFunction SGF(*this, *f, varDC);
987987
SGF.emitGeneratorFunction(constant, init, /*EmitProfilerIncrement*/ true);
@@ -1039,7 +1039,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
10391039
auto *dd = cast<DestructorDecl>(constant.getDecl());
10401040
preEmitFunction(constant, f, dd);
10411041
PrettyStackTraceSILFunction X("silgen emitDestroyingDestructor", f);
1042-
f->createProfiler(dd, constant);
1042+
f->createProfiler(constant);
10431043
SILGenFunction(*this, *f, dd).emitDestroyingDestructor(dd);
10441044
postEmitFunction(constant, f);
10451045
return;
@@ -1053,7 +1053,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
10531053
if (usesObjCAllocator(cd)) {
10541054
preEmitFunction(constant, f, dd);
10551055
PrettyStackTraceSILFunction X("silgen emitDestructor -dealloc", f);
1056-
f->createProfiler(dd, constant);
1056+
f->createProfiler(constant);
10571057
SILGenFunction(*this, *f, dd).emitObjCDestructor(constant);
10581058
postEmitFunction(constant, f);
10591059
return;
@@ -1936,23 +1936,8 @@ void SILGenModule::visitTopLevelCodeDecl(TopLevelCodeDecl *td) {
19361936
if (!TopLevelSGF->B.hasValidInsertionPoint())
19371937
return;
19381938

1939-
// Retrieve the entry point constant we're emitting for.
1940-
// FIXME: This won't be necessary once we unify emission of the entry point
1941-
// such that we walk all of the TopLevelCodeDecls in one shot. This will be
1942-
// needed in order to requestify entry point emission.
1943-
auto *SF = td->getParentSourceFile();
1944-
assert(SF && "TopLevelDecl outside of a SourceFile?");
1945-
auto entryPoint = TopLevelSGF->F.isAsync()
1946-
? SILDeclRef::getAsyncMainFileEntryPoint(SF)
1947-
: SILDeclRef::getMainFileEntryPoint(SF);
1948-
1949-
// A single SILFunction may be used to lower multiple top-level decls. When
1950-
// this happens, fresh profile counters must be assigned to the new decl.
1951-
TopLevelSGF->F.discardProfiler();
1952-
TopLevelSGF->F.createProfiler(td, entryPoint);
1953-
19541939
TopLevelSGF->emitProfilerIncrement(td->getBody());
1955-
1940+
19561941
DebugScope DS(*TopLevelSGF, CleanupLocation(td));
19571942

19581943
for (auto &ESD : td->getBody()->getElements()) {
@@ -1985,11 +1970,15 @@ namespace {
19851970
/// An RAII class to scope source file codegen.
19861971
class SourceFileScope {
19871972
SILGenModule &sgm;
1973+
SILDeclRef EntryRef;
19881974
Optional<Scope> scope;
19891975
bool isAsyncTopLevel = false;
19901976
public:
19911977
SourceFileScope(SILGenModule &sgm, SourceFile *sf) : sgm(sgm) {
19921978
// If this is the script-mode file for the module, create a toplevel.
1979+
// TODO: We need to unify emission of the entry point such that we walk
1980+
// all of the TopLevelCodeDecls in one shot. This will be needed in order
1981+
// to requestify entry point emission.
19931982
if (sf->isScriptMode()) {
19941983
assert(!sgm.TopLevelSGF && "already emitted toplevel?!");
19951984
assert(!sgm.M.lookUpFunction(
@@ -1999,15 +1988,19 @@ class SourceFileScope {
19991988
auto mainEntryRef = SILDeclRef::getMainFileEntryPoint(sf);
20001989
SILFunction * toplevel = sgm.getFunction(mainEntryRef, ForDefinition);
20011990
toplevel->setBare(IsBare);
1991+
EntryRef = mainEntryRef;
20021992

20031993
if (sf->isAsyncContext()) {
20041994
isAsyncTopLevel = true;
20051995
auto asyncEntryRef = SILDeclRef::getAsyncMainFileEntryPoint(sf);
2006-
SILFunction * asyncTopLevel = sgm.getFunction(asyncEntryRef, ForDefinition);
1996+
auto *asyncTopLevel = sgm.getFunction(asyncEntryRef, ForDefinition);
20071997
SILGenFunction(sgm, *toplevel, sf).emitAsyncMainThreadStart(asyncEntryRef);
20081998
toplevel = asyncTopLevel;
1999+
EntryRef = asyncEntryRef;
20092000
}
20102001

2002+
toplevel->createProfiler(EntryRef);
2003+
20112004
sgm.TopLevelSGF = new SILGenFunction(sgm, *toplevel, sf);
20122005
sgm.TopLevelSGF->MagicFunctionName = sgm.SwiftModule->getName();
20132006
auto moduleCleanupLoc = CleanupLocation::getModuleCleanupLocation();

test/Profiler/coverage_maindecl.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -emit-sil -emit-sorted-sil -module-name coverage_maindecl -parse-as-library %s | %FileCheck %s
2+
// RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -emit-ir -parse-as-library %s
3+
4+
// CHECK-NOT: sil_coverage_map {{.*}} "main"
5+
6+
@main
7+
struct S {
8+
// CHECK: sil_coverage_map {{.*}} "{{.*}}s17coverage_maindecl1SV4mainyyFZ"
9+
// CHECK-NEXT: [[@LINE+1]]:22 -> [[@LINE+10]]:4 : 0
10+
static func main() {
11+
var i : Int32 = 0
12+
13+
// CHECK-NEXT: [[@LINE+3]]:11 -> [[@LINE+3]]:19 : (0 + 1)
14+
// CHECK-NEXT: [[@LINE+2]]:20 -> [[@LINE+4]]:6 : 1
15+
// CHECK-NEXT: [[@LINE+3]]:6 -> [[@LINE+4]]:4 : 0
16+
while (i < 10) {
17+
i += 1
18+
}
19+
} // CHECK-NEXT: }
20+
}
21+
22+
// CHECK-NOT: sil_coverage_map {{.*}} "main"

test/Profiler/coverage_optimized.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,6 @@ func baz() {
4646
baz()
4747

4848
// IRGEN-LABEL: define {{.*}} @main
49-
// IRGEN: call void @llvm.instrprof.increment({{.*}} @"__profn_{{.*}}__tlcd_line
49+
// IRGEN: call void @llvm.instrprof.increment({{.*}} @"__profn_{{.*}}main
5050
// IRGEN: call void @llvm.instrprof.increment({{.*}} @"__profn_{{.*}}$s18coverage_optimized3bazyyF"
5151
// IRGEN: call void @llvm.instrprof.increment({{.*}} @"__profn_{{.*}}$s18coverage_optimized3barSbyF"

0 commit comments

Comments
 (0)