Skip to content

Commit cc46593

Browse files
committed
[IRGen] Emit name data for unused coverage counters
Previously, if all the coverage counters for a given SIL function were optimized out, we would have dropped the coverage map on the floor, leading to inaccurate coverage reports in `-O`. However LLVM allows us to emit the name data for the coverage maps separately by defining an `__llvm_coverage_names` global with the name data, which is picked up by its instrumentation lowering pass. Use this to continue to emit coverage maps for functions that do not get emitted. rdar://99813754
1 parent c72ddcf commit cc46593

File tree

4 files changed

+109
-32
lines changed

4 files changed

+109
-32
lines changed

lib/IRGen/GenCoverage.cpp

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,23 +43,41 @@ static std::string getInstrProfSection(IRGenModule &IGM,
4343
}
4444

4545
void IRGenModule::emitCoverageMapping() {
46+
SmallVector<llvm::Constant *, 4> UnusedFuncNames;
4647
std::vector<const SILCoverageMap *> Mappings;
4748
for (const auto &M : getSILModule().getCoverageMaps()) {
49+
auto FuncName = M.second->getPGOFuncName();
50+
auto VarLinkage = llvm::GlobalValue::LinkOnceAnyLinkage;
51+
auto FuncNameVarName = llvm::getPGOFuncNameVarName(FuncName, VarLinkage);
52+
4853
// Check whether this coverage mapping can reference its name data within
49-
// the profile symbol table. If the name global is gone, this function has
50-
// been optimized out.
51-
StringRef PGOFuncName = M.second->getPGOFuncName();
52-
std::string PGOFuncNameVar = llvm::getPGOFuncNameVarName(
53-
PGOFuncName, llvm::GlobalValue::LinkOnceAnyLinkage);
54-
if (!Module.getNamedGlobal(PGOFuncNameVar))
55-
continue;
54+
// the profile symbol table. If the name global isn't there, this function
55+
// has been optimized out. We need to tell LLVM about it by emitting the
56+
// name data separately.
57+
if (!Module.getNamedGlobal(FuncNameVarName)) {
58+
auto *Var = llvm::createPGOFuncNameVar(Module, VarLinkage, FuncName);
59+
UnusedFuncNames.push_back(llvm::ConstantExpr::getBitCast(Var, Int8PtrTy));
60+
}
5661
Mappings.push_back(M.second);
5762
}
5863

5964
// If there aren't any coverage maps, there's nothing to emit.
6065
if (Mappings.empty())
6166
return;
6267

68+
// Emit the name data for any unused functions.
69+
if (!UnusedFuncNames.empty()) {
70+
auto NamePtrsTy = llvm::ArrayType::get(Int8PtrTy, UnusedFuncNames.size());
71+
auto NamePtrs = llvm::ConstantArray::get(NamePtrsTy, UnusedFuncNames);
72+
73+
// Note we don't mark this variable as used, as it doesn't need to be
74+
// present in the object file, it gets picked up by the LLVM instrumentation
75+
// lowering pass.
76+
new llvm::GlobalVariable(Module, NamePtrsTy, /*IsConstant*/ true,
77+
llvm::GlobalValue::InternalLinkage, NamePtrs,
78+
llvm::getCoverageUnusedNamesVarName());
79+
}
80+
6381
std::vector<StringRef> Files;
6482
for (const auto &M : Mappings)
6583
if (std::find(Files.begin(), Files.end(), M->getFile()) == Files.end())
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// RUN: %target-swift-frontend -emit-irgen -profile-generate -profile-coverage-mapping -O -module-name coverage_optimized %s | %FileCheck %s -check-prefix IRGEN
2+
// RUN: %target-swift-frontend -emit-ir -profile-generate -profile-coverage-mapping -O -module-name coverage_optimized %s | %FileCheck %s -check-prefix IR
3+
4+
// The functions 'unused' and 'optimizedOut' below will be optimized out, but
5+
// make sure we still emit coverage records for them, using name data emitted
6+
// separately in @__llvm_coverage_names.
7+
8+
// IRGEN: @__llvm_coverage_names {{.*}} @"__profn_{{.*}}$s18coverage_optimized6unusedyyF", {{.*}} @"__profn_{{.*}}$s18coverage_optimized0B3OutSiyF"
9+
10+
// IRGEN: @__covrec
11+
// IRGEN: @__covrec
12+
// IRGEN: @__covrec
13+
// IRGEN: @__covrec
14+
// IRGEN: @__covrec
15+
16+
// IRGEN: @__llvm_coverage_mapping
17+
18+
// IR: @__covrec
19+
// IR: @__covrec
20+
// IR: @__covrec
21+
// IR: @__covrec
22+
// IR: @__covrec
23+
24+
// We shouldn't have any lingering @__profn's, they should have been emitted as
25+
// @__llvm_prf_nm.
26+
// IR-NOT: __profn_
27+
// IR-NOT: @__llvm_coverage_names
28+
// IR: @__llvm_prf_nm
29+
// IR-NOT: __profn_
30+
// IR-NOT: @__llvm_coverage_names
31+
32+
// IR-NOT: define {{.*}} @"$s18coverage_optimized6unusedyyF"
33+
// IR-NOT: define {{.*}} @"$s18coverage_optimized0B3OutSiyF"
34+
35+
func unused() {}
36+
func optimizedOut() -> Int { .random() ? 1 : 2 }
37+
38+
func bar() -> Bool { false }
39+
40+
func baz() {
41+
if bar() {
42+
_ = optimizedOut()
43+
}
44+
}
45+
46+
baz()
47+
48+
// IRGEN-LABEL: define {{.*}} @main
49+
// IRGEN: call void @llvm.instrprof.increment({{.*}} @"__profn_{{.*}}__tlcd_line
50+
// IRGEN: call void @llvm.instrprof.increment({{.*}} @"__profn_{{.*}}$s18coverage_optimized3bazyyF"
51+
// IRGEN: call void @llvm.instrprof.increment({{.*}} @"__profn_{{.*}}$s18coverage_optimized3barSbyF"
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-build-swift %s -profile-generate -profile-coverage-mapping -O -o %t/main
4+
5+
// This unusual use of 'sh' allows the path of the profraw file to be
6+
// substituted by %target-run.
7+
// RUN: %target-codesign %t/main
8+
// RUN: %target-run sh -c 'env LLVM_PROFILE_FILE=$1 $2' -- %t/default.profraw %t/main
9+
10+
// RUN: %llvm-profdata merge %t/default.profraw -o %t/default.profdata
11+
// RUN: %llvm-cov export -summary-only %t/main -instr-profile=%t/default.profdata | %FileCheck %s
12+
13+
// REQUIRES: profile_runtime
14+
// REQUIRES: executable_test
15+
// REQUIRES: OS=macosx
16+
17+
// CHECK: "lines":{"count":9,"covered":6{{.*}}"functions":{"count":5,"covered":3
18+
19+
// The functions 'unused' and 'optimizedOut' will be optimized out, but
20+
// make sure we still emit coverage records for them, using name data emitted
21+
// separately in @__llvm_coverage_names.
22+
func unused() {}
23+
func optimizedOut() -> Int { .random() ? 1 : 2 }
24+
25+
func bar() -> Bool { false }
26+
27+
func baz() {
28+
if bar() {
29+
_ = optimizedOut()
30+
}
31+
}
32+
33+
baz()

test/Profiler/instrprof_symtab_valid.sil

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)