25
25
26
26
using namespace swift ;
27
27
28
- static bool isClosureWithBody (AbstractClosureExpr *ACE) {
28
+ // / Check if a closure has a body.
29
+ static bool doesClosureHaveBody (AbstractClosureExpr *ACE) {
29
30
if (auto *CE = dyn_cast<ClosureExpr>(ACE))
30
31
return CE->getBody ();
31
32
if (auto *autoCE = dyn_cast<AutoClosureExpr>(ACE))
32
33
return autoCE->getBody ();
33
34
return false ;
34
35
}
35
36
37
+ // / Check whether a root AST node is unmapped, i.e not profiled.
36
38
static bool isUnmapped (ASTNode N) {
37
39
if (auto *E = N.dyn_cast <Expr *>()) {
38
40
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 ;
41
51
}
42
52
43
53
auto *D = N.get <Decl *>();
44
54
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
55
+ // Don't map functions without bodies.
45
56
if (!AFD->getBody ())
46
57
return true ;
47
58
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.
48
64
if (auto *accessor = dyn_cast<AccessorDecl>(AFD))
49
65
if (accessor->isImplicit () && accessor->isGetter ())
50
66
return false ;
51
67
}
52
68
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 ;
96
72
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 ;
109
74
}
110
75
111
76
namespace swift {
@@ -122,27 +87,43 @@ static bool hasASTBeenTypeChecked(ASTNode N) {
122
87
return !SF || SF->ASTStage >= SourceFile::TypeChecked;
123
88
}
124
89
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
+
125
114
SILProfiler *SILProfiler::create (SILModule &M, ForDefinition_t forDefinition,
126
115
ASTNode N) {
127
116
// Avoid generating profiling state for declarations.
128
117
if (!forDefinition)
129
118
return nullptr ;
130
119
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
-
142
120
const auto &Opts = M.getOptions ();
143
121
if (!doesASTRequireProfiling (M, N) && Opts.UseProfile .empty ())
144
122
return nullptr ;
145
123
124
+ if (!canCreateProfilerForAST (N))
125
+ llvm_unreachable (" Invalid AST node for profiling" );
126
+
146
127
auto *Buf = M.allocate <SILProfiler>(1 );
147
128
auto *SP = ::new (Buf) SILProfiler (M, N, Opts.EmitProfileCoverageMapping );
148
129
SP->assignRegionCounters ();
@@ -151,6 +132,15 @@ SILProfiler *SILProfiler::create(SILModule &M, ForDefinition_t forDefinition,
151
132
152
133
namespace {
153
134
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
+
154
144
// / Special logic for handling closure visitation.
155
145
// /
156
146
// / To prevent a closure from being mapped twice, avoid recursively walking
@@ -169,25 +159,35 @@ std::pair<bool, Expr *> visitClosureExpr(ASTWalker &Walker,
169
159
// / An ASTWalker that maps ASTNodes to profiling counters.
170
160
struct MapRegionCounters : public ASTWalker {
171
161
// / The next counter value to assign.
172
- unsigned NextCounter;
162
+ unsigned NextCounter = 0 ;
173
163
174
164
// / The map of statements to counters.
175
165
llvm::DenseMap<ASTNode, unsigned > &CounterMap;
176
166
167
+ // / A flag indicating whether we're walking a nominal type.
168
+ bool WithinNominalType = false ;
169
+
177
170
MapRegionCounters (llvm::DenseMap<ASTNode, unsigned > &CounterMap)
178
- : NextCounter( 0 ), CounterMap(CounterMap) {}
171
+ : CounterMap(CounterMap) {}
179
172
180
173
bool walkToDeclPre (Decl *D) override {
181
174
if (isUnmapped (D))
182
175
return false ;
176
+
183
177
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)
186
181
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)) {
190
184
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
+ }
191
191
return true ;
192
192
}
193
193
@@ -559,7 +559,11 @@ struct CoverageMapping : public ASTWalker {
559
559
// / \brief A stack of active repeat-while loops.
560
560
std::vector<RepeatWhileStmt *> RepeatWhileStack;
561
561
562
- CounterExpr *ExitCounter;
562
+ CounterExpr *ExitCounter = nullptr ;
563
+
564
+ Stmt *ImplicitTopLevelBody = nullptr ;
565
+
566
+ NominalTypeDecl *ParentNominalType = nullptr ;
563
567
564
568
// / \brief Return true if \c Node has an associated counter.
565
569
bool hasCounter (ASTNode Node) { return CounterMap.count (Node); }
@@ -761,19 +765,40 @@ struct CoverageMapping : public ASTWalker {
761
765
bool walkToDeclPre (Decl *D) override {
762
766
if (isUnmapped (D))
763
767
return false ;
768
+
764
769
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)) {
771
780
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 ;
772
797
return true ;
773
798
}
774
799
775
800
std::pair<bool , Stmt *> walkToStmtPre (Stmt *S) override {
776
- if (S->isImplicit ())
801
+ if (S->isImplicit () && S != ImplicitTopLevelBody )
777
802
return {true , S};
778
803
779
804
if (!RegionStack.empty ())
@@ -834,7 +859,7 @@ struct CoverageMapping : public ASTWalker {
834
859
}
835
860
836
861
Stmt *walkToStmtPost (Stmt *S) override {
837
- if (S->isImplicit ())
862
+ if (S->isImplicit () && S != ImplicitTopLevelBody )
838
863
return S;
839
864
840
865
if (isa<BraceStmt>(S)) {
@@ -906,11 +931,8 @@ struct CoverageMapping : public ASTWalker {
906
931
return Result;
907
932
} else if (auto *IE = dyn_cast<IfExpr>(E)) {
908
933
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));
914
936
}
915
937
916
938
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