@@ -18,7 +18,6 @@ use tracing::debug;
18
18
19
19
use crate::common::CodegenCx;
20
20
use crate::coverageinfo::llvm_cov;
21
- use crate::coverageinfo::map_data::FunctionCoverage;
22
21
use crate::coverageinfo::mapgen::covfun::prepare_covfun_record;
23
22
use crate::llvm;
24
23
@@ -49,23 +48,11 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
49
48
50
49
debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name());
51
50
52
- // In order to show that unused functions have coverage counts of zero (0), LLVM requires the
53
- // functions exist. Generate synthetic functions with a (required) single counter, and add the
54
- // MIR `Coverage` code regions to the `function_coverage_map`, before calling
55
- // `ctx.take_function_coverage_map()`.
56
- if cx.codegen_unit.is_code_coverage_dead_code_cgu() {
57
- add_unused_functions(cx);
58
- }
59
-
60
51
// FIXME(#132395): Can this be none even when coverage is enabled?
61
- let function_coverage_map = match cx.coverage_cx {
62
- Some(ref cx) => cx.take_function_coverage_map (),
52
+ let instances_used = match cx.coverage_cx {
53
+ Some(ref cx) => cx.instances_used.borrow (),
63
54
None => return,
64
55
};
65
- if function_coverage_map.is_empty() {
66
- // This CGU has no functions with coverage instrumentation.
67
- return;
68
- }
69
56
70
57
// The order of entries in this global file table needs to be deterministic,
71
58
// and ideally should also be independent of the details of stable-hashing,
@@ -74,18 +61,27 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
74
61
// are satisfied, the order can be arbitrary.
75
62
let mut global_file_table = GlobalFileTable::new();
76
63
77
- let covfun_records = function_coverage_map
78
- .into_iter()
64
+ let mut covfun_records = instances_used
65
+ .iter()
66
+ .copied()
79
67
// Sort by symbol name, so that the global file table is built in an
80
68
// order that doesn't depend on the stable-hash-based order in which
81
69
// instances were visited during codegen.
82
- .sorted_by_cached_key(|&(instance, _)| tcx.symbol_name(instance).name)
83
- .filter_map(|(instance, function_coverage)| {
84
- let is_used = function_coverage.is_used();
85
- prepare_covfun_record(tcx, &mut global_file_table, instance, is_used)
86
- })
70
+ .sorted_by_cached_key(|&instance| tcx.symbol_name(instance).name)
71
+ .filter_map(|instance| prepare_covfun_record(tcx, &mut global_file_table, instance, true))
87
72
.collect::<Vec<_>>();
88
73
74
+ // In a single designated CGU, also prepare covfun records for functions
75
+ // in this crate that were instrumented for coverage, but are unused.
76
+ if cx.codegen_unit.is_code_coverage_dead_code_cgu() {
77
+ let mut unused_instances = gather_unused_function_instances(cx);
78
+ // Sort the unused instances by symbol name, for the same reason as the used ones.
79
+ unused_instances.sort_by_cached_key(|&instance| tcx.symbol_name(instance).name);
80
+ covfun_records.extend(unused_instances.into_iter().filter_map(|instance| {
81
+ prepare_covfun_record(tcx, &mut global_file_table, instance, false)
82
+ }));
83
+ }
84
+
89
85
// If there are no covfun records for this CGU, don't generate a covmap record.
90
86
// Emitting a covmap record without any covfun records causes `llvm-cov` to
91
87
// fail when generating coverage reports, and if there are no covfun records
@@ -261,7 +257,7 @@ fn generate_covmap_record<'ll>(cx: &CodegenCx<'ll, '_>, version: u32, filenames_
261
257
/// coverage map (in a single designated CGU) so that we still emit coverage mappings for them.
262
258
/// We also end up adding their symbol names to a special global array that LLVM will include in
263
259
/// its embedded coverage data.
264
- fn add_unused_functions (cx: &CodegenCx<'_, '_>) {
260
+ fn gather_unused_function_instances<'tcx> (cx: &CodegenCx<'_, 'tcx>) -> Vec<ty::Instance<'tcx>> {
265
261
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
266
262
267
263
let tcx = cx.tcx;
@@ -279,20 +275,17 @@ fn add_unused_functions(cx: &CodegenCx<'_, '_>) {
279
275
&& !usage.used_via_inlining.contains(&d)
280
276
};
281
277
282
- // Scan for unused functions that were instrumented for coverage.
283
- for def_id in tcx.mir_keys(()).iter().copied().filter(|&def_id| is_unused_fn(def_id)) {
284
- // Get the coverage info from MIR, skipping functions that were never instrumented.
285
- let body = tcx.optimized_mir(def_id);
286
- let Some(function_coverage_info) = body.function_coverage_info.as_deref() else { continue };
278
+ // FIXME(#79651): Consider trying to filter out dummy instantiations of
279
+ // unused generic functions from library crates, because they can produce
280
+ // "unused instantiation" in coverage reports even when they are actually
281
+ // used by some downstream crate in the same binary.
287
282
288
- // FIXME(79651): Consider trying to filter out dummy instantiations of
289
- // unused generic functions from library crates, because they can produce
290
- // "unused instantiation" in coverage reports even when they are actually
291
- // used by some downstream crate in the same binary.
292
-
293
- debug!("generating unused fn: {def_id:?}");
294
- add_unused_function_coverage(cx, def_id, function_coverage_info);
295
- }
283
+ tcx.mir_keys(())
284
+ .iter()
285
+ .copied()
286
+ .filter(|&def_id| is_unused_fn(def_id))
287
+ .map(|def_id| make_dummy_instance(tcx, def_id))
288
+ .collect::<Vec<_>>()
296
289
}
297
290
298
291
struct UsageSets<'tcx> {
@@ -357,16 +350,11 @@ fn prepare_usage_sets<'tcx>(tcx: TyCtxt<'tcx>) -> UsageSets<'tcx> {
357
350
UsageSets { all_mono_items, used_via_inlining, missing_own_coverage }
358
351
}
359
352
360
- fn add_unused_function_coverage<'tcx>(
361
- cx: &CodegenCx<'_, 'tcx>,
362
- def_id: LocalDefId,
363
- function_coverage_info: &'tcx mir::coverage::FunctionCoverageInfo,
364
- ) {
365
- let tcx = cx.tcx;
366
- let def_id = def_id.to_def_id();
353
+ fn make_dummy_instance<'tcx>(tcx: TyCtxt<'tcx>, local_def_id: LocalDefId) -> ty::Instance<'tcx> {
354
+ let def_id = local_def_id.to_def_id();
367
355
368
356
// Make a dummy instance that fills in all generics with placeholders.
369
- let instance = ty::Instance::new(
357
+ ty::Instance::new(
370
358
def_id,
371
359
ty::GenericArgs::for_item(tcx, def_id, |param, _| {
372
360
if let ty::GenericParamDefKind::Lifetime = param.kind {
@@ -375,9 +363,5 @@ fn add_unused_function_coverage<'tcx>(
375
363
tcx.mk_param_from_def(param)
376
364
}
377
365
}),
378
- );
379
-
380
- // An unused function's mappings will all be rewritten to map to zero.
381
- let function_coverage = FunctionCoverage::new_unused(function_coverage_info);
382
- cx.coverage_cx().function_coverage_map.borrow_mut().insert(instance, function_coverage);
366
+ )
383
367
}
0 commit comments