Skip to content

Commit 14e1f29

Browse files
authored
Merge pull request #16001 from vedantk/cov-delegate-init-4.2
2 parents 47b6963 + 314a65d commit 14e1f29

File tree

10 files changed

+279
-155
lines changed

10 files changed

+279
-155
lines changed

lib/SIL/SILProfiler.cpp

Lines changed: 133 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -25,87 +25,52 @@
2525

2626
using namespace swift;
2727

28-
static bool isClosureWithBody(AbstractClosureExpr *ACE) {
28+
/// Check if a closure has a body.
29+
static bool doesClosureHaveBody(AbstractClosureExpr *ACE) {
2930
if (auto *CE = dyn_cast<ClosureExpr>(ACE))
3031
return CE->getBody();
3132
if (auto *autoCE = dyn_cast<AutoClosureExpr>(ACE))
3233
return autoCE->getBody();
3334
return false;
3435
}
3536

37+
/// Check whether a root AST node is unmapped, i.e not profiled.
3638
static bool isUnmapped(ASTNode N) {
3739
if (auto *E = N.dyn_cast<Expr *>()) {
3840
auto *CE = dyn_cast<AbstractClosureExpr>(E);
39-
return !CE || (!isa<AutoClosureExpr>(CE) && CE->isImplicit()) ||
40-
!isClosureWithBody(CE);
41+
42+
// Only map closure expressions with bodies.
43+
if (!CE || !doesClosureHaveBody(CE))
44+
return true;
45+
46+
// Don't map implicit closures, unless they're autoclosures.
47+
if (!isa<AutoClosureExpr>(CE) && CE->isImplicit())
48+
return true;
49+
50+
return false;
4151
}
4252

4353
auto *D = N.get<Decl *>();
4454
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
55+
// Don't map functions without bodies.
4556
if (!AFD->getBody())
4657
return true;
4758

59+
// Map all *structors, even if they are implicit.
60+
if (isa<ConstructorDecl>(D) || isa<DestructorDecl>(D))
61+
return false;
62+
63+
// Map implicit getters.
4864
if (auto *accessor = dyn_cast<AccessorDecl>(AFD))
4965
if (accessor->isImplicit() && accessor->isGetter())
5066
return false;
5167
}
5268

53-
if (isa<ConstructorDecl>(D) || isa<DestructorDecl>(D))
54-
return false;
55-
56-
return D->isImplicit() || isa<EnumCaseDecl>(D);
57-
}
58-
59-
/// A simple heuristic to determine whether \p E contains a definition of a
60-
/// closure. This is not complete, but it suffices to cheaply filter away some
61-
/// redundant coverage mappings.
62-
static bool containsClosure(Expr *E) {
63-
Expr *candidateExpr = E;
64-
if (auto *ce = dyn_cast<CallExpr>(E))
65-
candidateExpr = ce->getDirectCallee();
66-
return dyn_cast_or_null<AbstractClosureExpr>(candidateExpr);
67-
}
68-
69-
/// Walk the non-static initializers in \p PBD.
70-
static void walkPatternForProfiling(PatternBindingDecl *PBD, ASTWalker &Walker,
71-
bool AllowClosures = true) {
72-
if (PBD && !PBD->isStatic())
73-
for (auto E : PBD->getPatternList())
74-
if (E.getInit())
75-
if (AllowClosures || !containsClosure(E.getInit()))
76-
E.getInit()->walk(Walker);
77-
}
78-
79-
/// Walk the AST of \c Root and related nodes that are relevant for profiling.
80-
static void walkFunctionForProfiling(AbstractFunctionDecl *Root,
81-
ASTWalker &Walker) {
82-
Root->walk(Walker);
83-
84-
// We treat non-closure member initializers as part of the constructor for
85-
// profiling.
86-
if (auto *CD = dyn_cast<ConstructorDecl>(Root)) {
87-
auto *NominalType =
88-
CD->getDeclContext()->getAsNominalTypeOrNominalTypeExtensionContext();
89-
for (auto *Member : NominalType->getMembers()) {
90-
// Find pattern binding declarations that have initializers.
91-
if (auto *PBD = dyn_cast<PatternBindingDecl>(Member))
92-
walkPatternForProfiling(PBD, Walker, /*AllowClosures=*/false);
93-
}
94-
}
95-
}
69+
// Skip any remaining implicit, or otherwise unsupported decls.
70+
if (D->isImplicit() || isa<EnumCaseDecl>(D))
71+
return true;
9672

97-
/// Walk \p D for profiling.
98-
static void walkForProfiling(ASTNode N, ASTWalker &Walker) {
99-
if (auto *D = N.dyn_cast<Decl *>()) {
100-
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D))
101-
walkFunctionForProfiling(AFD, Walker);
102-
else if (auto *PBD = dyn_cast<PatternBindingDecl>(D))
103-
walkPatternForProfiling(PBD, Walker);
104-
else if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D))
105-
TLCD->walk(Walker);
106-
} else if (auto *E = N.dyn_cast<Expr *>()) {
107-
cast<AbstractClosureExpr>(E)->walk(Walker);
108-
}
73+
return false;
10974
}
11075

11176
namespace swift {
@@ -122,27 +87,43 @@ static bool hasASTBeenTypeChecked(ASTNode N) {
12287
return !SF || SF->ASTStage >= SourceFile::TypeChecked;
12388
}
12489

90+
/// Check whether a mapped AST node requires a new profiler.
91+
static bool canCreateProfilerForAST(ASTNode N) {
92+
assert(hasASTBeenTypeChecked(N) && "Cannot use this AST for profiling");
93+
94+
if (auto *D = N.dyn_cast<Decl *>()) {
95+
// Any mapped function may be profiled. There's an exception for
96+
// constructors because all of the constructors for a type share a single
97+
// profiler.
98+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D))
99+
return !isa<ConstructorDecl>(AFD);
100+
101+
if (isa<TopLevelCodeDecl>(D))
102+
return true;
103+
104+
if (isa<NominalTypeDecl>(D))
105+
return true;
106+
} else {
107+
auto *E = N.get<Expr *>();
108+
if (isa<AbstractClosureExpr>(E))
109+
return true;
110+
}
111+
return false;
112+
}
113+
125114
SILProfiler *SILProfiler::create(SILModule &M, ForDefinition_t forDefinition,
126115
ASTNode N) {
127116
// Avoid generating profiling state for declarations.
128117
if (!forDefinition)
129118
return nullptr;
130119

131-
assert(hasASTBeenTypeChecked(N) && "Cannot use this AST for code coverage");
132-
133-
if (auto *D = N.dyn_cast<Decl *>()) {
134-
assert(isa<AbstractFunctionDecl>(D) ||
135-
isa<TopLevelCodeDecl>(D) && "Cannot create profiler");
136-
} else if (auto *E = N.dyn_cast<Expr *>()) {
137-
assert(isa<AbstractClosureExpr>(E) && "Cannot create profiler");
138-
} else {
139-
llvm_unreachable("Invalid AST node for profiling");
140-
}
141-
142120
const auto &Opts = M.getOptions();
143121
if (!doesASTRequireProfiling(M, N) && Opts.UseProfile.empty())
144122
return nullptr;
145123

124+
if (!canCreateProfilerForAST(N))
125+
llvm_unreachable("Invalid AST node for profiling");
126+
146127
auto *Buf = M.allocate<SILProfiler>(1);
147128
auto *SP = ::new (Buf) SILProfiler(M, N, Opts.EmitProfileCoverageMapping);
148129
SP->assignRegionCounters();
@@ -151,6 +132,15 @@ SILProfiler *SILProfiler::create(SILModule &M, ForDefinition_t forDefinition,
151132

152133
namespace {
153134

135+
/// Walk the non-static initializers in \p PBD.
136+
static void walkPatternForProfiling(PatternBindingDecl *PBD,
137+
ASTWalker &Walker) {
138+
if (PBD && !PBD->isStatic())
139+
for (auto E : PBD->getPatternList())
140+
if (E.getInit())
141+
E.getInit()->walk(Walker);
142+
}
143+
154144
/// Special logic for handling closure visitation.
155145
///
156146
/// To prevent a closure from being mapped twice, avoid recursively walking
@@ -169,25 +159,35 @@ std::pair<bool, Expr *> visitClosureExpr(ASTWalker &Walker,
169159
/// An ASTWalker that maps ASTNodes to profiling counters.
170160
struct MapRegionCounters : public ASTWalker {
171161
/// The next counter value to assign.
172-
unsigned NextCounter;
162+
unsigned NextCounter = 0;
173163

174164
/// The map of statements to counters.
175165
llvm::DenseMap<ASTNode, unsigned> &CounterMap;
176166

167+
/// A flag indicating whether we're walking a nominal type.
168+
bool WithinNominalType = false;
169+
177170
MapRegionCounters(llvm::DenseMap<ASTNode, unsigned> &CounterMap)
178-
: NextCounter(0), CounterMap(CounterMap) {}
171+
: CounterMap(CounterMap) {}
179172

180173
bool walkToDeclPre(Decl *D) override {
181174
if (isUnmapped(D))
182175
return false;
176+
183177
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
184-
bool FirstDecl = Parent.isNull();
185-
if (FirstDecl)
178+
// Don't map a nested function unless it's a constructor.
179+
bool continueWalk = Parent.isNull() || isa<ConstructorDecl>(AFD);
180+
if (continueWalk)
186181
CounterMap[AFD->getBody()] = NextCounter++;
187-
return FirstDecl;
188-
}
189-
if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D))
182+
return continueWalk;
183+
} else if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D)) {
190184
CounterMap[TLCD->getBody()] = NextCounter++;
185+
} else if (isa<NominalTypeDecl>(D)) {
186+
bool continueWalk = Parent.isNull();
187+
if (continueWalk)
188+
WithinNominalType = true;
189+
return continueWalk;
190+
}
191191
return true;
192192
}
193193

@@ -559,7 +559,11 @@ struct CoverageMapping : public ASTWalker {
559559
/// \brief A stack of active repeat-while loops.
560560
std::vector<RepeatWhileStmt *> RepeatWhileStack;
561561

562-
CounterExpr *ExitCounter;
562+
CounterExpr *ExitCounter = nullptr;
563+
564+
Stmt *ImplicitTopLevelBody = nullptr;
565+
566+
NominalTypeDecl *ParentNominalType = nullptr;
563567

564568
/// \brief Return true if \c Node has an associated counter.
565569
bool hasCounter(ASTNode Node) { return CounterMap.count(Node); }
@@ -761,19 +765,40 @@ struct CoverageMapping : public ASTWalker {
761765
bool walkToDeclPre(Decl *D) override {
762766
if (isUnmapped(D))
763767
return false;
768+
764769
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
765-
bool FirstDecl = Parent.isNull();
766-
if (FirstDecl)
767-
assignCounter(AFD->getBody());
768-
return FirstDecl;
769-
}
770-
else if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D))
770+
// Don't map a nested function unless it's a constructor.
771+
bool continueWalk = Parent.isNull() || isa<ConstructorDecl>(AFD);
772+
if (continueWalk) {
773+
CounterExpr &funcCounter = assignCounter(AFD->getBody());
774+
775+
if (isa<ConstructorDecl>(AFD))
776+
addToCounter(ParentNominalType, funcCounter);
777+
}
778+
return continueWalk;
779+
} else if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D)) {
771780
assignCounter(TLCD->getBody());
781+
ImplicitTopLevelBody = TLCD->getBody();
782+
} else if (auto *NTD = dyn_cast<NominalTypeDecl>(D)) {
783+
bool continueWalk = Parent.isNull();
784+
if (continueWalk) {
785+
ParentNominalType = NTD;
786+
assignCounter(NTD, CounterExpr::Zero());
787+
pushRegion(NTD);
788+
}
789+
return continueWalk;
790+
}
791+
return true;
792+
}
793+
794+
bool walkToDeclPost(Decl *D) override {
795+
if (isa<TopLevelCodeDecl>(D))
796+
ImplicitTopLevelBody = nullptr;
772797
return true;
773798
}
774799

775800
std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override {
776-
if (S->isImplicit())
801+
if (S->isImplicit() && S != ImplicitTopLevelBody)
777802
return {true, S};
778803

779804
if (!RegionStack.empty())
@@ -834,7 +859,7 @@ struct CoverageMapping : public ASTWalker {
834859
}
835860

836861
Stmt *walkToStmtPost(Stmt *S) override {
837-
if (S->isImplicit())
862+
if (S->isImplicit() && S != ImplicitTopLevelBody)
838863
return S;
839864

840865
if (isa<BraceStmt>(S)) {
@@ -906,11 +931,8 @@ struct CoverageMapping : public ASTWalker {
906931
return Result;
907932
} else if (auto *IE = dyn_cast<IfExpr>(E)) {
908933
CounterExpr &ThenCounter = assignCounter(IE->getThenExpr());
909-
if (RegionStack.empty())
910-
assignCounter(IE->getElseExpr());
911-
else
912-
assignCounter(IE->getElseExpr(),
913-
CounterExpr::Sub(getCurrentCounter(), ThenCounter));
934+
assignCounter(IE->getElseExpr(),
935+
CounterExpr::Sub(getCurrentCounter(), ThenCounter));
914936
}
915937

916938
if (hasCounter(E))
@@ -951,6 +973,16 @@ static StringRef getCurrentFileName(ASTNode Root) {
951973
return {};
952974
}
953975

976+
static void walkTopLevelNodeForProfiling(ASTNode Root, ASTWalker &Walker) {
977+
Root.walk(Walker);
978+
979+
// Visit extensions when walking through a nominal type.
980+
auto *NTD = dyn_cast_or_null<NominalTypeDecl>(Root.dyn_cast<Decl *>());
981+
if (NTD)
982+
for (ExtensionDecl *ED : NTD->getExtensions())
983+
ED->walk(Walker);
984+
}
985+
954986
void SILProfiler::assignRegionCounters() {
955987
const auto &SM = M.getASTContext().SourceMgr;
956988

@@ -970,31 +1002,31 @@ void SILProfiler::assignRegionCounters() {
9701002
TLCD->getStartLoc().printLineAndColumn(OS, SM);
9711003
CurrentFuncLinkage = FormalLinkage::HiddenUnique;
9721004
} else {
973-
llvm_unreachable("Unsupported decl");
974-
}
975-
} else {
976-
auto *E = Root.get<Expr *>();
977-
if (auto *CE = dyn_cast<AbstractClosureExpr>(E)) {
978-
CurrentFuncName = SILDeclRef(CE).mangle();
1005+
auto *NTD = cast<NominalTypeDecl>(D);
1006+
llvm::raw_string_ostream OS{CurrentFuncName};
1007+
OS << "__ntd_" << NTD->getNameStr() << "_";
1008+
NTD->getStartLoc().printLineAndColumn(OS, SM);
9791009
CurrentFuncLinkage = FormalLinkage::HiddenUnique;
980-
} else {
981-
llvm_unreachable("Unsupported expr");
9821010
}
1011+
} else {
1012+
auto *CE = cast<AbstractClosureExpr>(Root.get<Expr *>());
1013+
CurrentFuncName = SILDeclRef(CE).mangle();
1014+
CurrentFuncLinkage = FormalLinkage::HiddenUnique;
9831015
}
9841016

9851017
PGOFuncName = llvm::getPGOFuncName(
9861018
CurrentFuncName, getEquivalentPGOLinkage(CurrentFuncLinkage),
9871019
CurrentFileName);
9881020

989-
walkForProfiling(Root, Mapper);
1021+
walkTopLevelNodeForProfiling(Root, Mapper);
9901022

9911023
NumRegionCounters = Mapper.NextCounter;
9921024
// TODO: Mapper needs to calculate a function hash as it goes.
9931025
PGOFuncHash = 0x0;
9941026

9951027
if (EmitCoverageMapping) {
9961028
CoverageMapping Coverage(SM);
997-
walkForProfiling(Root, Coverage);
1029+
walkTopLevelNodeForProfiling(Root, Coverage);
9981030
CovMap =
9991031
Coverage.emitSourceRegions(M, CurrentFuncName, PGOFuncName, PGOFuncHash,
10001032
RegionCounterMap, CurrentFileName);
@@ -1012,7 +1044,7 @@ void SILProfiler::assignRegionCounters() {
10121044
}
10131045
PGOMapping pgoMapper(RegionLoadedCounterMap, LoadedCounts,
10141046
RegionCondToParentMap);
1015-
walkForProfiling(Root, pgoMapper);
1047+
walkTopLevelNodeForProfiling(Root, pgoMapper);
10161048
}
10171049
}
10181050

0 commit comments

Comments
 (0)