@@ -5,15 +5,11 @@ use rustc_abi::Align;
5
5
use rustc_codegen_ssa:: traits:: {
6
6
BaseTypeCodegenMethods , ConstCodegenMethods , StaticCodegenMethods ,
7
7
} ;
8
- use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap } ;
9
- use rustc_hir:: def_id:: { DefId , LocalDefId } ;
8
+ use rustc_data_structures:: fx:: FxIndexMap ;
10
9
use 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 ;
14
11
use rustc_session:: RemapFileNameExt ;
15
12
use rustc_session:: config:: RemapPathScopeComponents ;
16
- use rustc_span:: def_id:: DefIdSet ;
17
13
use rustc_span:: { SourceFile , StableSourceFileId } ;
18
14
use tracing:: debug;
19
15
@@ -24,6 +20,7 @@ use crate::llvm;
24
20
25
21
mod covfun;
26
22
mod spans;
23
+ mod unused;
27
24
28
25
/// Generates and exports the coverage map, which is embedded in special
29
26
/// linker sections in the final binary.
@@ -76,7 +73,7 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
76
73
// In a single designated CGU, also prepare covfun records for functions
77
74
// in this crate that were instrumented for coverage, but are unused.
78
75
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) ;
80
77
// Sort the unused instances by symbol name, for the same reason as the used ones.
81
78
unused_instances. sort_by_cached_key ( |& instance| tcx. symbol_name ( instance) . name ) ;
82
79
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_
249
246
250
247
cx. add_used_global ( covmap_global) ;
251
248
}
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