@@ -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