@@ -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,17 +272,12 @@ 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.
279277fn 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 ( ) ) ;
281279
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 ;
285281
286282 let ignore_unused_generics = tcx. sess . instrument_coverage_except_unused_generics ( ) ;
287283
@@ -299,79 +295,24 @@ fn add_unused_functions<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
299295
300296 let codegenned_def_ids = tcx. codegened_and_inlined_items ( ( ) ) ;
301297
302- let mut unused_def_ids_by_file: FxHashMap < Symbol , Vec < DefId > > = FxHashMap :: default ( ) ;
303298 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 ;
344310 }
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 ( ) ;
367311
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) ;
375316 }
376317 }
377318}
0 commit comments