@@ -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,7 +73,7 @@ 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) ;
76+ let mut unused_instances = unused :: gather_unused_function_instances ( cx) ;
8077 // Sort the unused instances by symbol name, for the same reason as the used ones.
8178 unused_instances. sort_by_cached_key ( |& instance| tcx. symbol_name ( instance) . name ) ;
8279 covfun_records. extend ( unused_instances. into_iter ( ) . filter_map ( |instance| {
@@ -249,121 +246,3 @@ fn generate_covmap_record<'ll>(cx: &CodegenCx<'ll, '_>, version: u32, filenames_
249246
250247 cx. add_used_global ( covmap_global) ;
251248}
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