@@ -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
@@ -252,7 +245,7 @@ fn generate_covmap_record<'ll>(cx: &CodegenCx<'ll, '_>, version: u32, filenames_
252245/// coverage map (in a single designated CGU) so that we still emit coverage mappings for them.
253246/// We also end up adding their symbol names to a special global array that LLVM will include in
254247/// its embedded coverage data.
255- fn add_unused_functions ( cx : & CodegenCx < ' _ , ' _ > ) {
248+ fn gather_unused_functions < ' tcx > ( cx : & CodegenCx < ' _ , ' tcx > ) -> Vec < ty :: Instance < ' tcx > > {
256249 assert ! ( cx. codegen_unit. is_code_coverage_dead_code_cgu( ) ) ;
257250
258251 let tcx = cx. tcx ;
@@ -269,20 +262,17 @@ fn add_unused_functions(cx: &CodegenCx<'_, '_>) {
269262 && !usage. used_via_inlining . contains ( & def_id. to_def_id ( ) )
270263 } ;
271264
272- // Scan for unused functions that were instrumented for coverage.
273- for def_id in tcx. mir_keys ( ( ) ) . iter ( ) . copied ( ) . filter ( |& def_id| is_unused_fn ( def_id) ) {
274- // Get the coverage info from MIR, skipping functions that were never instrumented.
275- let body = tcx. optimized_mir ( def_id) ;
276- let Some ( function_coverage_info) = body. function_coverage_info . as_deref ( ) else { continue } ;
265+ // FIXME(79651): Consider trying to filter out dummy instantiations of
266+ // unused generic functions from library crates, because they can produce
267+ // "unused instantiation" in coverage reports even when they are actually
268+ // used by some downstream crate in the same binary.
277269
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.
282-
283- debug ! ( "generating unused fn: {def_id:?}" ) ;
284- add_unused_function_coverage ( cx, def_id, function_coverage_info) ;
285- }
270+ tcx. mir_keys ( ( ) )
271+ . iter ( )
272+ . copied ( )
273+ . filter ( |& def_id| is_unused_fn ( def_id) )
274+ . map ( |def_id| make_dummy_instance ( tcx, def_id) )
275+ . collect :: < Vec < _ > > ( )
286276}
287277
288278struct UsageSets < ' tcx > {
@@ -347,16 +337,11 @@ fn prepare_usage_sets<'tcx>(tcx: TyCtxt<'tcx>) -> UsageSets<'tcx> {
347337 UsageSets { all_mono_items, used_via_inlining, missing_own_coverage }
348338}
349339
350- fn add_unused_function_coverage < ' tcx > (
351- cx : & CodegenCx < ' _ , ' tcx > ,
352- def_id : LocalDefId ,
353- function_coverage_info : & ' tcx mir:: coverage:: FunctionCoverageInfo ,
354- ) {
355- let tcx = cx. tcx ;
356- let def_id = def_id. to_def_id ( ) ;
340+ fn make_dummy_instance < ' tcx > ( tcx : TyCtxt < ' tcx > , local_def_id : LocalDefId ) -> ty:: Instance < ' tcx > {
341+ let def_id = local_def_id. to_def_id ( ) ;
357342
358343 // Make a dummy instance that fills in all generics with placeholders.
359- let instance = ty:: Instance :: new (
344+ ty:: Instance :: new (
360345 def_id,
361346 ty:: GenericArgs :: for_item ( tcx, def_id, |param, _| {
362347 if let ty:: GenericParamDefKind :: Lifetime = param. kind {
@@ -365,9 +350,5 @@ fn add_unused_function_coverage<'tcx>(
365350 tcx. mk_param_from_def ( param)
366351 }
367352 } ) ,
368- ) ;
369-
370- // An unused function's mappings will all be rewritten to map to zero.
371- let function_coverage = FunctionCoverage :: new_unused ( function_coverage_info) ;
372- cx. coverage_cx ( ) . function_coverage_map . borrow_mut ( ) . insert ( instance, function_coverage) ;
353+ )
373354}
0 commit comments