26
26
27
27
using namespace swift ;
28
28
29
- static bool isClosureWithBody (AbstractClosureExpr *ACE) {
29
+ // / Check if a closure has a body.
30
+ static bool doesClosureHaveBody (AbstractClosureExpr *ACE) {
30
31
if (auto *CE = dyn_cast<ClosureExpr>(ACE))
31
32
return CE->getBody ();
32
33
if (auto *autoCE = dyn_cast<AutoClosureExpr>(ACE))
33
34
return autoCE->getBody ();
34
35
return false ;
35
36
}
36
37
38
+ // / Check whether a root AST node is unmapped, i.e not profiled.
37
39
static bool isUnmapped (ASTNode N) {
38
40
if (auto *E = N.dyn_cast <Expr *>()) {
39
41
auto *CE = dyn_cast<AbstractClosureExpr>(E);
40
- return !CE || (!isa<AutoClosureExpr>(CE) && CE->isImplicit ()) ||
41
- !isClosureWithBody (CE);
42
+
43
+ // Only map closure expressions with bodies.
44
+ if (!CE || !doesClosureHaveBody (CE))
45
+ return true ;
46
+
47
+ // Don't map implicit closures, unless they're autoclosures.
48
+ if (!isa<AutoClosureExpr>(CE) && CE->isImplicit ())
49
+ return true ;
50
+
51
+ return false ;
42
52
}
43
53
44
54
auto *D = N.get <Decl *>();
45
55
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
56
+ // Don't map functions without bodies.
46
57
if (!AFD->getBody ())
47
58
return true ;
48
59
60
+ // Map all *structors, even if they are implicit.
61
+ if (isa<ConstructorDecl>(D) || isa<DestructorDecl>(D))
62
+ return false ;
63
+
64
+ // Map implicit getters.
49
65
if (auto *accessor = dyn_cast<AccessorDecl>(AFD))
50
66
if (accessor->isImplicit () && accessor->isGetter ())
51
67
return false ;
52
68
}
53
69
54
- if (isa<ConstructorDecl>(D) || isa<DestructorDecl>(D))
55
- return false ;
56
-
57
- return D->isImplicit () || isa<EnumCaseDecl>(D);
58
- }
59
-
60
- // / A simple heuristic to determine whether \p E contains a definition of a
61
- // / closure. This is not complete, but it suffices to cheaply filter away some
62
- // / redundant coverage mappings.
63
- static bool containsClosure (Expr *E) {
64
- Expr *candidateExpr = E;
65
- if (auto *ce = dyn_cast<CallExpr>(E))
66
- candidateExpr = ce->getDirectCallee ();
67
- return dyn_cast_or_null<AbstractClosureExpr>(candidateExpr);
68
- }
69
-
70
- // / Walk the non-static initializers in \p PBD.
71
- static void walkPatternForProfiling (PatternBindingDecl *PBD, ASTWalker &Walker,
72
- bool AllowClosures = true ) {
73
- if (PBD && !PBD->isStatic ())
74
- for (auto E : PBD->getPatternList ())
75
- if (E.getInit ())
76
- if (AllowClosures || !containsClosure (E.getInit ()))
77
- E.getInit ()->walk (Walker);
78
- }
79
-
80
- // / Walk the AST of \c Root and related nodes that are relevant for profiling.
81
- static void walkFunctionForProfiling (AbstractFunctionDecl *Root,
82
- ASTWalker &Walker) {
83
- Root->walk (Walker);
84
-
85
- // We treat non-closure member initializers as part of the constructor for
86
- // profiling.
87
- if (auto *CD = dyn_cast<ConstructorDecl>(Root)) {
88
- auto *NominalType =
89
- CD->getDeclContext ()->getAsNominalTypeOrNominalTypeExtensionContext ();
90
- for (auto *Member : NominalType->getMembers ()) {
91
- // Find pattern binding declarations that have initializers.
92
- if (auto *PBD = dyn_cast<PatternBindingDecl>(Member))
93
- walkPatternForProfiling (PBD, Walker, /* AllowClosures=*/ false );
94
- }
95
- }
96
- }
70
+ // Skip any remaining implicit, or otherwise unsupported decls.
71
+ if (D->isImplicit () || isa<EnumCaseDecl>(D))
72
+ return true ;
97
73
98
- // / Walk \p D for profiling.
99
- static void walkForProfiling (ASTNode N, ASTWalker &Walker) {
100
- if (auto *D = N.dyn_cast <Decl *>()) {
101
- if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D))
102
- walkFunctionForProfiling (AFD, Walker);
103
- else if (auto *PBD = dyn_cast<PatternBindingDecl>(D))
104
- walkPatternForProfiling (PBD, Walker);
105
- else if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D))
106
- TLCD->walk (Walker);
107
- } else if (auto *E = N.dyn_cast <Expr *>()) {
108
- cast<AbstractClosureExpr>(E)->walk (Walker);
109
- }
74
+ return false ;
110
75
}
111
76
112
77
namespace swift {
@@ -123,27 +88,43 @@ static bool hasASTBeenTypeChecked(ASTNode N) {
123
88
return !SF || SF->ASTStage >= SourceFile::TypeChecked;
124
89
}
125
90
91
+ // / Check whether a mapped AST node requires a new profiler.
92
+ static bool canCreateProfilerForAST (ASTNode N) {
93
+ assert (hasASTBeenTypeChecked (N) && " Cannot use this AST for profiling" );
94
+
95
+ if (auto *D = N.dyn_cast <Decl *>()) {
96
+ // Any mapped function may be profiled. There's an exception for
97
+ // constructors because all of the constructors for a type share a single
98
+ // profiler.
99
+ if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D))
100
+ return !isa<ConstructorDecl>(AFD);
101
+
102
+ if (isa<TopLevelCodeDecl>(D))
103
+ return true ;
104
+
105
+ if (isa<NominalTypeDecl>(D))
106
+ return true ;
107
+ } else {
108
+ auto *E = N.get <Expr *>();
109
+ if (isa<AbstractClosureExpr>(E))
110
+ return true ;
111
+ }
112
+ return false ;
113
+ }
114
+
126
115
SILProfiler *SILProfiler::create (SILModule &M, ForDefinition_t forDefinition,
127
116
ASTNode N) {
128
117
// Avoid generating profiling state for declarations.
129
118
if (!forDefinition)
130
119
return nullptr ;
131
120
132
- assert (hasASTBeenTypeChecked (N) && " Cannot use this AST for code coverage" );
133
-
134
- if (auto *D = N.dyn_cast <Decl *>()) {
135
- assert (isa<AbstractFunctionDecl>(D) ||
136
- isa<TopLevelCodeDecl>(D) && " Cannot create profiler" );
137
- } else if (auto *E = N.dyn_cast <Expr *>()) {
138
- assert (isa<AbstractClosureExpr>(E) && " Cannot create profiler" );
139
- } else {
140
- llvm_unreachable (" Invalid AST node for profiling" );
141
- }
142
-
143
121
const auto &Opts = M.getOptions ();
144
122
if (!doesASTRequireProfiling (M, N) && Opts.UseProfile .empty ())
145
123
return nullptr ;
146
124
125
+ if (!canCreateProfilerForAST (N))
126
+ llvm_unreachable (" Invalid AST node for profiling" );
127
+
147
128
auto *Buf = M.allocate <SILProfiler>(1 );
148
129
auto *SP = ::new (Buf) SILProfiler (M, N, Opts.EmitProfileCoverageMapping );
149
130
SP->assignRegionCounters ();
@@ -152,6 +133,15 @@ SILProfiler *SILProfiler::create(SILModule &M, ForDefinition_t forDefinition,
152
133
153
134
namespace {
154
135
136
+ // / Walk the non-static initializers in \p PBD.
137
+ static void walkPatternForProfiling (PatternBindingDecl *PBD,
138
+ ASTWalker &Walker) {
139
+ if (PBD && !PBD->isStatic ())
140
+ for (auto E : PBD->getPatternList ())
141
+ if (E.getInit ())
142
+ E.getInit ()->walk (Walker);
143
+ }
144
+
155
145
// / Special logic for handling closure visitation.
156
146
// /
157
147
// / To prevent a closure from being mapped twice, avoid recursively walking
@@ -170,25 +160,35 @@ std::pair<bool, Expr *> visitClosureExpr(ASTWalker &Walker,
170
160
// / An ASTWalker that maps ASTNodes to profiling counters.
171
161
struct MapRegionCounters : public ASTWalker {
172
162
// / The next counter value to assign.
173
- unsigned NextCounter;
163
+ unsigned NextCounter = 0 ;
174
164
175
165
// / The map of statements to counters.
176
166
llvm::DenseMap<ASTNode, unsigned > &CounterMap;
177
167
168
+ // / A flag indicating whether we're walking a nominal type.
169
+ bool WithinNominalType = false ;
170
+
178
171
MapRegionCounters (llvm::DenseMap<ASTNode, unsigned > &CounterMap)
179
- : NextCounter( 0 ), CounterMap(CounterMap) {}
172
+ : CounterMap(CounterMap) {}
180
173
181
174
bool walkToDeclPre (Decl *D) override {
182
175
if (isUnmapped (D))
183
176
return false ;
177
+
184
178
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
185
- bool FirstDecl = Parent.isNull ();
186
- if (FirstDecl)
179
+ // Don't map a nested function unless it's a constructor.
180
+ bool continueWalk = Parent.isNull () || isa<ConstructorDecl>(AFD);
181
+ if (continueWalk)
187
182
CounterMap[AFD->getBody ()] = NextCounter++;
188
- return FirstDecl;
189
- }
190
- if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D))
183
+ return continueWalk;
184
+ } else if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D)) {
191
185
CounterMap[TLCD->getBody ()] = NextCounter++;
186
+ } else if (isa<NominalTypeDecl>(D)) {
187
+ bool continueWalk = Parent.isNull ();
188
+ if (continueWalk)
189
+ WithinNominalType = true ;
190
+ return continueWalk;
191
+ }
192
192
return true ;
193
193
}
194
194
@@ -560,7 +560,11 @@ struct CoverageMapping : public ASTWalker {
560
560
// / \brief A stack of active repeat-while loops.
561
561
std::vector<RepeatWhileStmt *> RepeatWhileStack;
562
562
563
- CounterExpr *ExitCounter;
563
+ CounterExpr *ExitCounter = nullptr ;
564
+
565
+ Stmt *ImplicitTopLevelBody = nullptr ;
566
+
567
+ NominalTypeDecl *ParentNominalType = nullptr ;
564
568
565
569
// / \brief Return true if \c Node has an associated counter.
566
570
bool hasCounter (ASTNode Node) { return CounterMap.count (Node); }
@@ -762,19 +766,40 @@ struct CoverageMapping : public ASTWalker {
762
766
bool walkToDeclPre (Decl *D) override {
763
767
if (isUnmapped (D))
764
768
return false ;
769
+
765
770
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
766
- bool FirstDecl = Parent.isNull ();
767
- if (FirstDecl)
768
- assignCounter (AFD->getBody ());
769
- return FirstDecl;
770
- }
771
- else if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D))
771
+ // Don't map a nested function unless it's a constructor.
772
+ bool continueWalk = Parent.isNull () || isa<ConstructorDecl>(AFD);
773
+ if (continueWalk) {
774
+ CounterExpr &funcCounter = assignCounter (AFD->getBody ());
775
+
776
+ if (isa<ConstructorDecl>(AFD))
777
+ addToCounter (ParentNominalType, funcCounter);
778
+ }
779
+ return continueWalk;
780
+ } else if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D)) {
772
781
assignCounter (TLCD->getBody ());
782
+ ImplicitTopLevelBody = TLCD->getBody ();
783
+ } else if (auto *NTD = dyn_cast<NominalTypeDecl>(D)) {
784
+ bool continueWalk = Parent.isNull ();
785
+ if (continueWalk) {
786
+ ParentNominalType = NTD;
787
+ assignCounter (NTD, CounterExpr::Zero ());
788
+ pushRegion (NTD);
789
+ }
790
+ return continueWalk;
791
+ }
792
+ return true ;
793
+ }
794
+
795
+ bool walkToDeclPost (Decl *D) override {
796
+ if (isa<TopLevelCodeDecl>(D))
797
+ ImplicitTopLevelBody = nullptr ;
773
798
return true ;
774
799
}
775
800
776
801
std::pair<bool , Stmt *> walkToStmtPre (Stmt *S) override {
777
- if (S->isImplicit ())
802
+ if (S->isImplicit () && S != ImplicitTopLevelBody )
778
803
return {true , S};
779
804
780
805
if (!RegionStack.empty ())
@@ -835,7 +860,7 @@ struct CoverageMapping : public ASTWalker {
835
860
}
836
861
837
862
Stmt *walkToStmtPost (Stmt *S) override {
838
- if (S->isImplicit ())
863
+ if (S->isImplicit () && S != ImplicitTopLevelBody )
839
864
return S;
840
865
841
866
if (isa<BraceStmt>(S)) {
@@ -907,11 +932,8 @@ struct CoverageMapping : public ASTWalker {
907
932
return Result;
908
933
} else if (auto *IE = dyn_cast<IfExpr>(E)) {
909
934
CounterExpr &ThenCounter = assignCounter (IE->getThenExpr ());
910
- if (RegionStack.empty ())
911
- assignCounter (IE->getElseExpr ());
912
- else
913
- assignCounter (IE->getElseExpr (),
914
- CounterExpr::Sub (getCurrentCounter (), ThenCounter));
935
+ assignCounter (IE->getElseExpr (),
936
+ CounterExpr::Sub (getCurrentCounter (), ThenCounter));
915
937
}
916
938
917
939
if (hasCounter (E))
@@ -951,6 +973,16 @@ static StringRef getCurrentFileName(ASTNode Root) {
951
973
return {};
952
974
}
953
975
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
+
954
986
void SILProfiler::assignRegionCounters () {
955
987
const auto &SM = M.getASTContext ().SourceMgr ;
956
988
@@ -970,31 +1002,31 @@ void SILProfiler::assignRegionCounters() {
970
1002
TLCD->getStartLoc ().printLineAndColumn (OS, SM);
971
1003
CurrentFuncLinkage = FormalLinkage::HiddenUnique;
972
1004
} 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);
979
1009
CurrentFuncLinkage = FormalLinkage::HiddenUnique;
980
- } else {
981
- llvm_unreachable (" Unsupported expr" );
982
1010
}
1011
+ } else {
1012
+ auto *CE = cast<AbstractClosureExpr>(Root.get <Expr *>());
1013
+ CurrentFuncName = SILDeclRef (CE).mangle ();
1014
+ CurrentFuncLinkage = FormalLinkage::HiddenUnique;
983
1015
}
984
1016
985
1017
PGOFuncName = llvm::getPGOFuncName (
986
1018
CurrentFuncName, getEquivalentPGOLinkage (CurrentFuncLinkage),
987
1019
CurrentFileName);
988
1020
989
- walkForProfiling (Root, Mapper);
1021
+ walkTopLevelNodeForProfiling (Root, Mapper);
990
1022
991
1023
NumRegionCounters = Mapper.NextCounter ;
992
1024
// TODO: Mapper needs to calculate a function hash as it goes.
993
1025
PGOFuncHash = 0x0 ;
994
1026
995
1027
if (EmitCoverageMapping) {
996
1028
CoverageMapping Coverage (SM);
997
- walkForProfiling (Root, Coverage);
1029
+ walkTopLevelNodeForProfiling (Root, Coverage);
998
1030
CovMap =
999
1031
Coverage.emitSourceRegions (M, CurrentFuncName, PGOFuncName, PGOFuncHash,
1000
1032
RegionCounterMap, CurrentFileName);
@@ -1012,7 +1044,7 @@ void SILProfiler::assignRegionCounters() {
1012
1044
}
1013
1045
PGOMapping pgoMapper (RegionLoadedCounterMap, LoadedCounts,
1014
1046
RegionCondToParentMap);
1015
- walkForProfiling (Root, pgoMapper);
1047
+ walkTopLevelNodeForProfiling (Root, pgoMapper);
1016
1048
}
1017
1049
}
1018
1050
0 commit comments