@@ -4,14 +4,16 @@ use crate::coverageinfo::ffi::CounterMappingRegion;
4
4
use crate :: coverageinfo:: map_data:: FunctionCoverage ;
5
5
use crate :: llvm;
6
6
7
- use rustc_codegen_ssa:: traits:: ConstMethods ;
7
+ use rustc_codegen_ssa:: traits:: { BaseTypeMethods , ConstMethods } ;
8
8
use rustc_data_structures:: fx:: FxIndexSet ;
9
9
use rustc_hir:: def:: DefKind ;
10
10
use rustc_hir:: def_id:: DefId ;
11
11
use rustc_index:: IndexVec ;
12
12
use rustc_middle:: bug;
13
+ use rustc_middle:: mir;
13
14
use rustc_middle:: mir:: coverage:: CodeRegion ;
14
15
use rustc_middle:: ty:: { self , TyCtxt } ;
16
+ use rustc_span:: def_id:: DefIdSet ;
15
17
use rustc_span:: Symbol ;
16
18
17
19
/// Generates and exports the Coverage Map.
@@ -94,8 +96,14 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
94
96
// Generate the LLVM IR representation of the coverage map and store it in a well-known global
95
97
let cov_data_val = generate_coverage_map ( cx, version, filenames_size, filenames_val) ;
96
98
99
+ let mut unused_function_names = Vec :: new ( ) ;
100
+
97
101
let covfun_section_name = coverageinfo:: covfun_section_name ( cx) ;
98
102
for ( mangled_function_name, source_hash, is_used, coverage_mapping_buffer) in function_data {
103
+ if !is_used {
104
+ unused_function_names. push ( mangled_function_name) ;
105
+ }
106
+
99
107
save_function_record (
100
108
cx,
101
109
& covfun_section_name,
@@ -107,6 +115,25 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
107
115
) ;
108
116
}
109
117
118
+ // For unused functions, we need to take their mangled names and store them
119
+ // in a specially-named global array. LLVM's `InstrProfiling` pass will
120
+ // detect this global and include those names in its `__llvm_prf_names`
121
+ // section. (See `llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp`.)
122
+ if !unused_function_names. is_empty ( ) {
123
+ assert ! ( cx. codegen_unit. is_code_coverage_dead_code_cgu( ) ) ;
124
+
125
+ let name_globals = unused_function_names
126
+ . into_iter ( )
127
+ . map ( |mangled_function_name| cx. const_str ( mangled_function_name) . 0 )
128
+ . collect :: < Vec < _ > > ( ) ;
129
+ let initializer = cx. const_array ( cx. type_ptr ( ) , & name_globals) ;
130
+
131
+ let array = llvm:: add_global ( cx. llmod , cx. val_ty ( initializer) , "__llvm_coverage_names" ) ;
132
+ llvm:: set_global_constant ( array, true ) ;
133
+ llvm:: set_linkage ( array, llvm:: Linkage :: InternalLinkage ) ;
134
+ llvm:: set_initializer ( array, initializer) ;
135
+ }
136
+
110
137
// Save the coverage data value to LLVM IR
111
138
coverageinfo:: save_cov_data_to_mod ( cx, cov_data_val) ;
112
139
}
@@ -287,13 +314,12 @@ fn save_function_record(
287
314
/// `-Clink-dead-code` will not generate code for unused generic functions.)
288
315
///
289
316
/// We can find the unused functions (including generic functions) by the set difference of all MIR
290
- /// `DefId`s (`tcx` query `mir_keys`) minus the codegenned `DefId`s (`tcx` query
291
- /// `codegened_and_inlined_items`).
317
+ /// `DefId`s (`tcx` query `mir_keys`) minus the codegenned `DefId`s (`codegenned_and_inlined_items`).
292
318
///
293
- /// These unused functions are then codegen'd in one of the CGUs which is marked as the
294
- /// "code coverage dead code cgu" during the partitioning process. This prevents us from generating
295
- /// code regions for the same function more than once which can lead to linker errors regarding
296
- /// duplicate symbols .
319
+ /// These unused functions don't need to be codegenned, but we do need to add them to the function
320
+ /// coverage map (in a single designated CGU) so that we still emit coverage mappings for them.
321
+ /// We also end up adding their symbol names to a special global array that LLVM will include in
322
+ /// its embedded coverage data .
297
323
fn add_unused_functions ( cx : & CodegenCx < ' _ , ' _ > ) {
298
324
assert ! ( cx. codegen_unit. is_code_coverage_dead_code_cgu( ) ) ;
299
325
@@ -324,19 +350,80 @@ fn add_unused_functions(cx: &CodegenCx<'_, '_>) {
324
350
} )
325
351
. collect ( ) ;
326
352
327
- let codegenned_def_ids = tcx . codegened_and_inlined_items ( ( ) ) ;
353
+ let codegenned_def_ids = codegenned_and_inlined_items ( tcx ) ;
328
354
329
- for non_codegenned_def_id in
330
- eligible_def_ids . into_iter ( ) . filter ( |id| !codegenned_def_ids . contains ( id ) )
331
- {
355
+ // For each `DefId` that should have coverage instrumentation but wasn't
356
+ // codegenned, add it to the function coverage map as an unused function.
357
+ for def_id in eligible_def_ids . into_iter ( ) . filter ( |id| !codegenned_def_ids . contains ( id ) ) {
332
358
// Skip any function that didn't have coverage data added to it by the
333
359
// coverage instrumentor.
334
- let body = tcx. instance_mir ( ty:: InstanceDef :: Item ( non_codegenned_def_id ) ) ;
360
+ let body = tcx. instance_mir ( ty:: InstanceDef :: Item ( def_id ) ) ;
335
361
let Some ( function_coverage_info) = body. function_coverage_info . as_deref ( ) else {
336
362
continue ;
337
363
} ;
338
364
339
- debug ! ( "generating unused fn: {:?}" , non_codegenned_def_id) ;
340
- cx. define_unused_fn ( non_codegenned_def_id, function_coverage_info) ;
365
+ debug ! ( "generating unused fn: {def_id:?}" ) ;
366
+ let instance = declare_unused_fn ( tcx, def_id) ;
367
+ add_unused_function_coverage ( cx, instance, function_coverage_info) ;
368
+ }
369
+ }
370
+
371
+ /// All items participating in code generation together with (instrumented)
372
+ /// items inlined into them.
373
+ fn codegenned_and_inlined_items ( tcx : TyCtxt < ' _ > ) -> DefIdSet {
374
+ let ( items, cgus) = tcx. collect_and_partition_mono_items ( ( ) ) ;
375
+ let mut visited = DefIdSet :: default ( ) ;
376
+ let mut result = items. clone ( ) ;
377
+
378
+ for cgu in cgus {
379
+ for item in cgu. items ( ) . keys ( ) {
380
+ if let mir:: mono:: MonoItem :: Fn ( ref instance) = item {
381
+ let did = instance. def_id ( ) ;
382
+ if !visited. insert ( did) {
383
+ continue ;
384
+ }
385
+ let body = tcx. instance_mir ( instance. def ) ;
386
+ for block in body. basic_blocks . iter ( ) {
387
+ for statement in & block. statements {
388
+ let mir:: StatementKind :: Coverage ( _) = statement. kind else { continue } ;
389
+ let scope = statement. source_info . scope ;
390
+ if let Some ( inlined) = scope. inlined_instance ( & body. source_scopes ) {
391
+ result. insert ( inlined. def_id ( ) ) ;
392
+ }
393
+ }
394
+ }
395
+ }
396
+ }
397
+ }
398
+
399
+ result
400
+ }
401
+
402
+ fn declare_unused_fn < ' tcx > ( tcx : TyCtxt < ' tcx > , def_id : DefId ) -> ty:: Instance < ' tcx > {
403
+ ty:: Instance :: new (
404
+ def_id,
405
+ ty:: GenericArgs :: for_item ( tcx, def_id, |param, _| {
406
+ if let ty:: GenericParamDefKind :: Lifetime = param. kind {
407
+ tcx. lifetimes . re_erased . into ( )
408
+ } else {
409
+ tcx. mk_param_from_def ( param)
410
+ }
411
+ } ) ,
412
+ )
413
+ }
414
+
415
+ fn add_unused_function_coverage < ' tcx > (
416
+ cx : & CodegenCx < ' _ , ' tcx > ,
417
+ instance : ty:: Instance < ' tcx > ,
418
+ function_coverage_info : & ' tcx mir:: coverage:: FunctionCoverageInfo ,
419
+ ) {
420
+ // An unused function's mappings will automatically be rewritten to map to
421
+ // zero, because none of its counters/expressions are marked as seen.
422
+ let function_coverage = FunctionCoverage :: unused ( instance, function_coverage_info) ;
423
+
424
+ if let Some ( coverage_context) = cx. coverage_context ( ) {
425
+ coverage_context. function_coverage_map . borrow_mut ( ) . insert ( instance, function_coverage) ;
426
+ } else {
427
+ bug ! ( "Could not get the `coverage_context`" ) ;
341
428
}
342
429
}
0 commit comments