@@ -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,26 +272,35 @@ 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. This prevents us from generating
277
+ /// code regions for the same function more than once which can lead to linker errors regarding
278
+ /// duplicate symbols.
279
279
fn add_unused_functions < ' ll , ' tcx > ( cx : & CodegenCx < ' ll , ' tcx > ) {
280
- let tcx = cx. tcx ;
280
+ assert ! ( cx. codegen_unit . is_code_coverage_dead_code_cgu ( ) ) ;
281
281
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)?
282
+ let tcx = cx. tcx ;
285
283
286
284
let ignore_unused_generics = tcx. sess . instrument_coverage_except_unused_generics ( ) ;
287
285
288
- let all_def_ids : DefIdSet = tcx
286
+ let eligible_def_ids : DefIdSet = tcx
289
287
. mir_keys ( ( ) )
290
288
. iter ( )
291
289
. filter_map ( |local_def_id| {
292
290
let def_id = local_def_id. to_def_id ( ) ;
293
- if ignore_unused_generics && tcx. generics_of ( def_id) . requires_monomorphization ( tcx) {
291
+ let kind = tcx. def_kind ( def_id) ;
292
+ // `mir_keys` will give us `DefId`s for all kinds of things, not
293
+ // just "functions", like consts, statics, etc. Filter those out.
294
+ // If `ignore_unused_generics` was specified, filter out any
295
+ // generic functions from consideration as well.
296
+ if !matches ! (
297
+ kind,
298
+ DefKind :: Fn | DefKind :: AssocFn | DefKind :: Closure | DefKind :: Generator
299
+ ) {
300
+ return None ;
301
+ } else if ignore_unused_generics
302
+ && tcx. generics_of ( def_id) . requires_monomorphization ( tcx)
303
+ {
294
304
return None ;
295
305
}
296
306
Some ( local_def_id. to_def_id ( ) )
@@ -299,79 +309,17 @@ fn add_unused_functions<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
299
309
300
310
let codegenned_def_ids = tcx. codegened_and_inlined_items ( ( ) ) ;
301
311
302
- let mut unused_def_ids_by_file: FxHashMap < Symbol , Vec < DefId > > = FxHashMap :: default ( ) ;
303
- 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
+ for & non_codegenned_def_id in eligible_def_ids. difference ( codegenned_def_ids) {
313
+ let codegen_fn_attrs = tcx. codegen_fn_attrs ( non_codegenned_def_id) ;
312
314
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) ;
344
- }
315
+ // If a function is marked `#[no_coverage]`, then skip generating a
316
+ // dead code stub for it.
317
+ if codegen_fn_attrs. flags . contains ( CodegenFnAttrFlags :: NO_COVERAGE ) {
318
+ debug ! ( "skipping unused fn marked #[no_coverage]: {:?}" , non_codegenned_def_id) ;
319
+ continue ;
345
320
}
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
321
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
-
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) ;
375
- }
322
+ debug ! ( "generating unused fn: {:?}" , non_codegenned_def_id) ;
323
+ cx. define_unused_fn ( non_codegenned_def_id) ;
376
324
}
377
325
}
0 commit comments