@@ -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