Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 16e6908

Browse files
committedOct 20, 2023
Auto merge of #116922 - Zalathar:unused, r=<try>
coverage: Emit mappings for unused functions without generating stubs For a while I've been annoyed by the fact that generating coverage maps for unused functions involves generating a stub function at the LLVM level. As I suspected, generating that stub function isn't actually necessary, as long as we specifically tell LLVM about the symbol names of all the functions that have coverage mappings but weren't codegenned (due to being unused). --- There is some helper code that gets moved around in the follow-up patches, so look at the first patch to see the most important functional changes. --- `@rustbot` label +A-code-coverage
2 parents 7db4a89 + d29e54a commit 16e6908

File tree

4 files changed

+103
-150
lines changed

4 files changed

+103
-150
lines changed
 

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

+101-14
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ use crate::coverageinfo::ffi::CounterMappingRegion;
44
use crate::coverageinfo::map_data::FunctionCoverage;
55
use crate::llvm;
66

7-
use rustc_codegen_ssa::traits::ConstMethods;
7+
use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods};
88
use rustc_data_structures::fx::FxIndexSet;
99
use rustc_hir::def::DefKind;
1010
use rustc_hir::def_id::DefId;
1111
use rustc_index::IndexVec;
1212
use rustc_middle::bug;
13+
use rustc_middle::mir;
1314
use rustc_middle::mir::coverage::CodeRegion;
1415
use rustc_middle::ty::{self, TyCtxt};
16+
use rustc_span::def_id::DefIdSet;
1517
use rustc_span::Symbol;
1618

1719
/// Generates and exports the Coverage Map.
@@ -94,8 +96,14 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
9496
// Generate the LLVM IR representation of the coverage map and store it in a well-known global
9597
let cov_data_val = generate_coverage_map(cx, version, filenames_size, filenames_val);
9698

99+
let mut unused_function_names = Vec::new();
100+
97101
let covfun_section_name = coverageinfo::covfun_section_name(cx);
98102
for (mangled_function_name, source_hash, is_used, coverage_mapping_buffer) in function_data {
103+
if !is_used {
104+
unused_function_names.push(mangled_function_name);
105+
}
106+
99107
save_function_record(
100108
cx,
101109
&covfun_section_name,
@@ -107,6 +115,25 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
107115
);
108116
}
109117

118+
// For unused functions, we need to take their mangled names and store them
119+
// in a specially-named global array. LLVM's `InstrProfiling` pass will
120+
// detect this global and include those names in its `__llvm_prf_names`
121+
// section. (See `llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp`.)
122+
if !unused_function_names.is_empty() {
123+
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
124+
125+
let name_globals = unused_function_names
126+
.into_iter()
127+
.map(|mangled_function_name| cx.const_str(mangled_function_name).0)
128+
.collect::<Vec<_>>();
129+
let initializer = cx.const_array(cx.type_ptr(), &name_globals);
130+
131+
let array = llvm::add_global(cx.llmod, cx.val_ty(initializer), "__llvm_coverage_names");
132+
llvm::set_global_constant(array, true);
133+
llvm::set_linkage(array, llvm::Linkage::InternalLinkage);
134+
llvm::set_initializer(array, initializer);
135+
}
136+
110137
// Save the coverage data value to LLVM IR
111138
coverageinfo::save_cov_data_to_mod(cx, cov_data_val);
112139
}
@@ -287,13 +314,12 @@ fn save_function_record(
287314
/// `-Clink-dead-code` will not generate code for unused generic functions.)
288315
///
289316
/// We can find the unused functions (including generic functions) by the set difference of all MIR
290-
/// `DefId`s (`tcx` query `mir_keys`) minus the codegenned `DefId`s (`tcx` query
291-
/// `codegened_and_inlined_items`).
317+
/// `DefId`s (`tcx` query `mir_keys`) minus the codegenned `DefId`s (`codegenned_and_inlined_items`).
292318
///
293-
/// These unused functions are then codegen'd in one of the CGUs which is marked as the
294-
/// "code coverage dead code cgu" during the partitioning process. This prevents us from generating
295-
/// code regions for the same function more than once which can lead to linker errors regarding
296-
/// duplicate symbols.
319+
/// These unused functions don't need to be codegenned, but we do need to add them to the function
320+
/// coverage map (in a single designated CGU) so that we still emit coverage mappings for them.
321+
/// We also end up adding their symbol names to a special global array that LLVM will include in
322+
/// its embedded coverage data.
297323
fn add_unused_functions(cx: &CodegenCx<'_, '_>) {
298324
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
299325

@@ -324,19 +350,80 @@ fn add_unused_functions(cx: &CodegenCx<'_, '_>) {
324350
})
325351
.collect();
326352

327-
let codegenned_def_ids = tcx.codegened_and_inlined_items(());
353+
let codegenned_def_ids = codegenned_and_inlined_items(tcx);
328354

329-
for non_codegenned_def_id in
330-
eligible_def_ids.into_iter().filter(|id| !codegenned_def_ids.contains(id))
331-
{
355+
// For each `DefId` that should have coverage instrumentation but wasn't
356+
// codegenned, add it to the function coverage map as an unused function.
357+
for def_id in eligible_def_ids.into_iter().filter(|id| !codegenned_def_ids.contains(id)) {
332358
// Skip any function that didn't have coverage data added to it by the
333359
// coverage instrumentor.
334-
let body = tcx.instance_mir(ty::InstanceDef::Item(non_codegenned_def_id));
360+
let body = tcx.instance_mir(ty::InstanceDef::Item(def_id));
335361
let Some(function_coverage_info) = body.function_coverage_info.as_deref() else {
336362
continue;
337363
};
338364

339-
debug!("generating unused fn: {:?}", non_codegenned_def_id);
340-
cx.define_unused_fn(non_codegenned_def_id, function_coverage_info);
365+
debug!("generating unused fn: {def_id:?}");
366+
let instance = declare_unused_fn(tcx, def_id);
367+
add_unused_function_coverage(cx, instance, function_coverage_info);
368+
}
369+
}
370+
371+
/// All items participating in code generation together with (instrumented)
372+
/// items inlined into them.
373+
fn codegenned_and_inlined_items(tcx: TyCtxt<'_>) -> DefIdSet {
374+
let (items, cgus) = tcx.collect_and_partition_mono_items(());
375+
let mut visited = DefIdSet::default();
376+
let mut result = items.clone();
377+
378+
for cgu in cgus {
379+
for item in cgu.items().keys() {
380+
if let mir::mono::MonoItem::Fn(ref instance) = item {
381+
let did = instance.def_id();
382+
if !visited.insert(did) {
383+
continue;
384+
}
385+
let body = tcx.instance_mir(instance.def);
386+
for block in body.basic_blocks.iter() {
387+
for statement in &block.statements {
388+
let mir::StatementKind::Coverage(_) = statement.kind else { continue };
389+
let scope = statement.source_info.scope;
390+
if let Some(inlined) = scope.inlined_instance(&body.source_scopes) {
391+
result.insert(inlined.def_id());
392+
}
393+
}
394+
}
395+
}
396+
}
397+
}
398+
399+
result
400+
}
401+
402+
fn declare_unused_fn<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::Instance<'tcx> {
403+
ty::Instance::new(
404+
def_id,
405+
ty::GenericArgs::for_item(tcx, def_id, |param, _| {
406+
if let ty::GenericParamDefKind::Lifetime = param.kind {
407+
tcx.lifetimes.re_erased.into()
408+
} else {
409+
tcx.mk_param_from_def(param)
410+
}
411+
}),
412+
)
413+
}
414+
415+
fn add_unused_function_coverage<'tcx>(
416+
cx: &CodegenCx<'_, 'tcx>,
417+
instance: ty::Instance<'tcx>,
418+
function_coverage_info: &'tcx mir::coverage::FunctionCoverageInfo,
419+
) {
420+
// An unused function's mappings will automatically be rewritten to map to
421+
// zero, because none of its counters/expressions are marked as seen.
422+
let function_coverage = FunctionCoverage::unused(instance, function_coverage_info);
423+
424+
if let Some(coverage_context) = cx.coverage_context() {
425+
coverage_context.function_coverage_map.borrow_mut().insert(instance, function_coverage);
426+
} else {
427+
bug!("Could not get the `coverage_context`");
341428
}
342429
}

‎compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

+2-99
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use crate::llvm;
22

3-
use crate::abi::Abi;
43
use crate::builder::Builder;
54
use crate::common::CodegenCx;
65
use crate::coverageinfo::ffi::{CounterExpression, CounterMappingRegion};
@@ -12,26 +11,19 @@ use rustc_codegen_ssa::traits::{
1211
StaticMethods,
1312
};
1413
use rustc_data_structures::fx::FxHashMap;
15-
use rustc_hir as hir;
16-
use rustc_hir::def_id::DefId;
1714
use rustc_llvm::RustString;
1815
use rustc_middle::bug;
19-
use rustc_middle::mir::coverage::{CounterId, CoverageKind, FunctionCoverageInfo};
16+
use rustc_middle::mir::coverage::CoverageKind;
2017
use rustc_middle::mir::Coverage;
21-
use rustc_middle::ty;
22-
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt};
23-
use rustc_middle::ty::GenericArgs;
18+
use rustc_middle::ty::layout::HasTyCtxt;
2419
use rustc_middle::ty::Instance;
25-
use rustc_middle::ty::Ty;
2620

2721
use std::cell::RefCell;
2822

2923
pub(crate) mod ffi;
3024
pub(crate) mod map_data;
3125
pub mod mapgen;
3226

33-
const UNUSED_FUNCTION_COUNTER_ID: CounterId = CounterId::START;
34-
3527
const VAR_ALIGN_BYTES: usize = 8;
3628

3729
/// A context object for maintaining all state needed by the coverageinfo module.
@@ -76,25 +68,6 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
7668
bug!("Could not get the `coverage_context`");
7769
}
7870
}
79-
80-
/// Functions with MIR-based coverage are normally codegenned _only_ if
81-
/// called. LLVM coverage tools typically expect every function to be
82-
/// defined (even if unused), with at least one call to LLVM intrinsic
83-
/// `instrprof.increment`.
84-
///
85-
/// Codegen a small function that will never be called, with one counter
86-
/// that will never be incremented.
87-
///
88-
/// For used/called functions, the coverageinfo was already added to the
89-
/// `function_coverage_map` (keyed by function `Instance`) during codegen.
90-
/// But in this case, since the unused function was _not_ previously
91-
/// codegenned, collect the function coverage info from MIR and add an
92-
/// "unused" entry to the function coverage map.
93-
fn define_unused_fn(&self, def_id: DefId, function_coverage_info: &'tcx FunctionCoverageInfo) {
94-
let instance = declare_unused_fn(self, def_id);
95-
codegen_unused_fn_and_counter(self, instance);
96-
add_unused_function_coverage(self, instance, function_coverage_info);
97-
}
9871
}
9972

10073
impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
@@ -159,76 +132,6 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
159132
}
160133
}
161134

162-
fn declare_unused_fn<'tcx>(cx: &CodegenCx<'_, 'tcx>, def_id: DefId) -> Instance<'tcx> {
163-
let tcx = cx.tcx;
164-
165-
let instance = Instance::new(
166-
def_id,
167-
GenericArgs::for_item(tcx, def_id, |param, _| {
168-
if let ty::GenericParamDefKind::Lifetime = param.kind {
169-
tcx.lifetimes.re_erased.into()
170-
} else {
171-
tcx.mk_param_from_def(param)
172-
}
173-
}),
174-
);
175-
176-
let llfn = cx.declare_fn(
177-
tcx.symbol_name(instance).name,
178-
cx.fn_abi_of_fn_ptr(
179-
ty::Binder::dummy(tcx.mk_fn_sig(
180-
[Ty::new_unit(tcx)],
181-
Ty::new_unit(tcx),
182-
false,
183-
hir::Unsafety::Unsafe,
184-
Abi::Rust,
185-
)),
186-
ty::List::empty(),
187-
),
188-
None,
189-
);
190-
191-
llvm::set_linkage(llfn, llvm::Linkage::PrivateLinkage);
192-
llvm::set_visibility(llfn, llvm::Visibility::Default);
193-
194-
assert!(cx.instances.borrow_mut().insert(instance, llfn).is_none());
195-
196-
instance
197-
}
198-
199-
fn codegen_unused_fn_and_counter<'tcx>(cx: &CodegenCx<'_, 'tcx>, instance: Instance<'tcx>) {
200-
let llfn = cx.get_fn(instance);
201-
let llbb = Builder::append_block(cx, llfn, "unused_function");
202-
let mut bx = Builder::build(cx, llbb);
203-
let fn_name = bx.get_pgo_func_name_var(instance);
204-
let hash = bx.const_u64(0);
205-
let num_counters = bx.const_u32(1);
206-
let index = bx.const_u32(u32::from(UNUSED_FUNCTION_COUNTER_ID));
207-
debug!(
208-
"codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?},
209-
index={:?}) for unused function: {:?}",
210-
fn_name, hash, num_counters, index, instance
211-
);
212-
bx.instrprof_increment(fn_name, hash, num_counters, index);
213-
bx.ret_void();
214-
}
215-
216-
fn add_unused_function_coverage<'tcx>(
217-
cx: &CodegenCx<'_, 'tcx>,
218-
instance: Instance<'tcx>,
219-
function_coverage_info: &'tcx FunctionCoverageInfo,
220-
) {
221-
// An unused function's mappings will automatically be rewritten to map to
222-
// zero, because none of its counters/expressions are marked as seen.
223-
let function_coverage = FunctionCoverage::unused(instance, function_coverage_info);
224-
225-
if let Some(coverage_context) = cx.coverage_context() {
226-
coverage_context.function_coverage_map.borrow_mut().insert(instance, function_coverage);
227-
} else {
228-
bug!("Could not get the `coverage_context`");
229-
}
230-
}
231-
232135
/// Calls llvm::createPGOFuncNameVar() with the given function instance's
233136
/// mangled function name. The LLVM API returns an llvm::GlobalVariable
234137
/// containing the function name, with the specific variable name and linkage

‎compiler/rustc_middle/src/query/mod.rs

-6
Original file line numberDiff line numberDiff line change
@@ -1882,12 +1882,6 @@ rustc_queries! {
18821882
desc { |tcx| "determining whether `{}` needs codegen", tcx.def_path_str(def_id) }
18831883
}
18841884

1885-
/// All items participating in code generation together with items inlined into them.
1886-
query codegened_and_inlined_items(_: ()) -> &'tcx DefIdSet {
1887-
eval_always
1888-
desc { "collecting codegened and inlined items" }
1889-
}
1890-
18911885
query codegen_unit(sym: Symbol) -> &'tcx CodegenUnit<'tcx> {
18921886
desc { "getting codegen unit `{sym}`" }
18931887
}

‎compiler/rustc_monomorphize/src/partitioning.rs

-31
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ use rustc_hir::def_id::{DefId, DefIdSet, LOCAL_CRATE};
105105
use rustc_hir::definitions::DefPathDataName;
106106
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
107107
use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
108-
use rustc_middle::mir;
109108
use rustc_middle::mir::mono::{
110109
CodegenUnit, CodegenUnitNameBuilder, InstantiationMode, Linkage, MonoItem, MonoItemData,
111110
Visibility,
@@ -1279,38 +1278,8 @@ fn dump_mono_items_stats<'tcx>(
12791278
Ok(())
12801279
}
12811280

1282-
fn codegened_and_inlined_items(tcx: TyCtxt<'_>, (): ()) -> &DefIdSet {
1283-
let (items, cgus) = tcx.collect_and_partition_mono_items(());
1284-
let mut visited = DefIdSet::default();
1285-
let mut result = items.clone();
1286-
1287-
for cgu in cgus {
1288-
for item in cgu.items().keys() {
1289-
if let MonoItem::Fn(ref instance) = item {
1290-
let did = instance.def_id();
1291-
if !visited.insert(did) {
1292-
continue;
1293-
}
1294-
let body = tcx.instance_mir(instance.def);
1295-
for block in body.basic_blocks.iter() {
1296-
for statement in &block.statements {
1297-
let mir::StatementKind::Coverage(_) = statement.kind else { continue };
1298-
let scope = statement.source_info.scope;
1299-
if let Some(inlined) = scope.inlined_instance(&body.source_scopes) {
1300-
result.insert(inlined.def_id());
1301-
}
1302-
}
1303-
}
1304-
}
1305-
}
1306-
}
1307-
1308-
tcx.arena.alloc(result)
1309-
}
1310-
13111281
pub fn provide(providers: &mut Providers) {
13121282
providers.collect_and_partition_mono_items = collect_and_partition_mono_items;
1313-
providers.codegened_and_inlined_items = codegened_and_inlined_items;
13141283

13151284
providers.is_codegened_item = |tcx, def_id| {
13161285
let (all_mono_items, _) = tcx.collect_and_partition_mono_items(());

0 commit comments

Comments
 (0)
Please sign in to comment.