Skip to content

Commit 50367ec

Browse files
committed
[Profiler] Don't walk into property initializer closures
These don't have a parent, but still shouldn't be walked into. Be sure to check that the SILDeclRef is for a closure. rdar://99963912
1 parent 9bb87a5 commit 50367ec

File tree

2 files changed

+72
-33
lines changed

2 files changed

+72
-33
lines changed

lib/SIL/IR/SILProfiler.cpp

Lines changed: 56 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -190,25 +190,29 @@ visitFunctionDecl(ASTWalker &Walker, AbstractFunctionDecl *AFD, F Func) {
190190
return ASTWalker::Action::SkipChildren();
191191
}
192192

193-
/// Whether to skip visitation of an expression. If the expression should be
194-
/// skipped, a walker action is returned that determines whether or not the
195-
/// children should also be skipped.
196-
static Optional<ASTWalker::PreWalkResult<Expr *>>
197-
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) {
198196
using Action = ASTWalker::Action;
199-
using Result = ASTWalker::PreWalkResult<Expr *>;
200197

201198
// Profiling for closures should be handled separately. Do not visit
202199
// closure expressions twice.
203-
if (isa<AbstractClosureExpr>(E) && !Parent.isNull())
204-
return Result(Action::SkipChildren(E));
205-
206-
// Expressions with no location should be skipped, but we still want to visit
207-
// their children.
208-
if (E->getStartLoc().isInvalid() || E->getEndLoc().isInvalid())
209-
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+
}
210210

211-
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();
212216
}
213217

214218
/// Whether the children of a decl that isn't explicitly handled should be
@@ -221,14 +225,18 @@ static bool shouldWalkIntoUnhandledDecl(const Decl *D) {
221225

222226
/// An ASTWalker that maps ASTNodes to profiling counters.
223227
struct MapRegionCounters : public ASTWalker {
228+
/// The SIL function being profiled.
229+
SILDeclRef Constant;
230+
224231
/// The next counter value to assign.
225232
unsigned NextCounter = 0;
226233

227234
/// The map of statements to counters.
228235
llvm::DenseMap<ASTNode, unsigned> &CounterMap;
229236

230-
MapRegionCounters(llvm::DenseMap<ASTNode, unsigned> &CounterMap)
231-
: CounterMap(CounterMap) {}
237+
MapRegionCounters(SILDeclRef Constant,
238+
llvm::DenseMap<ASTNode, unsigned> &CounterMap)
239+
: Constant(Constant), CounterMap(CounterMap) {}
232240

233241
LazyInitializerWalking getLazyInitializerWalkingBehavior() override {
234242
// We want to walk lazy initializers present in the synthesized getter for
@@ -287,8 +295,8 @@ struct MapRegionCounters : public ASTWalker {
287295
}
288296

289297
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
290-
if (auto SkipAction = shouldSkipExpr(E, Parent))
291-
return *SkipAction;
298+
if (shouldSkipExpr(E))
299+
return shouldWalkIntoExpr(E, Parent, Constant);
292300

293301
// If AST visitation begins with an expression, the counter map must be
294302
// empty. Set up a counter for the root.
@@ -304,7 +312,7 @@ struct MapRegionCounters : public ASTWalker {
304312
if (isa<LazyInitializerExpr>(E))
305313
mapRegion(E);
306314

307-
return Action::Continue(E);
315+
return shouldWalkIntoExpr(E, Parent, Constant);
308316
}
309317
};
310318

@@ -524,6 +532,9 @@ class SourceMappingRegion {
524532
/// CoverageMapping walker to recompute the correct counter information
525533
/// for this walker.
526534
struct PGOMapping : public ASTWalker {
535+
/// The SIL function being profiled.
536+
SILDeclRef Constant;
537+
527538
/// The counter indices for AST nodes.
528539
const llvm::DenseMap<ASTNode, unsigned> &CounterMap;
529540

@@ -534,11 +545,12 @@ struct PGOMapping : public ASTWalker {
534545
llvm::DenseMap<ASTNode, ProfileCounter> &LoadedCounterMap;
535546
llvm::DenseMap<ASTNode, ASTNode> &CondToParentMap;
536547

537-
PGOMapping(const llvm::DenseMap<ASTNode, unsigned> &CounterMap,
548+
PGOMapping(SILDeclRef Constant,
549+
const llvm::DenseMap<ASTNode, unsigned> &CounterMap,
538550
const llvm::InstrProfRecord &LoadedCounts,
539551
llvm::DenseMap<ASTNode, ProfileCounter> &LoadedCounterMap,
540552
llvm::DenseMap<ASTNode, ASTNode> &RegionCondToParentMap)
541-
: CounterMap(CounterMap), LoadedCounts(LoadedCounts),
553+
: Constant(Constant), CounterMap(CounterMap), LoadedCounts(LoadedCounts),
542554
LoadedCounterMap(LoadedCounterMap),
543555
CondToParentMap(RegionCondToParentMap) {}
544556

@@ -674,8 +686,8 @@ struct PGOMapping : public ASTWalker {
674686
}
675687

676688
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
677-
if (auto SkipAction = shouldSkipExpr(E, Parent))
678-
return *SkipAction;
689+
if (shouldSkipExpr(E))
690+
return shouldWalkIntoExpr(E, Parent, Constant);
679691

680692
unsigned parent = getParentCounter();
681693

@@ -704,7 +716,7 @@ struct PGOMapping : public ASTWalker {
704716
if (isa<LazyInitializerExpr>(E))
705717
setKnownExecutionCount(E);
706718

707-
return Action::Continue(E);
719+
return shouldWalkIntoExpr(E, Parent, Constant);
708720
}
709721
};
710722

@@ -715,6 +727,9 @@ struct CoverageMapping : public ASTWalker {
715727
private:
716728
const SourceManager &SM;
717729

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

@@ -938,7 +953,8 @@ struct CoverageMapping : public ASTWalker {
938953
}
939954

940955
public:
941-
CoverageMapping(const SourceManager &SM) : SM(SM) {}
956+
CoverageMapping(const SourceManager &SM, SILDeclRef Constant)
957+
: SM(SM), Constant(Constant) {}
942958

943959
LazyInitializerWalking getLazyInitializerWalkingBehavior() override {
944960
// We want to walk lazy initializers present in the synthesized getter for
@@ -1177,8 +1193,8 @@ struct CoverageMapping : public ASTWalker {
11771193
}
11781194

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

11831199
// If we're in an 'incomplete' region, update it to include this node. This
11841200
// ensures we only create the region if needed.
@@ -1206,11 +1222,19 @@ struct CoverageMapping : public ASTWalker {
12061222
assignCounter(IE->getElseExpr(),
12071223
CounterExpr::Sub(getCurrentCounter(), ThenCounter));
12081224
}
1209-
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;
12101234
}
12111235

12121236
PostWalkResult<Expr *> walkToExprPost(Expr *E) override {
1213-
if (shouldSkipExpr(E, Parent))
1237+
if (shouldSkipExpr(E))
12141238
return Action::Continue(E);
12151239

12161240
if (hasCounter(E))
@@ -1249,7 +1273,7 @@ void SILProfiler::assignRegionCounters() {
12491273

12501274
CurrentFileName = getCurrentFileName(Root, forDecl);
12511275

1252-
MapRegionCounters Mapper(RegionCounterMap);
1276+
MapRegionCounters Mapper(forDecl, RegionCounterMap);
12531277

12541278
std::string CurrentFuncName;
12551279
FormalLinkage CurrentFuncLinkage;
@@ -1285,7 +1309,7 @@ void SILProfiler::assignRegionCounters() {
12851309
PGOFuncHash = 0x0;
12861310

12871311
if (EmitCoverageMapping) {
1288-
CoverageMapping Coverage(SM);
1312+
CoverageMapping Coverage(SM, forDecl);
12891313
Root.walk(Coverage);
12901314
CovMap =
12911315
Coverage.emitSourceRegions(M, CurrentFuncName, PGOFuncName, PGOFuncHash,
@@ -1302,7 +1326,7 @@ void SILProfiler::assignRegionCounters() {
13021326
llvm::dbgs() << PGOFuncName << "\n";
13031327
return;
13041328
}
1305-
PGOMapping pgoMapper(RegionCounterMap, LoadedCounts.get(),
1329+
PGOMapping pgoMapper(forDecl, RegionCounterMap, LoadedCounts.get(),
13061330
RegionLoadedCounterMap, RegionCondToParentMap);
13071331
Root.walk(pgoMapper);
13081332
}

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)