Skip to content

Commit 3bfbd5e

Browse files
authored
Merge pull request #61158 from hamishknight/you-know-my-name
2 parents 109244c + 01c7c16 commit 3bfbd5e

File tree

8 files changed

+143
-48
lines changed

8 files changed

+143
-48
lines changed

lib/IRGen/GenCoverage.cpp

Lines changed: 30 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())
@@ -174,3 +192,8 @@ void IRGenModule::emitCoverageMapping() {
174192
CovData->setAlignment(llvm::Align(8));
175193
addUsedGlobal(CovData);
176194
}
195+
196+
void IRGenerator::emitCoverageMapping() {
197+
for (auto &IGM : *this)
198+
IGM.second->emitCoverageMapping();
199+
}

lib/IRGen/GenDecl.cpp

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,23 +1158,15 @@ void IRGenModule::emitGlobalLists() {
11581158
/*isConstant*/false, /*asContiguousArray*/true);
11591159
}
11601160

1161-
static bool hasCodeCoverageInstrumentation(SILFunction &f, SILModule &m) {
1162-
return f.getProfiler() && m.getOptions().EmitProfileCoverageMapping;
1163-
}
1164-
1165-
// Eagerly emit functions that are externally visible. Functions with code
1166-
// coverage instrumentation must also be eagerly emitted. So must functions
1167-
// that are a dynamic replacement for another.
1161+
// Eagerly emit functions that are externally visible. Functions that are
1162+
// dynamic replacements must also be eagerly emitted.
11681163
static bool isLazilyEmittedFunction(SILFunction &f, SILModule &m) {
11691164
if (f.isPossiblyUsedExternally())
11701165
return false;
11711166

11721167
if (f.getDynamicallyReplacedFunction())
11731168
return false;
11741169

1175-
if (hasCodeCoverageInstrumentation(f, m))
1176-
return false;
1177-
11781170
// Needed by lldb to print global variables which are propagated by the
11791171
// mandatory GlobalOpt.
11801172
if (m.getOptions().OptMode == OptimizationMode::NoOptimization &&
@@ -1258,12 +1250,6 @@ void IRGenerator::emitGlobalTopLevel(
12581250
IGM->emitSILDifferentiabilityWitness(&dw);
12591251
}
12601252

1261-
// Emit code coverage mapping data for all modules
1262-
for (auto Iter : *this) {
1263-
IRGenModule *IGM = Iter.second;
1264-
IGM->emitCoverageMapping();
1265-
}
1266-
12671253
for (auto Iter : *this) {
12681254
IRGenModule *IGM = Iter.second;
12691255
IGM->finishEmitAfterTopLevel();

lib/IRGen/IRGen.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,6 +1358,11 @@ GeneratedModule IRGenRequest::evaluate(Evaluator &evaluator,
13581358
irgen.emitDynamicReplacements();
13591359
}
13601360

1361+
// Emit coverage mapping info. This needs to happen after we've emitted
1362+
// any lazy definitions, as we need to know whether or not we emitted a
1363+
// profiler increment for a given coverage map.
1364+
IGM.emitCoverageMapping();
1365+
13611366
// Emit symbols for eliminated dead methods.
13621367
IGM.emitVTableStubs();
13631368

@@ -1608,6 +1613,11 @@ static void performParallelIRGeneration(IRGenDescriptor desc) {
16081613
// Emit reflection metadata for builtin and imported types.
16091614
irgen.emitBuiltinReflectionMetadata();
16101615

1616+
// Emit coverage mapping info. This needs to happen after we've emitted
1617+
// any lazy definitions, as we need to know whether or not we emitted a
1618+
// profiler increment for a given coverage map.
1619+
irgen.emitCoverageMapping();
1620+
16111621
IRGenModule *PrimaryGM = irgen.getPrimaryIGM();
16121622

16131623
// Emit symbols for eliminated dead methods.

lib/IRGen/IRGenModule.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,9 @@ class IRGenerator {
427427
// Emit info that describes the entry point to the module, if it has one.
428428
void emitEntryPointInfo();
429429

430+
/// Emit coverage mapping info.
431+
void emitCoverageMapping();
432+
430433
/// Checks if metadata for this type can be emitted lazily. This is true for
431434
/// non-public types as well as imported types, except for classes and
432435
/// protocols which are always emitted eagerly.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %target-swift-frontend -emit-sil -profile-generate -profile-coverage-mapping -module-name coverage_deadcode %s | %FileCheck %s -check-prefix SIL
2+
// RUN: %target-swift-frontend -emit-ir -profile-generate -profile-coverage-mapping -module-name coverage_deadcode %s | %FileCheck %s -check-prefix IR
3+
4+
// This function needs to be present in the SIL for the mandatory passes, but
5+
// we can drop it in IRGen. We still need to emit its coverage map though.
6+
func unused() -> Int { 5 }
7+
8+
// SIL: sil hidden @$s17coverage_deadcode6unusedSiyF : $@convention(thin) () -> Int
9+
// SIL: sil_coverage_map {{.*}} "$s17coverage_deadcode6unusedSiyF"
10+
11+
// IR: @__covrec
12+
// IR: @__llvm_coverage_mapping
13+
// IR: @__llvm_prf_nm
14+
// IR-NOT: define {{.*}} @"$s17coverage_deadcode6unusedSiyF"
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)