11use std:: iter;
22
3- use itertools:: Itertools as _ ;
3+ use itertools:: Itertools ;
44use rustc_abi:: Align ;
55use rustc_codegen_ssa:: traits:: {
66 BaseTypeCodegenMethods , ConstCodegenMethods , StaticCodegenMethods ,
77} ;
88use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap , FxIndexSet } ;
99use rustc_hir:: def_id:: { DefId , LocalDefId } ;
1010use rustc_index:: IndexVec ;
11+ use rustc_middle:: mir;
1112use rustc_middle:: ty:: { self , TyCtxt } ;
12- use rustc_middle:: { bug, mir} ;
1313use rustc_session:: RemapFileNameExt ;
1414use rustc_session:: config:: RemapPathScopeComponents ;
1515use rustc_span:: def_id:: DefIdSet ;
@@ -18,7 +18,6 @@ use tracing::debug;
1818
1919use crate :: common:: CodegenCx ;
2020use crate :: coverageinfo:: llvm_cov;
21- use crate :: coverageinfo:: map_data:: FunctionCoverage ;
2221use crate :: coverageinfo:: mapgen:: covfun:: prepare_covfun_record;
2322use crate :: llvm;
2423
@@ -49,46 +48,40 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
4948
5049 debug ! ( "Generating coverage map for CodegenUnit: `{}`" , cx. codegen_unit. name( ) ) ;
5150
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-
6051 // 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 ( ) ,
6354 None => return ,
6455 } ;
65- if function_coverage_map. is_empty ( ) {
66- // This CGU has no functions with coverage instrumentation.
67- return ;
68- }
6956
70- let all_file_names = function_coverage_map
71- . iter ( )
72- . map ( |( _, fn_cov) | fn_cov. function_coverage_info . body_span )
73- . map ( |span| span_file_name ( tcx, span) ) ;
74- let global_file_table = GlobalFileTable :: new ( all_file_names) ;
57+ // The order of entries in this global file table needs to be deterministic,
58+ // and ideally should also be independent of the details of stable-hashing,
59+ // because coverage tests snapshots (`.cov-map`) can observe the order and
60+ // would need to be re-blessed if it changes. As long as those requirements
61+ // are satisfied, the order can be arbitrary.
62+ let mut global_file_table = GlobalFileTable :: new ( ) ;
7563
76- // Encode all filenames referenced by coverage mappings in this CGU.
77- let filenames_buffer = global_file_table. make_filenames_buffer ( tcx) ;
78- // The `llvm-cov` tool uses this hash to associate each covfun record with
79- // its corresponding filenames table, since the final binary will typically
80- // contain multiple covmap records from different compilation units.
81- let filenames_hash = llvm_cov:: hash_bytes ( & filenames_buffer) ;
82-
83- let mut unused_function_names = Vec :: new ( ) ;
84-
85- let covfun_records = function_coverage_map
86- . into_iter ( )
87- . filter_map ( |( instance, function_coverage) | {
88- prepare_covfun_record ( tcx, & global_file_table, instance, & function_coverage)
89- } )
64+ let mut covfun_records = instances_used
65+ . iter ( )
66+ . copied ( )
67+ // Sort by symbol name, so that the global file table is built in an
68+ // order that doesn't depend on the stable-hash-based order in which
69+ // instances were visited during codegen.
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 ) )
9072 . collect :: < Vec < _ > > ( ) ;
9173
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+
9285 // If there are no covfun records for this CGU, don't generate a covmap record.
9386 // Emitting a covmap record without any covfun records causes `llvm-cov` to
9487 // fail when generating coverage reports, and if there are no covfun records
@@ -98,6 +91,15 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
9891 return ;
9992 }
10093
94+ // Encode all filenames referenced by coverage mappings in this CGU.
95+ let filenames_buffer = global_file_table. make_filenames_buffer ( tcx) ;
96+ // The `llvm-cov` tool uses this hash to associate each covfun record with
97+ // its corresponding filenames table, since the final binary will typically
98+ // contain multiple covmap records from different compilation units.
99+ let filenames_hash = llvm_cov:: hash_bytes ( & filenames_buffer) ;
100+
101+ let mut unused_function_names = vec ! [ ] ;
102+
101103 for covfun in & covfun_records {
102104 unused_function_names. extend ( covfun. mangled_function_name_if_unused ( ) ) ;
103105
@@ -137,22 +139,13 @@ struct GlobalFileTable {
137139}
138140
139141impl GlobalFileTable {
140- fn new ( all_file_names : impl IntoIterator < Item = Symbol > ) -> Self {
141- // Collect all of the filenames into a set. Filenames usually come in
142- // contiguous runs, so we can dedup adjacent ones to save work.
143- let mut raw_file_table = all_file_names. into_iter ( ) . dedup ( ) . collect :: < FxIndexSet < Symbol > > ( ) ;
144-
145- // Sort the file table by its actual string values, not the arbitrary
146- // ordering of its symbols.
147- raw_file_table. sort_unstable_by ( |a, b| a. as_str ( ) . cmp ( b. as_str ( ) ) ) ;
148-
149- Self { raw_file_table }
142+ fn new ( ) -> Self {
143+ Self { raw_file_table : FxIndexSet :: default ( ) }
150144 }
151145
152- fn global_file_id_for_file_name ( & self , file_name : Symbol ) -> GlobalFileId {
153- let raw_id = self . raw_file_table . get_index_of ( & file_name) . unwrap_or_else ( || {
154- bug ! ( "file name not found in prepared global file table: {file_name}" ) ;
155- } ) ;
146+ fn global_file_id_for_file_name ( & mut self , file_name : Symbol ) -> GlobalFileId {
147+ // Ensure the given file has a table entry, and get its index.
148+ let ( raw_id, _) = self . raw_file_table . insert_full ( file_name) ;
156149 // The raw file table doesn't include an entry for the working dir
157150 // (which has ID 0), so add 1 to get the correct ID.
158151 GlobalFileId :: from_usize ( raw_id + 1 )
@@ -264,39 +257,35 @@ fn generate_covmap_record<'ll>(cx: &CodegenCx<'ll, '_>, version: u32, filenames_
264257/// coverage map (in a single designated CGU) so that we still emit coverage mappings for them.
265258/// We also end up adding their symbol names to a special global array that LLVM will include in
266259/// its embedded coverage data.
267- fn add_unused_functions ( cx : & CodegenCx < ' _ , ' _ > ) {
260+ fn gather_unused_function_instances < ' tcx > ( cx : & CodegenCx < ' _ , ' tcx > ) -> Vec < ty :: Instance < ' tcx > > {
268261 assert ! ( cx. codegen_unit. is_code_coverage_dead_code_cgu( ) ) ;
269262
270263 let tcx = cx. tcx ;
271264 let usage = prepare_usage_sets ( tcx) ;
272265
273266 let is_unused_fn = |def_id : LocalDefId | -> bool {
274- let def_id = def_id . to_def_id ( ) ;
275-
276- // To be eligible for "unused function" mappings, a definition must:
277- // - Be function-like
267+ // Usage sets expect `DefId`, so convert from `LocalDefId`.
268+ let d : DefId = LocalDefId :: to_def_id ( def_id ) ;
269+ // To be potentially eligible for "unused function" mappings, a definition must:
270+ // - Be eligible for coverage instrumentation
278271 // - Not participate directly in codegen (or have lost all its coverage statements)
279272 // - Not have any coverage statements inlined into codegenned functions
280- tcx. def_kind ( def_id) . is_fn_like ( )
281- && ( !usage. all_mono_items . contains ( & def_id)
282- || usage. missing_own_coverage . contains ( & def_id) )
283- && !usage. used_via_inlining . contains ( & def_id)
273+ tcx. is_eligible_for_coverage ( def_id)
274+ && ( !usage. all_mono_items . contains ( & d) || usage. missing_own_coverage . contains ( & d) )
275+ && !usage. used_via_inlining . contains ( & d)
284276 } ;
285277
286- // Scan for unused functions that were instrumented for coverage.
287- for def_id in tcx. mir_keys ( ( ) ) . iter ( ) . copied ( ) . filter ( |& def_id| is_unused_fn ( def_id) ) {
288- // Get the coverage info from MIR, skipping functions that were never instrumented.
289- let body = tcx. optimized_mir ( def_id) ;
290- 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.
291282
292- // FIXME(79651): Consider trying to filter out dummy instantiations of
293- // unused generic functions from library crates, because they can produce
294- // "unused instantiation" in coverage reports even when they are actually
295- // used by some downstream crate in the same binary.
296-
297- debug ! ( "generating unused fn: {def_id:?}" ) ;
298- add_unused_function_coverage ( cx, def_id, function_coverage_info) ;
299- }
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 < _ > > ( )
300289}
301290
302291struct UsageSets < ' tcx > {
@@ -361,16 +350,11 @@ fn prepare_usage_sets<'tcx>(tcx: TyCtxt<'tcx>) -> UsageSets<'tcx> {
361350 UsageSets { all_mono_items, used_via_inlining, missing_own_coverage }
362351}
363352
364- fn add_unused_function_coverage < ' tcx > (
365- cx : & CodegenCx < ' _ , ' tcx > ,
366- def_id : LocalDefId ,
367- function_coverage_info : & ' tcx mir:: coverage:: FunctionCoverageInfo ,
368- ) {
369- let tcx = cx. tcx ;
370- 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 ( ) ;
371355
372356 // Make a dummy instance that fills in all generics with placeholders.
373- let instance = ty:: Instance :: new (
357+ ty:: Instance :: new (
374358 def_id,
375359 ty:: GenericArgs :: for_item ( tcx, def_id, |param, _| {
376360 if let ty:: GenericParamDefKind :: Lifetime = param. kind {
@@ -379,9 +363,5 @@ fn add_unused_function_coverage<'tcx>(
379363 tcx. mk_param_from_def ( param)
380364 }
381365 } ) ,
382- ) ;
383-
384- // An unused function's mappings will all be rewritten to map to zero.
385- let function_coverage = FunctionCoverage :: new_unused ( function_coverage_info) ;
386- cx. coverage_cx ( ) . function_coverage_map . borrow_mut ( ) . insert ( instance, function_coverage) ;
366+ )
387367}
0 commit comments