Skip to content

Commit 609de86

Browse files
committed
[Coverage] Emit mappings for top-level code decls
Make sure that code in top-level decls gets decent coverage reporting. rdar://problem/27874041
1 parent 2acf98f commit 609de86

File tree

5 files changed

+70
-15
lines changed

5 files changed

+70
-15
lines changed

lib/SILGen/SILGen.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,9 @@ void SILGenModule::visitTopLevelCodeDecl(TopLevelCodeDecl *td) {
11801180
if (!TopLevelSGF->B.hasValidInsertionPoint())
11811181
return;
11821182

1183+
ProfilerRAII Profiler(*this, td);
1184+
TopLevelSGF->emitProfilerIncrement(td->getBody());
1185+
11831186
for (auto &ESD : td->getBody()->getElements()) {
11841187
if (!TopLevelSGF->B.hasValidInsertionPoint()) {
11851188
if (Stmt *S = ESD.dyn_cast<Stmt*>()) {

lib/SILGen/SILGenProfiling.cpp

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,17 @@ static bool isUnmappedDecl(Decl *D) {
3737
}
3838

3939
/// Walk the non-static initializers in \p PBD.
40-
static void walkForProfiling(PatternBindingDecl *PBD, ASTWalker &Walker) {
40+
static void walkPatternForProfiling(PatternBindingDecl *PBD,
41+
ASTWalker &Walker) {
4142
if (PBD && !PBD->isStatic())
4243
for (auto E : PBD->getPatternList())
4344
if (E.getInit())
4445
E.getInit()->walk(Walker);
4546
}
4647

4748
/// Walk the AST of \c Root and related nodes that are relevant for profiling.
48-
static void walkForProfiling(AbstractFunctionDecl *Root, ASTWalker &Walker) {
49+
static void walkFunctionForProfiling(AbstractFunctionDecl *Root,
50+
ASTWalker &Walker) {
4951
Root->walk(Walker);
5052

5153
// We treat class initializers as part of the constructor for profiling.
@@ -55,13 +57,27 @@ static void walkForProfiling(AbstractFunctionDecl *Root, ASTWalker &Walker) {
5557
for (auto *Member : NominalType->getMembers()) {
5658
// Find pattern binding declarations that have initializers.
5759
if (auto *PBD = dyn_cast<PatternBindingDecl>(Member))
58-
walkForProfiling(PBD, Walker);
60+
walkPatternForProfiling(PBD, Walker);
5961
}
6062
}
6163
}
6264

63-
ProfilerRAII::ProfilerRAII(SILGenModule &SGM, AbstractFunctionDecl *D)
65+
/// Walk \p D for profiling.
66+
static void walkForProfiling(Decl *D, ASTWalker &Walker) {
67+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D))
68+
walkFunctionForProfiling(AFD, Walker);
69+
else if (auto *PBD = dyn_cast<PatternBindingDecl>(D))
70+
walkPatternForProfiling(PBD, Walker);
71+
else if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D))
72+
TLCD->walk(Walker);
73+
else
74+
llvm_unreachable("Don't know how to walk decl for profiling");
75+
}
76+
77+
ProfilerRAII::ProfilerRAII(SILGenModule &SGM, Decl *D)
6478
: SGM(SGM), PreviousProfiler(std::move(SGM.Profiler)) {
79+
assert(isa<AbstractFunctionDecl>(D) ||
80+
isa<TopLevelCodeDecl>(D) && "Cannot create profiler for this decl");
6581
const auto &Opts = SGM.M.getOptions();
6682
if (!Opts.GenerateProfile || isUnmappedDecl(D))
6783
return;
@@ -90,6 +106,8 @@ struct MapRegionCounters : public ASTWalker {
90106
return false;
91107
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D))
92108
CounterMap[AFD->getBody()] = NextCounter++;
109+
if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D))
110+
CounterMap[TLCD->getBody()] = NextCounter++;
93111
return true;
94112
}
95113

@@ -106,7 +124,7 @@ struct MapRegionCounters : public ASTWalker {
106124
CounterMap[FS->getBody()] = NextCounter++;
107125
} else if (auto *FES = dyn_cast<ForEachStmt>(S)) {
108126
CounterMap[FES->getBody()] = NextCounter++;
109-
walkForProfiling(FES->getIterator(), *this);
127+
walkPatternForProfiling(FES->getIterator(), *this);
110128
} else if (auto *SS = dyn_cast<SwitchStmt>(S)) {
111129
CounterMap[SS] = NextCounter++;
112130
} else if (auto *CS = dyn_cast<CaseStmt>(S)) {
@@ -475,6 +493,8 @@ struct CoverageMapping : public ASTWalker {
475493
return false;
476494
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D))
477495
assignCounter(AFD->getBody());
496+
else if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D))
497+
assignCounter(TLCD->getBody());
478498
return true;
479499
}
480500

@@ -522,7 +542,7 @@ struct CoverageMapping : public ASTWalker {
522542
} else if (auto *FES = dyn_cast<ForEachStmt>(S)) {
523543
assignCounter(FES, CounterExpr::Zero());
524544
assignCounter(FES->getBody());
525-
walkForProfiling(FES->getIterator(), *this);
545+
walkPatternForProfiling(FES->getIterator(), *this);
526546

527547
} else if (auto *SS = dyn_cast<SwitchStmt>(S)) {
528548
assignCounter(SS);
@@ -672,22 +692,32 @@ getEquivalentPGOLinkage(FormalLinkage Linkage) {
672692
}
673693
}
674694

675-
void SILGenProfiling::assignRegionCounters(AbstractFunctionDecl *Root) {
676-
CurrentFuncName = SILDeclRef(Root).mangle();
677-
CurrentFuncLinkage = getDeclLinkage(Root);
695+
void SILGenProfiling::assignRegionCounters(Decl *Root) {
696+
const auto &SM = SGM.M.getASTContext().SourceMgr;
678697

679-
if (auto *ParentFile = Root->getParentSourceFile())
698+
if (auto *ParentFile = cast<DeclContext>(Root)->getParentSourceFile())
680699
CurrentFileName = ParentFile->getFilename();
681700

682701
MapRegionCounters Mapper(RegionCounterMap);
702+
703+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(Root)) {
704+
CurrentFuncName = SILDeclRef(AFD).mangle();
705+
CurrentFuncLinkage = getDeclLinkage(AFD);
706+
} else if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(Root)) {
707+
llvm::raw_string_ostream OS{CurrentFuncName};
708+
OS << "__tlcd_";
709+
TLCD->getStartLoc().printLineAndColumn(OS, SM);
710+
CurrentFuncLinkage = FormalLinkage::HiddenUnique;
711+
}
712+
683713
walkForProfiling(Root, Mapper);
684714

685715
NumRegionCounters = Mapper.NextCounter;
686716
// TODO: Mapper needs to calculate a function hash as it goes.
687717
FunctionHash = 0x0;
688718

689719
if (EmitCoverageMapping) {
690-
CoverageMapping Coverage(SGM.M.getASTContext().SourceMgr);
720+
CoverageMapping Coverage(SM);
691721
walkForProfiling(Root, Coverage);
692722
Coverage.emitSourceRegions(SGM.M, CurrentFuncName,
693723
!llvm::GlobalValue::isLocalLinkage(

lib/SILGen/SILGenProfiling.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class SILGenProfiling {
5757

5858
private:
5959
/// Map counters to ASTNodes and set them up for profiling the given function.
60-
void assignRegionCounters(AbstractFunctionDecl *Root);
60+
void assignRegionCounters(Decl *Root);
6161

6262
friend struct ProfilerRAII;
6363
};
@@ -67,7 +67,7 @@ struct ProfilerRAII {
6767
SILGenModule &SGM;
6868
std::unique_ptr<SILGenProfiling> PreviousProfiler;
6969

70-
ProfilerRAII(SILGenModule &SGM, AbstractFunctionDecl *D);
70+
ProfilerRAII(SILGenModule &SGM, Decl *D);
7171
~ProfilerRAII();
7272
};
7373

test/SILGen/coverage_smoke.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,11 @@ func foo() {
6262
x += 1 // CHECK-COV: 1|{{.*}}[[@LINE]]
6363
}
6464

65-
main()
66-
foo()
65+
// rdar://problem/27874041 - top level code decls get no coverage
66+
var g1 : Int32 = 0 // CHECK-COV: |{{.*}}[[@LINE]]
67+
repeat { // CHECK-COV: 1|{{.*}}[[@LINE]]
68+
g1 += 1 // CHECK-COV: 1|{{.*}}[[@LINE]]
69+
} while g1 == 0 // CHECK-COV: 1|{{.*}}[[@LINE]]
70+
71+
main() // CHECK-COV: 1{{.*}}[[@LINE]]
72+
foo() // CHECK-COV: 1{{.*}}[[@LINE]]

test/SILGen/coverage_toplevel.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -emit-sil -module-name coverage_toplevel %s | %FileCheck %s
2+
3+
// CHECK: sil_coverage_map{{.*}}__tlcd_line:[[@LINE+2]]:1
4+
// CHECK: [[@LINE+1]]:1 -> [[@LINE+1]]:11
5+
print("a")
6+
7+
// CHECK: sil_coverage_map{{.*}}// coverage_toplevel.f1
8+
func f1() {}
9+
10+
var i : Int32 = 0
11+
12+
// CHECK: sil_coverage_map{{.*}}__tlcd_line:[[@LINE+2]]:1
13+
// CHECK: [[@LINE+1]]:7 -> [[@LINE+1]]:15 : (0 + 1)
14+
while (i < 10) {
15+
i += 1
16+
}

0 commit comments

Comments
 (0)