Skip to content

Commit b3c40cf

Browse files
committed
coverage: Deal with unused functions and their names in one place
1 parent 75135aa commit b3c40cf

File tree

3 files changed

+51
-39
lines changed

3 files changed

+51
-39
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

+5-29
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,11 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
7373
// In a single designated CGU, also prepare covfun records for functions
7474
// in this crate that were instrumented for coverage, but are unused.
7575
if cx.codegen_unit.is_code_coverage_dead_code_cgu() {
76-
let mut unused_instances = unused::gather_unused_function_instances(cx);
77-
// Sort the unused instances by symbol name, for the same reason as the used ones.
78-
unused_instances.sort_by_cached_key(|&instance| tcx.symbol_name(instance).name);
79-
covfun_records.extend(unused_instances.into_iter().filter_map(|instance| {
80-
prepare_covfun_record(tcx, &mut global_file_table, instance, false)
81-
}));
76+
unused::prepare_covfun_records_for_unused_functions(
77+
cx,
78+
&mut global_file_table,
79+
&mut covfun_records,
80+
);
8281
}
8382

8483
// If there are no covfun records for this CGU, don't generate a covmap record.
@@ -97,33 +96,10 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
9796
// contain multiple covmap records from different compilation units.
9897
let filenames_hash = llvm_cov::hash_bytes(&filenames_buffer);
9998

100-
let mut unused_function_names = vec![];
101-
10299
for covfun in &covfun_records {
103-
unused_function_names.extend(covfun.mangled_function_name_if_unused());
104-
105100
covfun::generate_covfun_record(cx, filenames_hash, covfun)
106101
}
107102

108-
// For unused functions, we need to take their mangled names and store them
109-
// in a specially-named global array. LLVM's `InstrProfiling` pass will
110-
// detect this global and include those names in its `__llvm_prf_names`
111-
// section. (See `llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp`.)
112-
if !unused_function_names.is_empty() {
113-
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
114-
115-
let name_globals = unused_function_names
116-
.into_iter()
117-
.map(|mangled_function_name| cx.const_str(mangled_function_name).0)
118-
.collect::<Vec<_>>();
119-
let initializer = cx.const_array(cx.type_ptr(), &name_globals);
120-
121-
let array = llvm::add_global(cx.llmod, cx.val_ty(initializer), c"__llvm_coverage_names");
122-
llvm::set_global_constant(array, true);
123-
llvm::set_linkage(array, llvm::Linkage::InternalLinkage);
124-
llvm::set_initializer(array, initializer);
125-
}
126-
127103
// Generate the coverage map header, which contains the filenames used by
128104
// this CGU's coverage mappings, and store it in a well-known global.
129105
// (This is skipped if we returned early due to having no covfun records.)

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs

-8
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,6 @@ pub(crate) struct CovfunRecord<'tcx> {
3737
regions: ffi::Regions,
3838
}
3939

40-
impl<'tcx> CovfunRecord<'tcx> {
41-
/// FIXME(Zalathar): Make this the responsibility of the code that determines
42-
/// which functions are unused.
43-
pub(crate) fn mangled_function_name_if_unused(&self) -> Option<&'tcx str> {
44-
(!self.is_used).then_some(self.mangled_function_name)
45-
}
46-
}
47-
4840
pub(crate) fn prepare_covfun_record<'tcx>(
4941
tcx: TyCtxt<'tcx>,
5042
global_file_table: &mut GlobalFileTable,

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/unused.rs

+46-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, ConstCodegenMethods};
12
use rustc_data_structures::fx::FxHashSet;
23
use rustc_hir::def_id::{DefId, LocalDefId};
34
use rustc_middle::mir;
@@ -6,6 +7,9 @@ use rustc_middle::ty::{self, TyCtxt};
67
use rustc_span::def_id::DefIdSet;
78

89
use crate::common::CodegenCx;
10+
use crate::coverageinfo::mapgen::GlobalFileTable;
11+
use crate::coverageinfo::mapgen::covfun::{CovfunRecord, prepare_covfun_record};
12+
use crate::llvm;
913

1014
/// Each CGU will normally only emit coverage metadata for the functions that it actually generates.
1115
/// But since we don't want unused functions to disappear from coverage reports, we also scan for
@@ -15,9 +19,48 @@ use crate::common::CodegenCx;
1519
/// coverage map (in a single designated CGU) so that we still emit coverage mappings for them.
1620
/// We also end up adding their symbol names to a special global array that LLVM will include in
1721
/// its embedded coverage data.
18-
pub(crate) fn gather_unused_function_instances<'tcx>(
22+
pub(crate) fn prepare_covfun_records_for_unused_functions<'tcx>(
1923
cx: &CodegenCx<'_, 'tcx>,
20-
) -> Vec<ty::Instance<'tcx>> {
24+
global_file_table: &mut GlobalFileTable,
25+
covfun_records: &mut Vec<CovfunRecord<'tcx>>,
26+
) {
27+
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
28+
29+
let mut unused_instances = gather_unused_function_instances(cx);
30+
// Sort the unused instances by symbol name, so that their order isn't hash-sensitive.
31+
unused_instances.sort_by_key(|instance| instance.symbol_name);
32+
33+
// Try to create a covfun record for each unused function.
34+
let mut name_globals = Vec::with_capacity(unused_instances.len());
35+
covfun_records.extend(unused_instances.into_iter().filter_map(|unused| try {
36+
let record = prepare_covfun_record(cx.tcx, global_file_table, unused.instance, false)?;
37+
// If successful, also store its symbol name in a global constant.
38+
name_globals.push(cx.const_str(unused.symbol_name.name).0);
39+
record
40+
}));
41+
42+
// Store the names of unused functions in a specially-named global array.
43+
// LLVM's `InstrProfilling` pass will detect this array, and include the
44+
// referenced names in its `__llvm_prf_names` section.
45+
// (See `llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp`.)
46+
if !name_globals.is_empty() {
47+
let initializer = cx.const_array(cx.type_ptr(), &name_globals);
48+
49+
let array = llvm::add_global(cx.llmod, cx.val_ty(initializer), c"__llvm_coverage_names");
50+
llvm::set_global_constant(array, true);
51+
llvm::set_linkage(array, llvm::Linkage::InternalLinkage);
52+
llvm::set_initializer(array, initializer);
53+
}
54+
}
55+
56+
/// Holds a dummy function instance along with its symbol name, to avoid having
57+
/// to repeatedly query for the name.
58+
struct UnusedInstance<'tcx> {
59+
instance: ty::Instance<'tcx>,
60+
symbol_name: ty::SymbolName<'tcx>,
61+
}
62+
63+
fn gather_unused_function_instances<'tcx>(cx: &CodegenCx<'_, 'tcx>) -> Vec<UnusedInstance<'tcx>> {
2164
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
2265

2366
let tcx = cx.tcx;
@@ -45,6 +88,7 @@ pub(crate) fn gather_unused_function_instances<'tcx>(
4588
.copied()
4689
.filter(|&def_id| is_unused_fn(def_id))
4790
.map(|def_id| make_dummy_instance(tcx, def_id))
91+
.map(|instance| UnusedInstance { instance, symbol_name: tcx.symbol_name(instance) })
4892
.collect::<Vec<_>>()
4993
}
5094

0 commit comments

Comments
 (0)