Skip to content

Commit 1eedefe

Browse files
authored
Merge pull request #61134 from hamishknight/comprehensive-coverage
2 parents 5190d9d + 50367ec commit 1eedefe

File tree

5 files changed

+162
-81
lines changed

5 files changed

+162
-81
lines changed

lib/SIL/IR/SILProfiler.cpp

Lines changed: 90 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -41,63 +41,68 @@ static bool doesClosureHaveBody(AbstractClosureExpr *ACE) {
4141
return false;
4242
}
4343

44-
/// Check whether a root AST node is unmapped, i.e not profiled.
45-
static bool isUnmapped(ASTNode N) {
46-
// Do not map AST nodes with invalid source locations.
44+
/// Check whether a root AST node should be profiled.
45+
static bool shouldProfile(ASTNode N) {
46+
// Do not profile AST nodes with invalid source locations.
4747
if (N.getStartLoc().isInvalid() || N.getEndLoc().isInvalid()) {
4848
LLVM_DEBUG(llvm::dbgs()
4949
<< "Skipping ASTNode: invalid start/end locations\n");
50-
return true;
50+
return false;
5151
}
5252

5353
if (auto *E = N.dyn_cast<Expr *>()) {
5454
if (auto *CE = dyn_cast<AbstractClosureExpr>(E)) {
55-
// Only map closure expressions with bodies.
55+
// Only profile closure expressions with bodies.
5656
if (!doesClosureHaveBody(CE)) {
5757
LLVM_DEBUG(llvm::dbgs() << "Skipping ASTNode: closure without body\n");
58-
return true;
58+
return false;
5959
}
6060

61-
// Don't map implicit closures, unless they're autoclosures.
61+
// Don't profile implicit closures, unless they're autoclosures.
6262
if (!isa<AutoClosureExpr>(CE) && CE->isImplicit()) {
6363
LLVM_DEBUG(llvm::dbgs() << "Skipping ASTNode: implicit closure expr\n");
64-
return true;
64+
return false;
6565
}
6666
}
6767

68-
// Map all other kinds of expressions.
69-
return false;
68+
// Profile all other kinds of expressions.
69+
return true;
7070
}
7171

7272
auto *D = N.get<Decl *>();
7373
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
74-
// Don't map functions without bodies.
74+
// Don't profile functions without bodies.
7575
if (!AFD->hasBody()) {
7676
LLVM_DEBUG(llvm::dbgs() << "Skipping ASTNode: function without body\n");
77-
return true;
77+
return false;
7878
}
7979

80-
// Map implicit getters for lazy variables.
80+
// Profile implicit getters for lazy variables.
8181
if (auto *accessor = dyn_cast<AccessorDecl>(AFD)) {
8282
if (accessor->isImplicit() && accessor->isGetter() &&
8383
accessor->getStorage()->getAttrs().hasAttribute<LazyAttr>()) {
84-
return false;
84+
return true;
8585
}
8686
}
8787
}
8888

8989
// Skip any remaining implicit, or otherwise unsupported decls.
9090
if (D->isImplicit() || isa<EnumCaseDecl>(D)) {
9191
LLVM_DEBUG(llvm::dbgs() << "Skipping ASTNode: implicit/unsupported decl\n");
92-
return true;
92+
return false;
9393
}
9494

95-
return false;
95+
return true;
9696
}
9797

9898
namespace swift {
9999
bool doesASTRequireProfiling(SILModule &M, ASTNode N) {
100-
return M.getOptions().GenerateProfile && !isUnmapped(N);
100+
// If profiling isn't enabled, don't profile anything.
101+
auto &Opts = M.getOptions();
102+
if (Opts.UseProfile.empty() && !Opts.GenerateProfile)
103+
return false;
104+
105+
return shouldProfile(N);
101106
}
102107
} // namespace swift
103108

@@ -152,7 +157,7 @@ static bool canCreateProfilerForAST(ASTNode N, SILDeclRef forDecl) {
152157

153158
SILProfiler *SILProfiler::create(SILModule &M, ASTNode N, SILDeclRef Ref) {
154159
const auto &Opts = M.getOptions();
155-
if (!doesASTRequireProfiling(M, N) && Opts.UseProfile.empty())
160+
if (!doesASTRequireProfiling(M, N))
156161
return nullptr;
157162

158163
if (!canCreateProfilerForAST(N, Ref)) {
@@ -185,44 +190,53 @@ visitFunctionDecl(ASTWalker &Walker, AbstractFunctionDecl *AFD, F Func) {
185190
return ASTWalker::Action::SkipChildren();
186191
}
187192

188-
/// Whether to skip visitation of an expression. If the expression should be
189-
/// skipped, a walker action is returned that determines whether or not the
190-
/// children should also be skipped.
191-
static Optional<ASTWalker::PreWalkResult<Expr *>>
192-
shouldSkipExpr(Expr *E, ASTWalker::ParentTy Parent) {
193+
/// Whether to walk the children of a given expression.
194+
ASTWalker::PreWalkResult<Expr *>
195+
shouldWalkIntoExpr(Expr *E, ASTWalker::ParentTy Parent, SILDeclRef Constant) {
193196
using Action = ASTWalker::Action;
194-
using Result = ASTWalker::PreWalkResult<Expr *>;
195197

196198
// Profiling for closures should be handled separately. Do not visit
197199
// closure expressions twice.
198-
if (isa<AbstractClosureExpr>(E) && !Parent.isNull())
199-
return Result(Action::SkipChildren(E));
200-
201-
// Expressions with no location should be skipped, but we still want to visit
202-
// their children.
203-
if (E->getStartLoc().isInvalid() || E->getEndLoc().isInvalid())
204-
return Result(Action::Continue(E));
200+
if (isa<AbstractClosureExpr>(E)) {
201+
// A non-null parent means we have a closure child, which we will visit
202+
// separately. Even if the parent is null, don't walk into a closure if the
203+
// SILDeclRef is not for a closure, as it could be for a property
204+
// initializer instead.
205+
if (!Parent.isNull() || !Constant || !Constant.getAbstractClosureExpr())
206+
return Action::SkipChildren(E);
207+
}
208+
return Action::Continue(E);
209+
}
205210

206-
return None;
211+
/// Whether to skip visitation of an expression. The children may however still
212+
/// be visited
213+
bool shouldSkipExpr(Expr *E) {
214+
// Expressions with no location should be skipped.
215+
return E->getStartLoc().isInvalid() || E->getEndLoc().isInvalid();
207216
}
208217

209-
/// Whether the children of an unmapped decl should still be walked.
210-
static bool shouldWalkUnmappedDecl(const Decl *D) {
218+
/// Whether the children of a decl that isn't explicitly handled should be
219+
/// walked.
220+
static bool shouldWalkIntoUnhandledDecl(const Decl *D) {
211221
// We want to walk into the initializer for a pattern binding decl. This
212222
// allows us to map LazyInitializerExprs.
213223
return isa<PatternBindingDecl>(D);
214224
}
215225

216226
/// An ASTWalker that maps ASTNodes to profiling counters.
217227
struct MapRegionCounters : public ASTWalker {
228+
/// The SIL function being profiled.
229+
SILDeclRef Constant;
230+
218231
/// The next counter value to assign.
219232
unsigned NextCounter = 0;
220233

221234
/// The map of statements to counters.
222235
llvm::DenseMap<ASTNode, unsigned> &CounterMap;
223236

224-
MapRegionCounters(llvm::DenseMap<ASTNode, unsigned> &CounterMap)
225-
: CounterMap(CounterMap) {}
237+
MapRegionCounters(SILDeclRef Constant,
238+
llvm::DenseMap<ASTNode, unsigned> &CounterMap)
239+
: Constant(Constant), CounterMap(CounterMap) {}
226240

227241
LazyInitializerWalking getLazyInitializerWalkingBehavior() override {
228242
// We want to walk lazy initializers present in the synthesized getter for
@@ -247,15 +261,13 @@ struct MapRegionCounters : public ASTWalker {
247261
}
248262

249263
PreWalkAction walkToDeclPre(Decl *D) override {
250-
if (isUnmapped(D))
251-
return Action::VisitChildrenIf(shouldWalkUnmappedDecl(D));
252-
253264
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
254265
return visitFunctionDecl(*this, AFD, [&] { mapRegion(AFD->getBody()); });
255266
} else if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D)) {
256267
mapRegion(TLCD->getBody());
268+
return Action::Continue();
257269
}
258-
return Action::Continue();
270+
return Action::VisitChildrenIf(shouldWalkIntoUnhandledDecl(D));
259271
}
260272

261273
PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
@@ -283,8 +295,8 @@ struct MapRegionCounters : public ASTWalker {
283295
}
284296

285297
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
286-
if (auto SkipAction = shouldSkipExpr(E, Parent))
287-
return *SkipAction;
298+
if (shouldSkipExpr(E))
299+
return shouldWalkIntoExpr(E, Parent, Constant);
288300

289301
// If AST visitation begins with an expression, the counter map must be
290302
// empty. Set up a counter for the root.
@@ -300,7 +312,7 @@ struct MapRegionCounters : public ASTWalker {
300312
if (isa<LazyInitializerExpr>(E))
301313
mapRegion(E);
302314

303-
return Action::Continue(E);
315+
return shouldWalkIntoExpr(E, Parent, Constant);
304316
}
305317
};
306318

@@ -520,6 +532,9 @@ class SourceMappingRegion {
520532
/// CoverageMapping walker to recompute the correct counter information
521533
/// for this walker.
522534
struct PGOMapping : public ASTWalker {
535+
/// The SIL function being profiled.
536+
SILDeclRef Constant;
537+
523538
/// The counter indices for AST nodes.
524539
const llvm::DenseMap<ASTNode, unsigned> &CounterMap;
525540

@@ -530,11 +545,12 @@ struct PGOMapping : public ASTWalker {
530545
llvm::DenseMap<ASTNode, ProfileCounter> &LoadedCounterMap;
531546
llvm::DenseMap<ASTNode, ASTNode> &CondToParentMap;
532547

533-
PGOMapping(const llvm::DenseMap<ASTNode, unsigned> &CounterMap,
548+
PGOMapping(SILDeclRef Constant,
549+
const llvm::DenseMap<ASTNode, unsigned> &CounterMap,
534550
const llvm::InstrProfRecord &LoadedCounts,
535551
llvm::DenseMap<ASTNode, ProfileCounter> &LoadedCounterMap,
536552
llvm::DenseMap<ASTNode, ASTNode> &RegionCondToParentMap)
537-
: CounterMap(CounterMap), LoadedCounts(LoadedCounts),
553+
: Constant(Constant), CounterMap(CounterMap), LoadedCounts(LoadedCounts),
538554
LoadedCounterMap(LoadedCounterMap),
539555
CondToParentMap(RegionCondToParentMap) {}
540556

@@ -596,17 +612,16 @@ struct PGOMapping : public ASTWalker {
596612
}
597613

598614
PreWalkAction walkToDeclPre(Decl *D) override {
599-
if (isUnmapped(D))
600-
return Action::VisitChildrenIf(shouldWalkUnmappedDecl(D));
601615
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
602616
return visitFunctionDecl(*this, AFD, [&] {
603617
setKnownExecutionCount(AFD->getBody());
604618
});
605619
}
606-
if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D))
620+
if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D)) {
607621
setKnownExecutionCount(TLCD->getBody());
608-
609-
return Action::Continue();
622+
return Action::Continue();
623+
}
624+
return Action::VisitChildrenIf(shouldWalkIntoUnhandledDecl(D));
610625
}
611626

612627
LazyInitializerWalking getLazyInitializerWalkingBehavior() override {
@@ -671,8 +686,8 @@ struct PGOMapping : public ASTWalker {
671686
}
672687

673688
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
674-
if (auto SkipAction = shouldSkipExpr(E, Parent))
675-
return *SkipAction;
689+
if (shouldSkipExpr(E))
690+
return shouldWalkIntoExpr(E, Parent, Constant);
676691

677692
unsigned parent = getParentCounter();
678693

@@ -701,7 +716,7 @@ struct PGOMapping : public ASTWalker {
701716
if (isa<LazyInitializerExpr>(E))
702717
setKnownExecutionCount(E);
703718

704-
return Action::Continue(E);
719+
return shouldWalkIntoExpr(E, Parent, Constant);
705720
}
706721
};
707722

@@ -712,6 +727,9 @@ struct CoverageMapping : public ASTWalker {
712727
private:
713728
const SourceManager &SM;
714729

730+
/// The SIL function being profiled.
731+
SILDeclRef Constant;
732+
715733
/// Storage for counter expressions.
716734
std::forward_list<CounterExpr> Exprs;
717735

@@ -935,7 +953,8 @@ struct CoverageMapping : public ASTWalker {
935953
}
936954

937955
public:
938-
CoverageMapping(const SourceManager &SM) : SM(SM) {}
956+
CoverageMapping(const SourceManager &SM, SILDeclRef Constant)
957+
: SM(SM), Constant(Constant) {}
939958

940959
LazyInitializerWalking getLazyInitializerWalkingBehavior() override {
941960
// We want to walk lazy initializers present in the synthesized getter for
@@ -969,18 +988,16 @@ struct CoverageMapping : public ASTWalker {
969988
}
970989

971990
PreWalkAction walkToDeclPre(Decl *D) override {
972-
if (isUnmapped(D))
973-
return Action::VisitChildrenIf(shouldWalkUnmappedDecl(D));
974-
975991
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
976992
return visitFunctionDecl(*this, AFD, [&] {
977993
assignCounter(AFD->getBody());
978994
});
979995
} else if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D)) {
980996
assignCounter(TLCD->getBody());
981997
ImplicitTopLevelBody = TLCD->getBody();
998+
return Action::Continue();
982999
}
983-
return Action::Continue();
1000+
return Action::VisitChildrenIf(shouldWalkIntoUnhandledDecl(D));
9841001
}
9851002

9861003
PostWalkAction walkToDeclPost(Decl *D) override {
@@ -1176,8 +1193,8 @@ struct CoverageMapping : public ASTWalker {
11761193
}
11771194

11781195
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
1179-
if (auto SkipAction = shouldSkipExpr(E, Parent))
1180-
return *SkipAction;
1196+
if (shouldSkipExpr(E))
1197+
return shouldWalkIntoExpr(E, Parent, Constant);
11811198

11821199
// If we're in an 'incomplete' region, update it to include this node. This
11831200
// ensures we only create the region if needed.
@@ -1205,11 +1222,19 @@ struct CoverageMapping : public ASTWalker {
12051222
assignCounter(IE->getElseExpr(),
12061223
CounterExpr::Sub(getCurrentCounter(), ThenCounter));
12071224
}
1208-
return Action::Continue(E);
1225+
auto WalkResult = shouldWalkIntoExpr(E, Parent, Constant);
1226+
if (WalkResult.Action.Action == PreWalkAction::SkipChildren) {
1227+
// We need to manually pop the region here as the ASTWalker won't call
1228+
// the post-visitation.
1229+
// FIXME: The ASTWalker should do a post-visit.
1230+
if (hasCounter(E))
1231+
popRegions(E);
1232+
}
1233+
return WalkResult;
12091234
}
12101235

12111236
PostWalkResult<Expr *> walkToExprPost(Expr *E) override {
1212-
if (shouldSkipExpr(E, Parent))
1237+
if (shouldSkipExpr(E))
12131238
return Action::Continue(E);
12141239

12151240
if (hasCounter(E))
@@ -1248,7 +1273,7 @@ void SILProfiler::assignRegionCounters() {
12481273

12491274
CurrentFileName = getCurrentFileName(Root, forDecl);
12501275

1251-
MapRegionCounters Mapper(RegionCounterMap);
1276+
MapRegionCounters Mapper(forDecl, RegionCounterMap);
12521277

12531278
std::string CurrentFuncName;
12541279
FormalLinkage CurrentFuncLinkage;
@@ -1284,7 +1309,7 @@ void SILProfiler::assignRegionCounters() {
12841309
PGOFuncHash = 0x0;
12851310

12861311
if (EmitCoverageMapping) {
1287-
CoverageMapping Coverage(SM);
1312+
CoverageMapping Coverage(SM, forDecl);
12881313
Root.walk(Coverage);
12891314
CovMap =
12901315
Coverage.emitSourceRegions(M, CurrentFuncName, PGOFuncName, PGOFuncHash,
@@ -1301,7 +1326,7 @@ void SILProfiler::assignRegionCounters() {
13011326
llvm::dbgs() << PGOFuncName << "\n";
13021327
return;
13031328
}
1304-
PGOMapping pgoMapper(RegionCounterMap, LoadedCounts.get(),
1329+
PGOMapping pgoMapper(forDecl, RegionCounterMap, LoadedCounts.get(),
13051330
RegionLoadedCounterMap, RegionCondToParentMap);
13061331
Root.walk(pgoMapper);
13071332
}

test/Profiler/coverage_closures.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -emit-sil -module-name coverage_closures %s | %FileCheck %s
1+
// RUN: %target-swift-frontend -emit-sil -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -module-name coverage_closures %s | %FileCheck %s
22
// RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -emit-ir %s
33

44
func bar(arr: [(Int32) -> Int32]) {
@@ -55,3 +55,18 @@ class C2 {
5555
}
5656
}
5757
}
58+
59+
// https://github.com/apple/swift/issues/61129 – Make sure we don't emit
60+
// duplicate coverage for closure expressions as property initializers.
61+
struct S {
62+
// CHECK-LABEL: sil_coverage_map {{.*}} "$s17coverage_closures1SV1xSiycvpfi" {{.*}} // variable initialization expression of coverage_closures.S.x : () -> Swift.Int
63+
// CHECK-NEXT: [[@LINE+8]]:11 -> [[@LINE+8]]:30 : 0
64+
// CHECK-NEXT: }
65+
66+
// CHECK-LABEL: sil_coverage_map {{.*}} "$s17coverage_closures1SV1xSiycvpfiSiycfU_" {{.*}} // closure #1 () -> Swift.Int in variable initialization expression of coverage_closures.S.x : () -> Swift.Int
67+
// CHECK-NEXT: [[@LINE+4]]:24 -> [[@LINE+4]]:25 : 1
68+
// CHECK-NEXT: [[@LINE+3]]:28 -> [[@LINE+3]]:29 : (0 - 1)
69+
// CHECK-NEXT: [[@LINE+2]]:11 -> [[@LINE+2]]:30 : 0
70+
// CHECK-NEXT: }
71+
var x = {.random() ? 1 : 2}
72+
}

0 commit comments

Comments
 (0)