1
- use std:: ffi:: CString ;
2
1
use std:: iter;
3
2
4
3
use itertools:: Itertools as _;
@@ -9,21 +8,22 @@ use rustc_codegen_ssa::traits::{
9
8
use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap , FxIndexSet } ;
10
9
use rustc_hir:: def_id:: { DefId , LocalDefId } ;
11
10
use rustc_index:: IndexVec ;
12
- use rustc_middle:: mir:: coverage:: MappingKind ;
13
11
use rustc_middle:: ty:: { self , TyCtxt } ;
14
12
use rustc_middle:: { bug, mir} ;
15
13
use rustc_session:: RemapFileNameExt ;
16
14
use rustc_session:: config:: RemapPathScopeComponents ;
17
15
use rustc_span:: def_id:: DefIdSet ;
18
16
use rustc_span:: { Span , Symbol } ;
19
- use rustc_target:: spec:: HasTargetSpec ;
20
17
use tracing:: debug;
21
18
22
19
use crate :: common:: CodegenCx ;
20
+ use crate :: coverageinfo:: llvm_cov;
23
21
use crate :: coverageinfo:: map_data:: FunctionCoverage ;
24
- use crate :: coverageinfo:: { ffi , llvm_cov } ;
22
+ use crate :: coverageinfo:: mapgen :: covfun :: prepare_covfun_record ;
25
23
use crate :: llvm;
26
24
25
+ mod covfun;
26
+
27
27
/// Generates and exports the coverage map, which is embedded in special
28
28
/// linker sections in the final binary.
29
29
///
@@ -80,47 +80,25 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
80
80
let filenames_val = cx. const_bytes ( & filenames_buffer) ;
81
81
let filenames_ref = llvm_cov:: hash_bytes ( & filenames_buffer) ;
82
82
83
- // Generate the coverage map header, which contains the filenames used by
84
- // this CGU's coverage mappings, and store it in a well-known global.
85
- generate_covmap_record ( cx, covmap_version, filenames_size, filenames_val) ;
86
-
87
83
let mut unused_function_names = Vec :: new ( ) ;
88
84
89
- // Encode coverage mappings and generate function records
90
- for ( instance, function_coverage) in function_coverage_map {
91
- debug ! ( "Generate function coverage for {}, {:?}" , cx. codegen_unit. name( ) , instance) ;
92
-
93
- let mangled_function_name = tcx. symbol_name ( instance) . name ;
94
- let source_hash = function_coverage. source_hash ( ) ;
95
- let is_used = function_coverage. is_used ( ) ;
96
-
97
- let coverage_mapping_buffer =
98
- encode_mappings_for_function ( tcx, & global_file_table, & function_coverage) ;
85
+ let covfun_records = function_coverage_map
86
+ . into_iter ( )
87
+ . filter_map ( |( instance, function_coverage) | {
88
+ prepare_covfun_record ( tcx, & global_file_table, instance, & function_coverage)
89
+ } )
90
+ . collect :: < Vec < _ > > ( ) ;
99
91
100
- if coverage_mapping_buffer. is_empty ( ) {
101
- if function_coverage. is_used ( ) {
102
- bug ! (
103
- "A used function should have had coverage mapping data but did not: {}" ,
104
- mangled_function_name
105
- ) ;
106
- } else {
107
- debug ! ( "unused function had no coverage mapping data: {}" , mangled_function_name) ;
108
- continue ;
109
- }
110
- }
92
+ // If there are no covfun records for this CGU, don't generate a covmap record.
93
+ // This should prevent a repeat of <https://github.com/rust-lang/rust/issues/133606>.
94
+ if covfun_records. is_empty ( ) {
95
+ return ;
96
+ }
111
97
112
- if !is_used {
113
- unused_function_names. push ( mangled_function_name) ;
114
- }
98
+ for covfun in & covfun_records {
99
+ unused_function_names. extend ( covfun. mangled_function_name_if_unused ( ) ) ;
115
100
116
- generate_covfun_record (
117
- cx,
118
- mangled_function_name,
119
- source_hash,
120
- filenames_ref,
121
- coverage_mapping_buffer,
122
- is_used,
123
- ) ;
101
+ covfun:: generate_covfun_record ( cx, filenames_ref, covfun)
124
102
}
125
103
126
104
// For unused functions, we need to take their mangled names and store them
@@ -141,6 +119,10 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
141
119
llvm:: set_linkage ( array, llvm:: Linkage :: InternalLinkage ) ;
142
120
llvm:: set_initializer ( array, initializer) ;
143
121
}
122
+
123
+ // Generate the coverage map header, which contains the filenames used by
124
+ // this CGU's coverage mappings, and store it in a well-known global.
125
+ generate_covmap_record ( cx, covmap_version, filenames_size, filenames_val) ;
144
126
}
145
127
146
128
/// Maps "global" (per-CGU) file ID numbers to their underlying filenames.
@@ -208,7 +190,7 @@ rustc_index::newtype_index! {
208
190
209
191
/// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU)
210
192
/// file IDs.
211
- #[ derive( Default ) ]
193
+ #[ derive( Debug , Default ) ]
212
194
struct VirtualFileMapping {
213
195
local_to_global : IndexVec < LocalFileId , GlobalFileId > ,
214
196
global_to_local : FxIndexMap < GlobalFileId , LocalFileId > ,
@@ -222,10 +204,8 @@ impl VirtualFileMapping {
222
204
. or_insert_with ( || self . local_to_global . push ( global_file_id) )
223
205
}
224
206
225
- fn into_vec ( self ) -> Vec < u32 > {
226
- // This conversion should be optimized away to ~zero overhead.
227
- // In any case, it's probably not hot enough to worry about.
228
- self . local_to_global . into_iter ( ) . map ( |global| global. as_u32 ( ) ) . collect ( )
207
+ fn to_vec ( & self ) -> Vec < u32 > {
208
+ self . local_to_global . iter ( ) . map ( |& global| GlobalFileId :: as_u32 ( global) ) . collect ( )
229
209
}
230
210
}
231
211
@@ -236,83 +216,6 @@ fn span_file_name(tcx: TyCtxt<'_>, span: Span) -> Symbol {
236
216
Symbol :: intern ( & name)
237
217
}
238
218
239
- /// Using the expressions and counter regions collected for a single function,
240
- /// generate the variable-sized payload of its corresponding `__llvm_covfun`
241
- /// entry. The payload is returned as a vector of bytes.
242
- ///
243
- /// Newly-encountered filenames will be added to the global file table.
244
- fn encode_mappings_for_function (
245
- tcx : TyCtxt < ' _ > ,
246
- global_file_table : & GlobalFileTable ,
247
- function_coverage : & FunctionCoverage < ' _ > ,
248
- ) -> Vec < u8 > {
249
- let counter_regions = function_coverage. counter_regions ( ) ;
250
- if counter_regions. is_empty ( ) {
251
- return Vec :: new ( ) ;
252
- }
253
-
254
- let expressions = function_coverage. counter_expressions ( ) . collect :: < Vec < _ > > ( ) ;
255
-
256
- let mut virtual_file_mapping = VirtualFileMapping :: default ( ) ;
257
- let mut code_regions = vec ! [ ] ;
258
- let mut branch_regions = vec ! [ ] ;
259
- let mut mcdc_branch_regions = vec ! [ ] ;
260
- let mut mcdc_decision_regions = vec ! [ ] ;
261
-
262
- // Currently a function's mappings must all be in the same file as its body span.
263
- let file_name = span_file_name ( tcx, function_coverage. function_coverage_info . body_span ) ;
264
-
265
- // Look up the global file ID for that filename.
266
- let global_file_id = global_file_table. global_file_id_for_file_name ( file_name) ;
267
-
268
- // Associate that global file ID with a local file ID for this function.
269
- let local_file_id = virtual_file_mapping. local_id_for_global ( global_file_id) ;
270
- debug ! ( " file id: {local_file_id:?} => {global_file_id:?} = '{file_name:?}'" ) ;
271
-
272
- // For each counter/region pair in this function+file, convert it to a
273
- // form suitable for FFI.
274
- for ( mapping_kind, region) in counter_regions {
275
- debug ! ( "Adding counter {mapping_kind:?} to map for {region:?}" ) ;
276
- let span = ffi:: CoverageSpan :: from_source_region ( local_file_id, region) ;
277
- match mapping_kind {
278
- MappingKind :: Code ( term) => {
279
- code_regions. push ( ffi:: CodeRegion { span, counter : ffi:: Counter :: from_term ( term) } ) ;
280
- }
281
- MappingKind :: Branch { true_term, false_term } => {
282
- branch_regions. push ( ffi:: BranchRegion {
283
- span,
284
- true_counter : ffi:: Counter :: from_term ( true_term) ,
285
- false_counter : ffi:: Counter :: from_term ( false_term) ,
286
- } ) ;
287
- }
288
- MappingKind :: MCDCBranch { true_term, false_term, mcdc_params } => {
289
- mcdc_branch_regions. push ( ffi:: MCDCBranchRegion {
290
- span,
291
- true_counter : ffi:: Counter :: from_term ( true_term) ,
292
- false_counter : ffi:: Counter :: from_term ( false_term) ,
293
- mcdc_branch_params : ffi:: mcdc:: BranchParameters :: from ( mcdc_params) ,
294
- } ) ;
295
- }
296
- MappingKind :: MCDCDecision ( mcdc_decision_params) => {
297
- mcdc_decision_regions. push ( ffi:: MCDCDecisionRegion {
298
- span,
299
- mcdc_decision_params : ffi:: mcdc:: DecisionParameters :: from ( mcdc_decision_params) ,
300
- } ) ;
301
- }
302
- }
303
- }
304
-
305
- // Encode the function's coverage mappings into a buffer.
306
- llvm_cov:: write_function_mappings_to_buffer (
307
- & virtual_file_mapping. into_vec ( ) ,
308
- & expressions,
309
- & code_regions,
310
- & branch_regions,
311
- & mcdc_branch_regions,
312
- & mcdc_decision_regions,
313
- )
314
- }
315
-
316
219
/// Generates the contents of the covmap record for this CGU, which mostly
317
220
/// consists of a header and a list of filenames. The record is then stored
318
221
/// as a global variable in the `__llvm_covmap` section.
@@ -350,61 +253,6 @@ fn generate_covmap_record<'ll>(
350
253
cx. add_used_global ( llglobal) ;
351
254
}
352
255
353
- /// Generates the contents of the covfun record for this function, which
354
- /// contains the function's coverage mapping data. The record is then stored
355
- /// as a global variable in the `__llvm_covfun` section.
356
- fn generate_covfun_record (
357
- cx : & CodegenCx < ' _ , ' _ > ,
358
- mangled_function_name : & str ,
359
- source_hash : u64 ,
360
- filenames_ref : u64 ,
361
- coverage_mapping_buffer : Vec < u8 > ,
362
- is_used : bool ,
363
- ) {
364
- // Concatenate the encoded coverage mappings
365
- let coverage_mapping_size = coverage_mapping_buffer. len ( ) ;
366
- let coverage_mapping_val = cx. const_bytes ( & coverage_mapping_buffer) ;
367
-
368
- let func_name_hash = llvm_cov:: hash_bytes ( mangled_function_name. as_bytes ( ) ) ;
369
- let func_name_hash_val = cx. const_u64 ( func_name_hash) ;
370
- let coverage_mapping_size_val = cx. const_u32 ( coverage_mapping_size as u32 ) ;
371
- let source_hash_val = cx. const_u64 ( source_hash) ;
372
- let filenames_ref_val = cx. const_u64 ( filenames_ref) ;
373
- let func_record_val = cx. const_struct (
374
- & [
375
- func_name_hash_val,
376
- coverage_mapping_size_val,
377
- source_hash_val,
378
- filenames_ref_val,
379
- coverage_mapping_val,
380
- ] ,
381
- /*packed=*/ true ,
382
- ) ;
383
-
384
- // Choose a variable name to hold this function's covfun data.
385
- // Functions that are used have a suffix ("u") to distinguish them from
386
- // unused copies of the same function (from different CGUs), so that if a
387
- // linker sees both it won't discard the used copy's data.
388
- let func_record_var_name =
389
- CString :: new ( format ! ( "__covrec_{:X}{}" , func_name_hash, if is_used { "u" } else { "" } ) )
390
- . unwrap ( ) ;
391
- debug ! ( "function record var name: {:?}" , func_record_var_name) ;
392
-
393
- let llglobal = llvm:: add_global ( cx. llmod , cx. val_ty ( func_record_val) , & func_record_var_name) ;
394
- llvm:: set_initializer ( llglobal, func_record_val) ;
395
- llvm:: set_global_constant ( llglobal, true ) ;
396
- llvm:: set_linkage ( llglobal, llvm:: Linkage :: LinkOnceODRLinkage ) ;
397
- llvm:: set_visibility ( llglobal, llvm:: Visibility :: Hidden ) ;
398
- llvm:: set_section ( llglobal, cx. covfun_section_name ( ) ) ;
399
- // LLVM's coverage mapping format specifies 8-byte alignment for items in this section.
400
- // <https://llvm.org/docs/CoverageMappingFormat.html>
401
- llvm:: set_alignment ( llglobal, Align :: EIGHT ) ;
402
- if cx. target_spec ( ) . supports_comdat ( ) {
403
- llvm:: set_comdat ( cx. llmod , llglobal, & func_record_var_name) ;
404
- }
405
- cx. add_used_global ( llglobal) ;
406
- }
407
-
408
256
/// Each CGU will normally only emit coverage metadata for the functions that it actually generates.
409
257
/// But since we don't want unused functions to disappear from coverage reports, we also scan for
410
258
/// functions that were instrumented but are not participating in codegen.
0 commit comments