1
- mod spans;
2
-
3
1
use std:: ffi:: CString ;
4
- use std:: sync :: Arc ;
2
+ use std:: iter ;
5
3
6
4
use itertools:: Itertools as _;
7
5
use rustc_abi:: Align ;
8
6
use rustc_codegen_ssa:: traits:: {
9
7
BaseTypeCodegenMethods , ConstCodegenMethods , StaticCodegenMethods ,
10
8
} ;
11
- use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap } ;
9
+ use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap , FxIndexSet } ;
12
10
use rustc_hir:: def_id:: { DefId , LocalDefId } ;
13
11
use rustc_index:: IndexVec ;
14
12
use rustc_middle:: mir:: coverage:: MappingKind ;
@@ -17,7 +15,7 @@ use rustc_middle::{bug, mir};
17
15
use rustc_session:: RemapFileNameExt ;
18
16
use rustc_session:: config:: RemapPathScopeComponents ;
19
17
use rustc_span:: def_id:: DefIdSet ;
20
- use rustc_span:: { SourceFile , StableSourceFileId } ;
18
+ use rustc_span:: { Span , Symbol } ;
21
19
use rustc_target:: spec:: HasTargetSpec ;
22
20
use tracing:: debug;
23
21
@@ -74,11 +72,11 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
74
72
. map ( |( instance, function_coverage) | ( instance, function_coverage. into_finished ( ) ) )
75
73
. collect :: < Vec < _ > > ( ) ;
76
74
77
- let all_files = function_coverage_entries
75
+ let all_file_names = function_coverage_entries
78
76
. iter ( )
79
77
. map ( |( _, fn_cov) | fn_cov. function_coverage_info . body_span )
80
- . map ( |span| tcx . sess . source_map ( ) . lookup_source_file ( span. lo ( ) ) ) ;
81
- let global_file_table = GlobalFileTable :: new ( all_files ) ;
78
+ . map ( |span| span_file_name ( tcx , span) ) ;
79
+ let global_file_table = GlobalFileTable :: new ( all_file_names ) ;
82
80
83
81
// Encode all filenames referenced by coverage mappings in this CGU.
84
82
let filenames_buffer = global_file_table. make_filenames_buffer ( tcx) ;
@@ -105,8 +103,15 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
105
103
encode_mappings_for_function ( tcx, & global_file_table, & function_coverage) ;
106
104
107
105
if coverage_mapping_buffer. is_empty ( ) {
108
- debug ! ( "function has no mappings to embed; skipping" ) ;
109
- continue ;
106
+ if function_coverage. is_used ( ) {
107
+ bug ! (
108
+ "A used function should have had coverage mapping data but did not: {}" ,
109
+ mangled_function_name
110
+ ) ;
111
+ } else {
112
+ debug ! ( "unused function had no coverage mapping data: {}" , mangled_function_name) ;
113
+ continue ;
114
+ }
110
115
}
111
116
112
117
if !is_used {
@@ -143,62 +148,54 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
143
148
}
144
149
}
145
150
146
- /// Maps "global" (per-CGU) file ID numbers to their underlying source files .
151
+ /// Maps "global" (per-CGU) file ID numbers to their underlying filenames .
147
152
struct GlobalFileTable {
148
- /// This "raw" table doesn't include the working dir, so a file 's
153
+ /// This "raw" table doesn't include the working dir, so a filename 's
149
154
/// global ID is its index in this set **plus one**.
150
- raw_file_table : FxIndexMap < StableSourceFileId , Arc < SourceFile > > ,
155
+ raw_file_table : FxIndexSet < Symbol > ,
151
156
}
152
157
153
158
impl GlobalFileTable {
154
- fn new ( all_files : impl IntoIterator < Item = Arc < SourceFile > > ) -> Self {
155
- // Collect all of the files into a set. Files usually come in contiguous
156
- // runs, so we can dedup adjacent ones to save work.
157
- let mut raw_file_table = all_files
158
- . into_iter ( )
159
- . dedup_by ( |a, b| a. stable_id == b. stable_id )
160
- . map ( |f| ( f. stable_id , f) )
161
- . collect :: < FxIndexMap < StableSourceFileId , Arc < SourceFile > > > ( ) ;
159
+ fn new ( all_file_names : impl IntoIterator < Item = Symbol > ) -> Self {
160
+ // Collect all of the filenames into a set. Filenames usually come in
161
+ // contiguous runs, so we can dedup adjacent ones to save work.
162
+ let mut raw_file_table = all_file_names. into_iter ( ) . dedup ( ) . collect :: < FxIndexSet < Symbol > > ( ) ;
162
163
163
- // Sort the file table by its underlying filenames.
164
- raw_file_table. sort_unstable_by ( |_, a, _, b| {
165
- Ord :: cmp ( & a. name , & b. name ) . then_with ( || Ord :: cmp ( & a. stable_id , & b. stable_id ) )
166
- } ) ;
164
+ // Sort the file table by its actual string values, not the arbitrary
165
+ // ordering of its symbols.
166
+ raw_file_table. sort_unstable_by ( |a, b| a. as_str ( ) . cmp ( b. as_str ( ) ) ) ;
167
167
168
168
Self { raw_file_table }
169
169
}
170
170
171
- fn global_file_id_for_file ( & self , file : & SourceFile ) -> GlobalFileId {
172
- let raw_id = self . raw_file_table . get_index_of ( & file . stable_id ) . unwrap_or_else ( || {
173
- bug ! ( "file not found in prepared global file table: {:?}" , file . name ) ;
171
+ fn global_file_id_for_file_name ( & self , file_name : Symbol ) -> GlobalFileId {
172
+ let raw_id = self . raw_file_table . get_index_of ( & file_name ) . unwrap_or_else ( || {
173
+ bug ! ( "file name not found in prepared global file table: {file_name}" ) ;
174
174
} ) ;
175
175
// The raw file table doesn't include an entry for the working dir
176
176
// (which has ID 0), so add 1 to get the correct ID.
177
177
GlobalFileId :: from_usize ( raw_id + 1 )
178
178
}
179
179
180
180
fn make_filenames_buffer ( & self , tcx : TyCtxt < ' _ > ) -> Vec < u8 > {
181
- let mut table = Vec :: with_capacity ( self . raw_file_table . len ( ) + 1 ) ;
182
-
183
181
// LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
184
182
// requires setting the first filename to the compilation directory.
185
183
// Since rustc generates coverage maps with relative paths, the
186
184
// compilation directory can be combined with the relative paths
187
185
// to get absolute paths, if needed.
188
- table. push (
189
- tcx. sess
190
- . opts
191
- . working_dir
192
- . for_scope ( tcx. sess , RemapPathScopeComponents :: MACRO )
193
- . to_string_lossy ( ) ,
194
- ) ;
195
-
196
- // Add the regular entries after the base directory.
197
- table. extend ( self . raw_file_table . values ( ) . map ( |file| {
198
- file. name . for_scope ( tcx. sess , RemapPathScopeComponents :: MACRO ) . to_string_lossy ( )
199
- } ) ) ;
200
-
201
- llvm_cov:: write_filenames_to_buffer ( table. iter ( ) . map ( |f| f. as_ref ( ) ) )
186
+ use rustc_session:: RemapFileNameExt ;
187
+ use rustc_session:: config:: RemapPathScopeComponents ;
188
+ let working_dir: & str = & tcx
189
+ . sess
190
+ . opts
191
+ . working_dir
192
+ . for_scope ( tcx. sess , RemapPathScopeComponents :: MACRO )
193
+ . to_string_lossy ( ) ;
194
+
195
+ // Insert the working dir at index 0, before the other filenames.
196
+ let filenames =
197
+ iter:: once ( working_dir) . chain ( self . raw_file_table . iter ( ) . map ( Symbol :: as_str) ) ;
198
+ llvm_cov:: write_filenames_to_buffer ( filenames)
202
199
}
203
200
}
204
201
@@ -211,7 +208,7 @@ rustc_index::newtype_index! {
211
208
/// An index into a function's list of global file IDs. That underlying list
212
209
/// of local-to-global mappings will be embedded in the function's record in
213
210
/// the `__llvm_covfun` linker section.
214
- struct LocalFileId { }
211
+ pub ( crate ) struct LocalFileId { }
215
212
}
216
213
217
214
/// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU)
@@ -237,6 +234,13 @@ impl VirtualFileMapping {
237
234
}
238
235
}
239
236
237
+ fn span_file_name ( tcx : TyCtxt < ' _ > , span : Span ) -> Symbol {
238
+ let source_file = tcx. sess . source_map ( ) . lookup_source_file ( span. lo ( ) ) ;
239
+ let name =
240
+ source_file. name . for_scope ( tcx. sess , RemapPathScopeComponents :: MACRO ) . to_string_lossy ( ) ;
241
+ Symbol :: intern ( & name)
242
+ }
243
+
240
244
/// Using the expressions and counter regions collected for a single function,
241
245
/// generate the variable-sized payload of its corresponding `__llvm_covfun`
242
246
/// entry. The payload is returned as a vector of bytes.
@@ -247,13 +251,11 @@ fn encode_mappings_for_function(
247
251
global_file_table : & GlobalFileTable ,
248
252
function_coverage : & FunctionCoverage < ' _ > ,
249
253
) -> Vec < u8 > {
250
- let mapping_spans = function_coverage. mapping_spans ( ) ;
251
- if mapping_spans . is_empty ( ) {
254
+ let counter_regions = function_coverage. counter_regions ( ) ;
255
+ if counter_regions . is_empty ( ) {
252
256
return Vec :: new ( ) ;
253
257
}
254
258
255
- let fn_cov_info = function_coverage. function_coverage_info ;
256
-
257
259
let expressions = function_coverage. counter_expressions ( ) . collect :: < Vec < _ > > ( ) ;
258
260
259
261
let mut virtual_file_mapping = VirtualFileMapping :: default ( ) ;
@@ -263,47 +265,42 @@ fn encode_mappings_for_function(
263
265
let mut mcdc_decision_regions = vec ! [ ] ;
264
266
265
267
// Currently a function's mappings must all be in the same file as its body span.
266
- let source_map = tcx. sess . source_map ( ) ;
267
- let source_file = source_map. lookup_source_file ( fn_cov_info. body_span . lo ( ) ) ;
268
+ let file_name = span_file_name ( tcx, function_coverage. function_coverage_info . body_span ) ;
268
269
269
- // Look up the global file ID for that file .
270
- let global_file_id = global_file_table. global_file_id_for_file ( & source_file ) ;
270
+ // Look up the global file ID for that filename .
271
+ let global_file_id = global_file_table. global_file_id_for_file_name ( file_name ) ;
271
272
272
273
// Associate that global file ID with a local file ID for this function.
273
274
let local_file_id = virtual_file_mapping. local_id_for_global ( global_file_id) ;
275
+ debug ! ( " file id: {local_file_id:?} => {global_file_id:?} = '{file_name:?}'" ) ;
274
276
275
- let make_cov_span = |span| {
276
- spans:: make_coverage_span ( local_file_id, source_map, fn_cov_info, & source_file, span)
277
- } ;
278
-
279
- // For each coverage mapping span in this function+file, convert it to a
277
+ // For each counter/region pair in this function+file, convert it to a
280
278
// form suitable for FFI.
281
- for ( mapping_kind, span ) in mapping_spans {
282
- debug ! ( "Adding counter {mapping_kind:?} to map for {span :?}" ) ;
283
- let Some ( cov_span ) = make_cov_span ( span ) else { continue } ;
279
+ for ( mapping_kind, region ) in counter_regions {
280
+ debug ! ( "Adding counter {mapping_kind:?} to map for {region :?}" ) ;
281
+ let span = ffi :: CoverageSpan :: from_source_region ( local_file_id , region ) ;
284
282
match mapping_kind {
285
283
MappingKind :: Code ( term) => {
286
- code_regions
287
- . push ( ffi:: CodeRegion { cov_span, counter : ffi:: Counter :: from_term ( term) } ) ;
284
+ code_regions. push ( ffi:: CodeRegion { span, counter : ffi:: Counter :: from_term ( term) } ) ;
288
285
}
289
286
MappingKind :: Branch { true_term, false_term } => {
290
287
branch_regions. push ( ffi:: BranchRegion {
291
- cov_span ,
288
+ span ,
292
289
true_counter : ffi:: Counter :: from_term ( true_term) ,
293
290
false_counter : ffi:: Counter :: from_term ( false_term) ,
294
291
} ) ;
295
292
}
296
293
MappingKind :: MCDCBranch { true_term, false_term, mcdc_params } => {
297
294
mcdc_branch_regions. push ( ffi:: MCDCBranchRegion {
298
- cov_span ,
295
+ span ,
299
296
true_counter : ffi:: Counter :: from_term ( true_term) ,
300
297
false_counter : ffi:: Counter :: from_term ( false_term) ,
301
298
mcdc_branch_params : ffi:: mcdc:: BranchParameters :: from ( mcdc_params) ,
302
299
} ) ;
303
300
}
304
301
MappingKind :: MCDCDecision ( mcdc_decision_params) => {
305
302
mcdc_decision_regions. push ( ffi:: MCDCDecisionRegion {
306
- cov_span ,
303
+ span ,
307
304
mcdc_decision_params : ffi:: mcdc:: DecisionParameters :: from ( mcdc_decision_params) ,
308
305
} ) ;
309
306
}
0 commit comments