@@ -5,12 +5,13 @@ use crate::llvm;
55use llvm:: coverageinfo:: CounterMappingRegion ;
66use rustc_codegen_ssa:: coverageinfo:: map:: { Counter , CounterExpression } ;
77use 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 ;
1011use rustc_llvm:: RustString ;
12+ use rustc_middle:: middle:: codegen_fn_attrs:: CodegenFnAttrFlags ;
1113use rustc_middle:: mir:: coverage:: CodeRegion ;
1214use rustc_middle:: ty:: TyCtxt ;
13- use rustc_span:: Symbol ;
1415
1516use std:: ffi:: CString ;
1617
@@ -46,7 +47,7 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
4647 // functions exist. Generate synthetic functions with a (required) single counter, and add the
4748 // MIR `Coverage` code regions to the `function_coverage_map`, before calling
4849 // `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 ( ) {
5051 add_unused_functions ( cx) ;
5152 }
5253
@@ -271,26 +272,35 @@ fn save_function_record(
271272/// `DefId`s (`tcx` query `mir_keys`) minus the codegenned `DefId`s (`tcx` query
272273/// `codegened_and_inlined_items`).
273274///
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.
279279fn 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 ( ) ) ;
281281
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 ;
285283
286284 let ignore_unused_generics = tcx. sess . instrument_coverage_except_unused_generics ( ) ;
287285
288- let all_def_ids : DefIdSet = tcx
286+ let eligible_def_ids : DefIdSet = tcx
289287 . mir_keys ( ( ) )
290288 . iter ( )
291289 . filter_map ( |local_def_id| {
292290 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+ {
294304 return None ;
295305 }
296306 Some ( local_def_id. to_def_id ( ) )
@@ -299,79 +309,17 @@ fn add_unused_functions<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
299309
300310 let codegenned_def_ids = tcx. codegened_and_inlined_items ( ( ) ) ;
301311
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) ;
312314
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 ;
345320 }
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- } ;
358321
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) ;
376324 }
377325}
0 commit comments