@@ -5,15 +5,11 @@ use rustc_abi::Align;
55use rustc_codegen_ssa:: traits:: {
66 BaseTypeCodegenMethods , ConstCodegenMethods , StaticCodegenMethods ,
77} ;
8- use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap } ;
9- use rustc_hir:: def_id:: { DefId , LocalDefId } ;
8+ use rustc_data_structures:: fx:: FxIndexMap ;
109use rustc_index:: IndexVec ;
11- use rustc_middle:: mir;
12- use rustc_middle:: mir:: mono:: MonoItemPartitions ;
13- use rustc_middle:: ty:: { self , TyCtxt } ;
10+ use rustc_middle:: ty:: TyCtxt ;
1411use rustc_session:: RemapFileNameExt ;
1512use rustc_session:: config:: RemapPathScopeComponents ;
16- use rustc_span:: def_id:: DefIdSet ;
1713use rustc_span:: { SourceFile , StableSourceFileId } ;
1814use tracing:: debug;
1915
@@ -24,6 +20,7 @@ use crate::llvm;
2420
2521mod covfun;
2622mod spans;
23+ mod unused;
2724
2825/// Generates and exports the coverage map, which is embedded in special
2926/// linker sections in the final binary.
@@ -76,12 +73,11 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
7673 // In a single designated CGU, also prepare covfun records for functions
7774 // in this crate that were instrumented for coverage, but are unused.
7875 if cx. codegen_unit . is_code_coverage_dead_code_cgu ( ) {
79- let mut unused_instances = gather_unused_function_instances ( cx) ;
80- // Sort the unused instances by symbol name, for the same reason as the used ones.
81- unused_instances. sort_by_cached_key ( |& instance| tcx. symbol_name ( instance) . name ) ;
82- covfun_records. extend ( unused_instances. into_iter ( ) . filter_map ( |instance| {
83- prepare_covfun_record ( tcx, & mut global_file_table, instance, false )
84- } ) ) ;
76+ unused:: prepare_covfun_records_for_unused_functions (
77+ cx,
78+ & mut global_file_table,
79+ & mut covfun_records,
80+ ) ;
8581 }
8682
8783 // If there are no covfun records for this CGU, don't generate a covmap record.
@@ -100,33 +96,10 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
10096 // contain multiple covmap records from different compilation units.
10197 let filenames_hash = llvm_cov:: hash_bytes ( & filenames_buffer) ;
10298
103- let mut unused_function_names = vec ! [ ] ;
104-
10599 for covfun in & covfun_records {
106- unused_function_names. extend ( covfun. mangled_function_name_if_unused ( ) ) ;
107-
108100 covfun:: generate_covfun_record ( cx, filenames_hash, covfun)
109101 }
110102
111- // For unused functions, we need to take their mangled names and store them
112- // in a specially-named global array. LLVM's `InstrProfiling` pass will
113- // detect this global and include those names in its `__llvm_prf_names`
114- // section. (See `llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp`.)
115- if !unused_function_names. is_empty ( ) {
116- assert ! ( cx. codegen_unit. is_code_coverage_dead_code_cgu( ) ) ;
117-
118- let name_globals = unused_function_names
119- . into_iter ( )
120- . map ( |mangled_function_name| cx. const_str ( mangled_function_name) . 0 )
121- . collect :: < Vec < _ > > ( ) ;
122- let initializer = cx. const_array ( cx. type_ptr ( ) , & name_globals) ;
123-
124- let array = llvm:: add_global ( cx. llmod , cx. val_ty ( initializer) , c"__llvm_coverage_names" ) ;
125- llvm:: set_global_constant ( array, true ) ;
126- llvm:: set_linkage ( array, llvm:: Linkage :: InternalLinkage ) ;
127- llvm:: set_initializer ( array, initializer) ;
128- }
129-
130103 // Generate the coverage map header, which contains the filenames used by
131104 // this CGU's coverage mappings, and store it in a well-known global.
132105 // (This is skipped if we returned early due to having no covfun records.)
@@ -249,121 +222,3 @@ fn generate_covmap_record<'ll>(cx: &CodegenCx<'ll, '_>, version: u32, filenames_
249222
250223 cx. add_used_global ( covmap_global) ;
251224}
252-
253- /// Each CGU will normally only emit coverage metadata for the functions that it actually generates.
254- /// But since we don't want unused functions to disappear from coverage reports, we also scan for
255- /// functions that were instrumented but are not participating in codegen.
256- ///
257- /// These unused functions don't need to be codegenned, but we do need to add them to the function
258- /// coverage map (in a single designated CGU) so that we still emit coverage mappings for them.
259- /// We also end up adding their symbol names to a special global array that LLVM will include in
260- /// its embedded coverage data.
261- fn gather_unused_function_instances < ' tcx > ( cx : & CodegenCx < ' _ , ' tcx > ) -> Vec < ty:: Instance < ' tcx > > {
262- assert ! ( cx. codegen_unit. is_code_coverage_dead_code_cgu( ) ) ;
263-
264- let tcx = cx. tcx ;
265- let usage = prepare_usage_sets ( tcx) ;
266-
267- let is_unused_fn = |def_id : LocalDefId | -> bool {
268- // Usage sets expect `DefId`, so convert from `LocalDefId`.
269- let d: DefId = LocalDefId :: to_def_id ( def_id) ;
270- // To be potentially eligible for "unused function" mappings, a definition must:
271- // - Be eligible for coverage instrumentation
272- // - Not participate directly in codegen (or have lost all its coverage statements)
273- // - Not have any coverage statements inlined into codegenned functions
274- tcx. is_eligible_for_coverage ( def_id)
275- && ( !usage. all_mono_items . contains ( & d) || usage. missing_own_coverage . contains ( & d) )
276- && !usage. used_via_inlining . contains ( & d)
277- } ;
278-
279- // FIXME(#79651): Consider trying to filter out dummy instantiations of
280- // unused generic functions from library crates, because they can produce
281- // "unused instantiation" in coverage reports even when they are actually
282- // used by some downstream crate in the same binary.
283-
284- tcx. mir_keys ( ( ) )
285- . iter ( )
286- . copied ( )
287- . filter ( |& def_id| is_unused_fn ( def_id) )
288- . map ( |def_id| make_dummy_instance ( tcx, def_id) )
289- . collect :: < Vec < _ > > ( )
290- }
291-
292- struct UsageSets < ' tcx > {
293- all_mono_items : & ' tcx DefIdSet ,
294- used_via_inlining : FxHashSet < DefId > ,
295- missing_own_coverage : FxHashSet < DefId > ,
296- }
297-
298- /// Prepare sets of definitions that are relevant to deciding whether something
299- /// is an "unused function" for coverage purposes.
300- fn prepare_usage_sets < ' tcx > ( tcx : TyCtxt < ' tcx > ) -> UsageSets < ' tcx > {
301- let MonoItemPartitions { all_mono_items, codegen_units, .. } =
302- tcx. collect_and_partition_mono_items ( ( ) ) ;
303-
304- // Obtain a MIR body for each function participating in codegen, via an
305- // arbitrary instance.
306- let mut def_ids_seen = FxHashSet :: default ( ) ;
307- let def_and_mir_for_all_mono_fns = codegen_units
308- . iter ( )
309- . flat_map ( |cgu| cgu. items ( ) . keys ( ) )
310- . filter_map ( |item| match item {
311- mir:: mono:: MonoItem :: Fn ( instance) => Some ( instance) ,
312- mir:: mono:: MonoItem :: Static ( _) | mir:: mono:: MonoItem :: GlobalAsm ( _) => None ,
313- } )
314- // We only need one arbitrary instance per definition.
315- . filter ( move |instance| def_ids_seen. insert ( instance. def_id ( ) ) )
316- . map ( |instance| {
317- // We don't care about the instance, just its underlying MIR.
318- let body = tcx. instance_mir ( instance. def ) ;
319- ( instance. def_id ( ) , body)
320- } ) ;
321-
322- // Functions whose coverage statements were found inlined into other functions.
323- let mut used_via_inlining = FxHashSet :: default ( ) ;
324- // Functions that were instrumented, but had all of their coverage statements
325- // removed by later MIR transforms (e.g. UnreachablePropagation).
326- let mut missing_own_coverage = FxHashSet :: default ( ) ;
327-
328- for ( def_id, body) in def_and_mir_for_all_mono_fns {
329- let mut saw_own_coverage = false ;
330-
331- // Inspect every coverage statement in the function's MIR.
332- for stmt in body
333- . basic_blocks
334- . iter ( )
335- . flat_map ( |block| & block. statements )
336- . filter ( |stmt| matches ! ( stmt. kind, mir:: StatementKind :: Coverage ( _) ) )
337- {
338- if let Some ( inlined) = stmt. source_info . scope . inlined_instance ( & body. source_scopes ) {
339- // This coverage statement was inlined from another function.
340- used_via_inlining. insert ( inlined. def_id ( ) ) ;
341- } else {
342- // Non-inlined coverage statements belong to the enclosing function.
343- saw_own_coverage = true ;
344- }
345- }
346-
347- if !saw_own_coverage && body. function_coverage_info . is_some ( ) {
348- missing_own_coverage. insert ( def_id) ;
349- }
350- }
351-
352- UsageSets { all_mono_items, used_via_inlining, missing_own_coverage }
353- }
354-
355- fn make_dummy_instance < ' tcx > ( tcx : TyCtxt < ' tcx > , local_def_id : LocalDefId ) -> ty:: Instance < ' tcx > {
356- let def_id = local_def_id. to_def_id ( ) ;
357-
358- // Make a dummy instance that fills in all generics with placeholders.
359- ty:: Instance :: new (
360- def_id,
361- ty:: GenericArgs :: for_item ( tcx, def_id, |param, _| {
362- if let ty:: GenericParamDefKind :: Lifetime = param. kind {
363- tcx. lifetimes . re_erased . into ( )
364- } else {
365- tcx. mk_param_from_def ( param)
366- }
367- } ) ,
368- )
369- }
0 commit comments