@@ -3,11 +3,14 @@ use crate::coverageinfo;
3
3
use crate :: llvm;
4
4
5
5
use llvm:: coverageinfo:: CounterMappingRegion ;
6
- use rustc_codegen_ssa:: coverageinfo:: map:: { Counter , CounterExpression } ;
6
+ use rustc_codegen_ssa:: coverageinfo:: map:: { Counter , CounterExpression , FunctionCoverage } ;
7
7
use rustc_codegen_ssa:: traits:: ConstMethods ;
8
- use rustc_data_structures:: fx:: FxIndexSet ;
8
+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet , FxIndexSet } ;
9
+ use rustc_hir:: def_id:: { DefId , DefIdSet , LOCAL_CRATE } ;
9
10
use rustc_llvm:: RustString ;
10
11
use rustc_middle:: mir:: coverage:: CodeRegion ;
12
+ use rustc_middle:: ty:: { Instance , TyCtxt } ;
13
+ use rustc_span:: Symbol ;
11
14
12
15
use std:: ffi:: CString ;
13
16
@@ -26,14 +29,17 @@ use tracing::debug;
26
29
/// undocumented details in Clang's implementation (that may or may not be important) were also
27
30
/// replicated for Rust's Coverage Map.
28
31
pub fn finalize < ' ll , ' tcx > ( cx : & CodegenCx < ' ll , ' tcx > ) {
32
+ let tcx = cx. tcx ;
29
33
// Ensure LLVM supports Coverage Map Version 4 (encoded as a zero-based value: 3).
30
34
// If not, the LLVM Version must be less than 11.
31
35
let version = coverageinfo:: mapping_version ( ) ;
32
36
if version != 3 {
33
- cx . tcx . sess . fatal ( "rustc option `-Z instrument-coverage` requires LLVM 11 or higher." ) ;
37
+ tcx. sess . fatal ( "rustc option `-Z instrument-coverage` requires LLVM 11 or higher." ) ;
34
38
}
35
39
36
- let function_coverage_map = match cx. coverage_context ( ) {
40
+ debug ! ( "Generating coverage map for CodegenUnit: `{}`" , cx. codegen_unit. name( ) ) ;
41
+
42
+ let mut function_coverage_map = match cx. coverage_context ( ) {
37
43
Some ( ctx) => ctx. take_function_coverage_map ( ) ,
38
44
None => return ,
39
45
} ;
@@ -42,14 +48,15 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
42
48
return ;
43
49
}
44
50
51
+ add_unreachable_coverage ( tcx, & mut function_coverage_map) ;
52
+
45
53
let mut mapgen = CoverageMapGenerator :: new ( ) ;
46
54
47
55
// Encode coverage mappings and generate function records
48
56
let mut function_data = Vec :: new ( ) ;
49
57
for ( instance, function_coverage) in function_coverage_map {
50
- debug ! ( "Generate coverage map for: {:?}" , instance) ;
51
-
52
- let mangled_function_name = cx. tcx . symbol_name ( instance) . to_string ( ) ;
58
+ debug ! ( "Generate function coverage for {}, {:?}" , cx. codegen_unit. name( ) , instance) ;
59
+ let mangled_function_name = tcx. symbol_name ( instance) . to_string ( ) ;
53
60
let function_source_hash = function_coverage. source_hash ( ) ;
54
61
let ( expressions, counter_regions) =
55
62
function_coverage. get_expressions_and_counter_regions ( ) ;
@@ -228,3 +235,137 @@ fn save_function_record(
228
235
let is_used = true ;
229
236
coverageinfo:: save_func_record_to_mod ( cx, func_name_hash, func_record_val, is_used) ;
230
237
}
238
+
239
+ /// When finalizing the coverage map, `FunctionCoverage` only has the `CodeRegion`s and counters for
240
+ /// the functions that went through codegen; such as public functions and "used" functions
241
+ /// (functions referenced by other "used" or public items). Any other functions considered unused,
242
+ /// or "Unreachable" were still parsed and processed through the MIR stage.
243
+ ///
244
+ /// We can find the unreachable functions by the set different of all MIR `DefId`s (`tcx` query
245
+ /// `mir_keys`) minus the codegenned `DefId`s (`tcx` query `collect_and_partition_mono_items`).
246
+ ///
247
+ /// *HOWEVER* the codegenned `DefId`s are partitioned across multiple `CodegenUnit`s (CGUs), and
248
+ /// this function is processing a `function_coverage_map` for the functions (`Instance`/`DefId`)
249
+ /// allocated to only one of those CGUs. We must NOT inject any "Unreachable" functions's
250
+ /// `CodeRegion`s more than once, so we have to pick which CGU's `function_coverage_map` to add
251
+ /// each "Unreachable" function to.
252
+ ///
253
+ /// Some constraints:
254
+ ///
255
+ /// 1. The file name of an "Unreachable" function must match the file name of the existing
256
+ /// codegenned (covered) function to which the unreachable code regions will be added.
257
+ /// 2. The function to which the unreachable code regions will be added must not be a genaric
258
+ /// function (must not have type parameters) because the coverage tools will get confused
259
+ /// if the codegenned function has more than one instantiation and additional `CodeRegion`s
260
+ /// attached to only one of those instantiations.
261
+ fn add_unreachable_coverage < ' tcx > (
262
+ tcx : TyCtxt < ' tcx > ,
263
+ function_coverage_map : & mut FxHashMap < Instance < ' tcx > , FunctionCoverage < ' tcx > > ,
264
+ ) {
265
+ // Note: If the crate *only* defines generic functions, there are no codegenerated non-generic
266
+ // functions to add any unreachable code to. In this case, the unreachable code regions will
267
+ // have no coverage, instead of having coverage with zero executions.
268
+ //
269
+ // This is probably still an improvement over Clang, which does not generate any coverage
270
+ // for uninstantiated template functions.
271
+
272
+ let has_non_generic_def_ids =
273
+ function_coverage_map. keys ( ) . any ( |instance| instance. def . attrs ( tcx) . len ( ) == 0 ) ;
274
+
275
+ if !has_non_generic_def_ids {
276
+ // There are no non-generic functions to add unreachable `CodeRegion`s to
277
+ return ;
278
+ }
279
+
280
+ let all_def_ids: DefIdSet =
281
+ tcx. mir_keys ( LOCAL_CRATE ) . iter ( ) . map ( |local_def_id| local_def_id. to_def_id ( ) ) . collect ( ) ;
282
+
283
+ let ( codegenned_def_ids, _) = tcx. collect_and_partition_mono_items ( LOCAL_CRATE ) ;
284
+
285
+ let mut unreachable_def_ids_by_file: FxHashMap < Symbol , Vec < DefId > > = FxHashMap :: default ( ) ;
286
+ for & non_codegenned_def_id in all_def_ids. difference ( codegenned_def_ids) {
287
+ // Make sure the non-codegenned (unreachable) function has a file_name
288
+ if let Some ( non_codegenned_file_name) = tcx. covered_file_name ( non_codegenned_def_id) {
289
+ let def_ids = unreachable_def_ids_by_file
290
+ . entry ( * non_codegenned_file_name)
291
+ . or_insert_with ( || Vec :: new ( ) ) ;
292
+ def_ids. push ( non_codegenned_def_id) ;
293
+ }
294
+ }
295
+
296
+ if unreachable_def_ids_by_file. is_empty ( ) {
297
+ // There are no unreachable functions with file names to add (in any CGU)
298
+ return ;
299
+ }
300
+
301
+ // Since there may be multiple `CodegenUnit`s, some codegenned_def_ids may be codegenned in a
302
+ // different CGU, and will be added to the function_coverage_map for each CGU. Determine which
303
+ // function_coverage_map has the responsibility for publishing unreachable coverage
304
+ // based on file name:
305
+ //
306
+ // For each covered file name, sort ONLY the non-generic codegenned_def_ids, and if
307
+ // covered_def_ids.contains(the first def_id) for a given file_name, add the unreachable code
308
+ // region in this function_coverage_map. Otherwise, ignore it and assume another CGU's
309
+ // function_coverage_map will be adding it (because it will be first for one, and only one,
310
+ // of them).
311
+ let mut sorted_codegenned_def_ids: Vec < DefId > =
312
+ codegenned_def_ids. iter ( ) . map ( |def_id| * def_id) . collect ( ) ;
313
+ sorted_codegenned_def_ids. sort_unstable ( ) ;
314
+
315
+ let mut first_covered_def_id_by_file: FxHashMap < Symbol , DefId > = FxHashMap :: default ( ) ;
316
+ for & def_id in sorted_codegenned_def_ids. iter ( ) {
317
+ // Only consider non-generic functions, to potentially add unreachable code regions
318
+ if tcx. generics_of ( def_id) . count ( ) == 0 {
319
+ if let Some ( covered_file_name) = tcx. covered_file_name ( def_id) {
320
+ // Only add files known to have unreachable functions
321
+ if unreachable_def_ids_by_file. contains_key ( covered_file_name) {
322
+ first_covered_def_id_by_file. entry ( * covered_file_name) . or_insert ( def_id) ;
323
+ }
324
+ }
325
+ }
326
+ }
327
+
328
+ // Get the set of def_ids with coverage regions, known by *this* CoverageContext.
329
+ let cgu_covered_def_ids: DefIdSet =
330
+ function_coverage_map. keys ( ) . map ( |instance| instance. def . def_id ( ) ) . collect ( ) ;
331
+
332
+ let mut cgu_covered_files: FxHashSet < Symbol > = first_covered_def_id_by_file
333
+ . iter ( )
334
+ . filter_map (
335
+ |( & file_name, def_id) | {
336
+ if cgu_covered_def_ids. contains ( def_id) { Some ( file_name) } else { None }
337
+ } ,
338
+ )
339
+ . collect ( ) ;
340
+
341
+ // Find the first covered, non-generic function (instance) for each cgu_covered_file. Take the
342
+ // unreachable code regions for that file, and add them to the function.
343
+ //
344
+ // There are three `for` loops here, but (a) the lists have already been reduced to the minimum
345
+ // required values, the lists are further reduced (by `remove()` calls) when elements are no
346
+ // longer needed, and there are several opportunities to branch out of loops early.
347
+ for ( instance, function_coverage) in function_coverage_map. iter_mut ( ) {
348
+ if instance. def . attrs ( tcx) . len ( ) > 0 {
349
+ continue ;
350
+ }
351
+ // The covered function is not generic...
352
+ let covered_def_id = instance. def . def_id ( ) ;
353
+ if let Some ( covered_file_name) = tcx. covered_file_name ( covered_def_id) {
354
+ if !cgu_covered_files. remove ( & covered_file_name) {
355
+ continue ;
356
+ }
357
+ // The covered function's file is one of the files with unreachable code regions, so
358
+ // all of the unreachable code regions for this file will be added to this function.
359
+ for def_id in
360
+ unreachable_def_ids_by_file. remove ( & covered_file_name) . into_iter ( ) . flatten ( )
361
+ {
362
+ for & region in tcx. covered_code_regions ( def_id) {
363
+ function_coverage. add_unreachable_region ( region. clone ( ) ) ;
364
+ }
365
+ }
366
+ if cgu_covered_files. is_empty ( ) {
367
+ break ;
368
+ }
369
+ }
370
+ }
371
+ }
0 commit comments