@@ -17,7 +17,6 @@ use tracing::debug;
1717
1818use crate :: common:: CodegenCx ;
1919use crate :: coverageinfo:: llvm_cov;
20- use crate :: coverageinfo:: map_data:: FunctionCoverage ;
2120use crate :: coverageinfo:: mapgen:: covfun:: prepare_covfun_record;
2221use crate :: llvm;
2322
@@ -48,34 +47,28 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
4847
4948 debug ! ( "Generating coverage map for CodegenUnit: `{}`" , cx. codegen_unit. name( ) ) ;
5049
51- // In order to show that unused functions have coverage counts of zero (0), LLVM requires the
52- // functions exist. Generate synthetic functions with a (required) single counter, and add the
53- // MIR `Coverage` code regions to the `function_coverage_map`, before calling
54- // `ctx.take_function_coverage_map()`.
55- if cx. codegen_unit . is_code_coverage_dead_code_cgu ( ) {
56- add_unused_functions ( cx) ;
57- }
58-
5950 // FIXME(#132395): Can this be none even when coverage is enabled?
60- let function_coverage_map = match cx. coverage_cx {
61- Some ( ref cx) => cx. take_function_coverage_map ( ) ,
51+ let instances_used = match cx. coverage_cx {
52+ Some ( ref cx) => cx. instances_used . borrow ( ) ,
6253 None => return ,
6354 } ;
64- if function_coverage_map. is_empty ( ) {
65- // This CGU has no functions with coverage instrumentation.
66- return ;
67- }
6855
6956 let mut global_file_table = GlobalFileTable :: new ( ) ;
7057
71- let covfun_records = function_coverage_map
72- . into_iter ( )
73- . filter_map ( |( instance, function_coverage) | {
74- let is_used = function_coverage. is_used ( ) ;
75- prepare_covfun_record ( tcx, & mut global_file_table, instance, is_used)
76- } )
58+ let mut covfun_records = instances_used
59+ . iter ( )
60+ . copied ( )
61+ . filter_map ( |instance| prepare_covfun_record ( tcx, & mut global_file_table, instance, true ) )
7762 . collect :: < Vec < _ > > ( ) ;
7863
64+ // In a single designated CGU, also prepare covfun records for functions
65+ // in this crate that were instrumented for coverage, but are unused.
66+ if cx. codegen_unit . is_code_coverage_dead_code_cgu ( ) {
67+ covfun_records. extend ( gather_unused_functions ( cx) . into_iter ( ) . filter_map ( |instance| {
68+ prepare_covfun_record ( tcx, & mut global_file_table, instance, false )
69+ } ) ) ;
70+ }
71+
7972 // If there are no covfun records for this CGU, don't generate a covmap record.
8073 // Emitting a covmap record without any covfun records causes `llvm-cov` to
8174 // fail when generating coverage reports, and if there are no covfun records
@@ -251,7 +244,7 @@ fn generate_covmap_record<'ll>(cx: &CodegenCx<'ll, '_>, version: u32, filenames_
251244/// coverage map (in a single designated CGU) so that we still emit coverage mappings for them.
252245/// We also end up adding their symbol names to a special global array that LLVM will include in
253246/// its embedded coverage data.
254- fn add_unused_functions ( cx : & CodegenCx < ' _ , ' _ > ) {
247+ fn gather_unused_functions < ' tcx > ( cx : & CodegenCx < ' _ , ' tcx > ) -> Vec < ty :: Instance < ' tcx > > {
255248 assert ! ( cx. codegen_unit. is_code_coverage_dead_code_cgu( ) ) ;
256249
257250 let tcx = cx. tcx ;
@@ -268,20 +261,17 @@ fn add_unused_functions(cx: &CodegenCx<'_, '_>) {
268261 && !usage. used_via_inlining . contains ( & def_id. to_def_id ( ) )
269262 } ;
270263
271- // Scan for unused functions that were instrumented for coverage.
272- for def_id in tcx. mir_keys ( ( ) ) . iter ( ) . copied ( ) . filter ( |& def_id| is_unused_fn ( def_id) ) {
273- // Get the coverage info from MIR, skipping functions that were never instrumented.
274- let body = tcx. optimized_mir ( def_id) ;
275- let Some ( function_coverage_info) = body. function_coverage_info . as_deref ( ) else { continue } ;
264+ // FIXME(79651): Consider trying to filter out dummy instantiations of
265+ // unused generic functions from library crates, because they can produce
266+ // "unused instantiation" in coverage reports even when they are actually
267+ // used by some downstream crate in the same binary.
276268
277- // FIXME(79651): Consider trying to filter out dummy instantiations of
278- // unused generic functions from library crates, because they can produce
279- // "unused instantiation" in coverage reports even when they are actually
280- // used by some downstream crate in the same binary.
281-
282- debug ! ( "generating unused fn: {def_id:?}" ) ;
283- add_unused_function_coverage ( cx, def_id, function_coverage_info) ;
284- }
269+ tcx. mir_keys ( ( ) )
270+ . iter ( )
271+ . copied ( )
272+ . filter ( |& def_id| is_unused_fn ( def_id) )
273+ . map ( |def_id| make_dummy_instance ( tcx, def_id) )
274+ . collect :: < Vec < _ > > ( )
285275}
286276
287277struct UsageSets < ' tcx > {
@@ -346,16 +336,11 @@ fn prepare_usage_sets<'tcx>(tcx: TyCtxt<'tcx>) -> UsageSets<'tcx> {
346336 UsageSets { all_mono_items, used_via_inlining, missing_own_coverage }
347337}
348338
349- fn add_unused_function_coverage < ' tcx > (
350- cx : & CodegenCx < ' _ , ' tcx > ,
351- def_id : LocalDefId ,
352- function_coverage_info : & ' tcx mir:: coverage:: FunctionCoverageInfo ,
353- ) {
354- let tcx = cx. tcx ;
355- let def_id = def_id. to_def_id ( ) ;
339+ fn make_dummy_instance < ' tcx > ( tcx : TyCtxt < ' tcx > , local_def_id : LocalDefId ) -> ty:: Instance < ' tcx > {
340+ let def_id = local_def_id. to_def_id ( ) ;
356341
357342 // Make a dummy instance that fills in all generics with placeholders.
358- let instance = ty:: Instance :: new (
343+ ty:: Instance :: new (
359344 def_id,
360345 ty:: GenericArgs :: for_item ( tcx, def_id, |param, _| {
361346 if let ty:: GenericParamDefKind :: Lifetime = param. kind {
@@ -364,9 +349,5 @@ fn add_unused_function_coverage<'tcx>(
364349 tcx. mk_param_from_def ( param)
365350 }
366351 } ) ,
367- ) ;
368-
369- // An unused function's mappings will all be rewritten to map to zero.
370- let function_coverage = FunctionCoverage :: new_unused ( function_coverage_info) ;
371- cx. coverage_cx ( ) . function_coverage_map . borrow_mut ( ) . insert ( instance, function_coverage) ;
352+ )
372353}
0 commit comments