Skip to content

Commit 6d3545f

Browse files
committed
[IRGen] Don't emit multiple copies of coverage maps
Iterating over the IRGenModules and emitting every SILCoverageMap in the SILModule meant that we were emitting N copies of the coverage maps for parallel IRGen, where N is the number of output object files. This has always been wrong, but was previously saved by the fact that we would drop the coverage map on the floor if we didn't have the name data available. This would only be the case for the IRGenModule that had the emitted entity to profile, in addition to any IRGenModules that had inlined the body of a profiled enitity. As such, this prevented the duplication from being too egregious. However with the recent change to emit unused name data in the case where we don't already have name data available, we're now fully duplicating every coverage mapping, and emitting a ton of redundant name data. Fix things such that we only emit coverage mapping records for the IRGenModule that corresponds to the entity being profiled. rdar://102905496
1 parent d59ff45 commit 6d3545f

File tree

7 files changed

+77
-21
lines changed

7 files changed

+77
-21
lines changed

lib/IRGen/GenCoverage.cpp

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,14 @@ static std::string getInstrProfSection(IRGenModule &IGM,
4242
return llvm::getInstrProfSectionName(SK, IGM.Triple.getObjectFormat());
4343
}
4444

45-
void IRGenModule::emitCoverageMapping() {
45+
void IRGenModule::emitCoverageMaps(ArrayRef<const SILCoverageMap *> Mappings) {
46+
// If there aren't any coverage maps, there's nothing to emit.
47+
if (Mappings.empty())
48+
return;
49+
4650
SmallVector<llvm::Constant *, 4> UnusedFuncNames;
47-
std::vector<const SILCoverageMap *> Mappings;
48-
for (const auto &M : getSILModule().getCoverageMaps()) {
49-
auto FuncName = M.second->getPGOFuncName();
51+
for (const auto *Mapping : Mappings) {
52+
auto FuncName = Mapping->getPGOFuncName();
5053
auto VarLinkage = llvm::GlobalValue::LinkOnceAnyLinkage;
5154
auto FuncNameVarName = llvm::getPGOFuncNameVarName(FuncName, VarLinkage);
5255

@@ -58,13 +61,8 @@ void IRGenModule::emitCoverageMapping() {
5861
auto *Var = llvm::createPGOFuncNameVar(Module, VarLinkage, FuncName);
5962
UnusedFuncNames.push_back(llvm::ConstantExpr::getBitCast(Var, Int8PtrTy));
6063
}
61-
Mappings.push_back(M.second);
6264
}
6365

64-
// If there aren't any coverage maps, there's nothing to emit.
65-
if (Mappings.empty())
66-
return;
67-
6866
// Emit the name data for any unused functions.
6967
if (!UnusedFuncNames.empty()) {
7068
auto NamePtrsTy = llvm::ArrayType::get(Int8PtrTy, UnusedFuncNames.size());
@@ -194,6 +192,27 @@ void IRGenModule::emitCoverageMapping() {
194192
}
195193

196194
void IRGenerator::emitCoverageMapping() {
197-
for (auto &IGM : *this)
198-
IGM.second->emitCoverageMapping();
195+
if (SIL.getCoverageMaps().empty())
196+
return;
197+
198+
// Shard the coverage maps across their designated IRGenModules. This is
199+
// necessary to ensure we don't output N copies of a coverage map when doing
200+
// parallel IRGen, where N is the number of output object files.
201+
//
202+
// Note we don't just dump all the coverage maps into the primary IGM as
203+
// that would require creating unecessary name data entries, since the name
204+
// data is likely to already be present in the IGM that contains the entity
205+
// being profiled (unless it has been optimized out). Matching the coverage
206+
// map to its originating SourceFile also matches the behavior of a debug
207+
// build where the files are compiled separately.
208+
llvm::DenseMap<IRGenModule *, std::vector<const SILCoverageMap *>> MapsToEmit;
209+
for (const auto &M : SIL.getCoverageMaps()) {
210+
auto &Mapping = M.second;
211+
auto *SF = Mapping->getParentSourceFile();
212+
MapsToEmit[getGenModule(SF)].push_back(Mapping);
213+
}
214+
for (auto &IGMPair : *this) {
215+
auto *IGM = IGMPair.second;
216+
IGM->emitCoverageMaps(MapsToEmit[IGM]);
217+
}
199218
}

lib/IRGen/IRGen.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1369,7 +1369,7 @@ GeneratedModule IRGenRequest::evaluate(Evaluator &evaluator,
13691369
// Emit coverage mapping info. This needs to happen after we've emitted
13701370
// any lazy definitions, as we need to know whether or not we emitted a
13711371
// profiler increment for a given coverage map.
1372-
IGM.emitCoverageMapping();
1372+
irgen.emitCoverageMapping();
13731373

13741374
// Emit symbols for eliminated dead methods.
13751375
IGM.emitVTableStubs();

lib/IRGen/IRGenModule.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1892,6 +1892,17 @@ void IRGenerator::addGenModule(SourceFile *SF, IRGenModule *IGM) {
18921892
Queue.push_back(IGM);
18931893
}
18941894

1895+
IRGenModule *IRGenerator::getGenModule(SourceFile *SF) {
1896+
// If we're emitting for a single module, or a single file, we always use the
1897+
// primary IGM.
1898+
if (GenModules.size() == 1)
1899+
return getPrimaryIGM();
1900+
1901+
IRGenModule *IGM = GenModules[SF];
1902+
assert(IGM);
1903+
return IGM;
1904+
}
1905+
18951906
IRGenModule *IRGenerator::getGenModule(DeclContext *ctxt) {
18961907
if (GenModules.size() == 1 || !ctxt) {
18971908
return getPrimaryIGM();

lib/IRGen/IRGenModule.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ namespace swift {
104104
class ProtocolConformance;
105105
class ProtocolCompositionType;
106106
class RootProtocolConformance;
107+
class SILCoverageMap;
107108
struct SILDeclRef;
108109
class SILDefaultWitnessTable;
109110
class SILDifferentiabilityWitness;
@@ -350,11 +351,7 @@ class IRGenerator {
350351
void addGenModule(SourceFile *SF, IRGenModule *IGM);
351352

352353
/// Get an IRGenModule for a source file.
353-
IRGenModule *getGenModule(SourceFile *SF) {
354-
IRGenModule *IGM = GenModules[SF];
355-
assert(IGM);
356-
return IGM;
357-
}
354+
IRGenModule *getGenModule(SourceFile *SF);
358355

359356
SourceFile *getSourceFile(IRGenModule *module) {
360357
for (auto pair : GenModules) {
@@ -1481,7 +1478,7 @@ private: \
14811478
void maybeEmitOpaqueTypeDecl(OpaqueTypeDecl *D);
14821479

14831480
void emitSILGlobalVariable(SILGlobalVariable *gv);
1484-
void emitCoverageMapping();
1481+
void emitCoverageMaps(ArrayRef<const SILCoverageMap *> Mappings);
14851482
void emitSILFunction(SILFunction *f);
14861483
void emitSILWitnessTable(SILWitnessTable *wt);
14871484
void emitSILProperty(SILProperty *prop);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
public func func1() { func2() }
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
public func func2() {}
Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,40 @@
1-
// RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -num-threads 0 -emit-ir %S/Inputs/coverage_num_threads1.swift | %FileCheck %s -check-prefix=SINGLE-SOURCE --implicit-check-not="llvm_coverage_mapping ="
1+
// RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -num-threads 0 -emit-ir %S/Inputs/coverage_num_threads1.swift | %FileCheck %s -check-prefix=SINGLE-SOURCE --implicit-check-not="llvm_coverage_mapping =" --implicit-check-not="@__covrec_{{[a-zA-Z0-9]+}} ="
22

3+
// Make sure we only emit 1 coverage record in total.
4+
// SINGLE-SOURCE: @__covrec_{{[a-zA-Z0-9]+}} =
35
// SINGLE-SOURCE: llvm_coverage_mapping =
46

5-
// RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -num-threads 0 -emit-ir %S/Inputs/coverage_num_threads1.swift %S/Inputs/coverage_num_threads2.swift | %FileCheck %s -check-prefix=SINGLE-OBJECT --implicit-check-not="llvm_coverage_mapping ="
7+
// RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -num-threads 0 -emit-ir %S/Inputs/coverage_num_threads1.swift %S/Inputs/coverage_num_threads2.swift | %FileCheck %s -check-prefix=SINGLE-OBJECT --implicit-check-not="llvm_coverage_mapping =" --implicit-check-not="@__covrec_{{[a-zA-Z0-9]+}} ="
68

9+
// Make sure we only emit 2 coverage records in total.
10+
// SINGLE-OBJECT: @__covrec_{{[a-zA-Z0-9]+}} =
11+
// SINGLE-OBJECT: @__covrec_{{[a-zA-Z0-9]+}} =
712
// SINGLE-OBJECT: llvm_coverage_mapping =
813

914
// Using 1 goes down the multithreaded codepath but only operates with a single thread to work around an issue on Windows where the output of both IR modules is interleaved and therefore the output is invalid
10-
// RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -num-threads 1 -emit-ir %S/Inputs/coverage_num_threads1.swift %S/Inputs/coverage_num_threads2.swift | %FileCheck %s -check-prefix=MULTIPLE-OBJECTS --implicit-check-not="llvm_coverage_mapping ="
15+
// RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -num-threads 1 -emit-ir %S/Inputs/coverage_num_threads1.swift %S/Inputs/coverage_num_threads2.swift | %FileCheck %s -check-prefix=MULTIPLE-OBJECTS --implicit-check-not="llvm_coverage_mapping =" --implicit-check-not="@__covrec_{{[a-zA-Z0-9]+}} ="
1116

17+
// Make sure we only emit 2 coverage records in total (1 per output).
18+
// MULTIPLE-OBJECTS: @__covrec_{{[a-zA-Z0-9]+}} =
1219
// MULTIPLE-OBJECTS: llvm_coverage_mapping =
20+
// MULTIPLE-OBJECTS: ; ModuleID =
21+
// MULTIPLE-OBJECTS: @__covrec_{{[a-zA-Z0-9]+}} =
1322
// MULTIPLE-OBJECTS: llvm_coverage_mapping =
23+
24+
// Under -O, we inline the profiler increment of func2 into func1. The coverage
25+
// mapping for func2 should still however be present only in its original file.
26+
// RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -num-threads 1 -O -emit-sil %S/Inputs/coverage_num_threads3.swift %S/Inputs/coverage_num_threads4.swift | %FileCheck %s -check-prefix=MULTIPLE-OBJECTS-INLINE-SIL
27+
// RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -num-threads 1 -O -emit-ir %S/Inputs/coverage_num_threads3.swift %S/Inputs/coverage_num_threads4.swift | %FileCheck %s -check-prefix=MULTIPLE-OBJECTS-INLINE --implicit-check-not="llvm_coverage_mapping =" --implicit-check-not="@__covrec_{{[a-zA-Z0-9]+}} ="
28+
29+
// MULTIPLE-OBJECTS-INLINE-SIL-LABEL: sil @$s21coverage_num_threads35func1yyF
30+
// MULTIPLE-OBJECTS-INLINE-SIL: increment_profiler_counter 0, "$s21coverage_num_threads35func1yyF"
31+
// MULTIPLE-OBJECTS-INLINE-SIL: increment_profiler_counter 0, "$s21coverage_num_threads35func2yyF"
32+
33+
// MULTIPLE-OBJECTS-INLINE-SIL-LABEL: sil @$s21coverage_num_threads35func2yyF
34+
// MULTIPLE-OBJECTS-INLINE-SIL: increment_profiler_counter 0, "$s21coverage_num_threads35func2yyF"
35+
36+
// MULTIPLE-OBJECTS-INLINE: @__covrec_{{[a-zA-Z0-9]+}} =
37+
// MULTIPLE-OBJECTS-INLINE: llvm_coverage_mapping =
38+
// MULTIPLE-OBJECTS-INLINE: ; ModuleID =
39+
// MULTIPLE-OBJECTS-INLINE: @__covrec_{{[a-zA-Z0-9]+}} =
40+
// MULTIPLE-OBJECTS-INLINE: llvm_coverage_mapping =

0 commit comments

Comments
 (0)