@@ -5,12 +5,13 @@ use crate::llvm;
5
5
use llvm:: coverageinfo:: CounterMappingRegion ;
6
6
use rustc_codegen_ssa:: coverageinfo:: map:: { Counter , CounterExpression } ;
7
7
use rustc_codegen_ssa:: traits:: { ConstMethods , CoverageInfoMethods } ;
8
- use rustc_data_structures:: fx:: { FxHashMap , FxHashSet , FxIndexSet } ;
9
- use rustc_hir:: def_id:: { DefId , DefIdSet } ;
8
+ use rustc_data_structures:: fx:: FxIndexSet ;
9
+ use rustc_hir:: def:: DefKind ;
10
+ use rustc_hir:: def_id:: DefIdSet ;
10
11
use rustc_llvm:: RustString ;
12
+ use rustc_middle:: middle:: codegen_fn_attrs:: CodegenFnAttrFlags ;
11
13
use rustc_middle:: mir:: coverage:: CodeRegion ;
12
14
use rustc_middle:: ty:: TyCtxt ;
13
- use rustc_span:: Symbol ;
14
15
15
16
use std:: ffi:: CString ;
16
17
@@ -46,7 +47,7 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
46
47
// functions exist. Generate synthetic functions with a (required) single counter, and add the
47
48
// MIR `Coverage` code regions to the `function_coverage_map`, before calling
48
49
// `ctx.take_function_coverage_map()`.
49
- if !tcx . sess . instrument_coverage_except_unused_functions ( ) {
50
+ if cx . codegen_unit . is_code_coverage_dead_code_cgu ( ) {
50
51
add_unused_functions ( cx) ;
51
52
}
52
53
@@ -271,17 +272,12 @@ fn save_function_record(
271
272
/// `DefId`s (`tcx` query `mir_keys`) minus the codegenned `DefId`s (`tcx` query
272
273
/// `codegened_and_inlined_items`).
273
274
///
274
- /// *HOWEVER* the codegenned `DefId`s are partitioned across multiple `CodegenUnit`s (CGUs), and
275
- /// this function is processing a `function_coverage_map` for the functions (`Instance`/`DefId`)
276
- /// allocated to only one of those CGUs. We must NOT inject any unused functions's `CodeRegion`s
277
- /// more than once, so we have to pick a CGUs `function_coverage_map` into which the unused
278
- /// function will be inserted.
275
+ /// These unused functions are then codegen'd in one of the CGUs which is marked as the
276
+ /// "code coverage dead code cgu" during the partitioning process.
279
277
fn add_unused_functions < ' ll , ' tcx > ( cx : & CodegenCx < ' ll , ' tcx > ) {
280
- let tcx = cx. tcx ;
278
+ assert ! ( cx. codegen_unit . is_code_coverage_dead_code_cgu ( ) ) ;
281
279
282
- // FIXME(#79622): Can this solution be simplified and/or improved? Are there other sources
283
- // of compiler state data that might help (or better sources that could be exposed, but
284
- // aren't yet)?
280
+ let tcx = cx. tcx ;
285
281
286
282
let ignore_unused_generics = tcx. sess . instrument_coverage_except_unused_generics ( ) ;
287
283
@@ -299,79 +295,24 @@ fn add_unused_functions<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
299
295
300
296
let codegenned_def_ids = tcx. codegened_and_inlined_items ( ( ) ) ;
301
297
302
- let mut unused_def_ids_by_file: FxHashMap < Symbol , Vec < DefId > > = FxHashMap :: default ( ) ;
303
298
for & non_codegenned_def_id in all_def_ids. difference ( codegenned_def_ids) {
304
- // Make sure the non-codegenned (unused) function has at least one MIR
305
- // `Coverage` statement with a code region, and return its file name.
306
- if let Some ( non_codegenned_file_name) = tcx. covered_file_name ( non_codegenned_def_id) {
307
- let def_ids =
308
- unused_def_ids_by_file. entry ( * non_codegenned_file_name) . or_insert_with ( Vec :: new) ;
309
- def_ids. push ( non_codegenned_def_id) ;
310
- }
311
- }
312
-
313
- if unused_def_ids_by_file. is_empty ( ) {
314
- // There are no unused functions with file names to add (in any CGU)
315
- return ;
316
- }
317
-
318
- // Each `CodegenUnit` (CGU) has its own function_coverage_map, and generates a specific binary
319
- // with its own coverage map.
320
- //
321
- // Each covered function `Instance` can be included in only one coverage map, produced from a
322
- // specific function_coverage_map, from a specific CGU.
323
- //
324
- // Since unused functions did not generate code, they are not associated with any CGU yet.
325
- //
326
- // To avoid injecting the unused functions in multiple coverage maps (for multiple CGUs)
327
- // determine which function_coverage_map has the responsibility for publishing unreachable
328
- // coverage, based on file name: For each unused function, find the CGU that generates the
329
- // first function (based on sorted `DefId`) from the same file.
330
- //
331
- // Add a new `FunctionCoverage` to the `function_coverage_map`, with unreachable code regions
332
- // for each region in it's MIR.
333
-
334
- // Convert the `HashSet` of `codegenned_def_ids` to a sortable vector, and sort them.
335
- let mut sorted_codegenned_def_ids: Vec < DefId > = codegenned_def_ids. iter ( ) . copied ( ) . collect ( ) ;
336
- sorted_codegenned_def_ids. sort_unstable ( ) ;
337
-
338
- let mut first_covered_def_id_by_file: FxHashMap < Symbol , DefId > = FxHashMap :: default ( ) ;
339
- for & def_id in sorted_codegenned_def_ids. iter ( ) {
340
- if let Some ( covered_file_name) = tcx. covered_file_name ( def_id) {
341
- // Only add files known to have unused functions
342
- if unused_def_ids_by_file. contains_key ( covered_file_name) {
343
- first_covered_def_id_by_file. entry ( * covered_file_name) . or_insert ( def_id) ;
299
+ // `all_def_ids` contains things besides just "functions" such as constants,
300
+ // statics, etc. We need to filter those out.
301
+ let kind = tcx. def_kind ( non_codegenned_def_id) ;
302
+ if matches ! ( kind, DefKind :: Fn | DefKind :: AssocFn | DefKind :: Closure | DefKind :: Generator ) {
303
+ let codegen_fn_attrs = tcx. codegen_fn_attrs ( non_codegenned_def_id) ;
304
+
305
+ // If a function is marked `#[no_coverage]`, then skip generating a
306
+ // dead code stub for it.
307
+ if codegen_fn_attrs. flags . contains ( CodegenFnAttrFlags :: NO_COVERAGE ) {
308
+ debug ! ( "skipping unused fn marked #[no_coverage]: {:?}" , non_codegenned_def_id) ;
309
+ continue ;
344
310
}
345
- }
346
- }
347
-
348
- // Get the set of def_ids with coverage regions, known by *this* CoverageContext.
349
- let cgu_covered_def_ids: DefIdSet = match cx. coverage_context ( ) {
350
- Some ( ctx) => ctx
351
- . function_coverage_map
352
- . borrow ( )
353
- . keys ( )
354
- . map ( |& instance| instance. def . def_id ( ) )
355
- . collect ( ) ,
356
- None => return ,
357
- } ;
358
-
359
- let cgu_covered_files: FxHashSet < Symbol > = first_covered_def_id_by_file
360
- . iter ( )
361
- . filter_map (
362
- |( & file_name, def_id) | {
363
- if cgu_covered_def_ids. contains ( def_id) { Some ( file_name) } else { None }
364
- } ,
365
- )
366
- . collect ( ) ;
367
311
368
- // For each file for which this CGU is responsible for adding unused function coverage,
369
- // get the `def_id`s for each unused function (if any), define a synthetic function with a
370
- // single LLVM coverage counter, and add the function's coverage `CodeRegion`s. to the
371
- // function_coverage_map.
372
- for covered_file_name in cgu_covered_files {
373
- for def_id in unused_def_ids_by_file. remove ( & covered_file_name) . into_iter ( ) . flatten ( ) {
374
- cx. define_unused_fn ( def_id) ;
312
+ debug ! ( "generating unused fn: {:?}" , non_codegenned_def_id) ;
313
+ cx. define_unused_fn ( non_codegenned_def_id) ;
314
+ } else {
315
+ debug ! ( "skipping unused {:?}: {:?}" , kind, non_codegenned_def_id) ;
375
316
}
376
317
}
377
318
}
0 commit comments