1+ mod spans;
2+
13use std:: ffi:: CString ;
2- use std:: iter ;
4+ use std:: sync :: Arc ;
35
46use itertools:: Itertools as _;
57use rustc_abi:: Align ;
68use rustc_codegen_ssa:: traits:: {
79 BaseTypeCodegenMethods , ConstCodegenMethods , StaticCodegenMethods ,
810} ;
9- use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap , FxIndexSet } ;
11+ use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap } ;
1012use rustc_hir:: def_id:: { DefId , LocalDefId } ;
1113use rustc_index:: IndexVec ;
1214use rustc_middle:: mir:: coverage:: MappingKind ;
@@ -15,7 +17,7 @@ use rustc_middle::{bug, mir};
1517use rustc_session:: RemapFileNameExt ;
1618use rustc_session:: config:: RemapPathScopeComponents ;
1719use rustc_span:: def_id:: DefIdSet ;
18- use rustc_span:: { Span , Symbol } ;
20+ use rustc_span:: { SourceFile , StableSourceFileId } ;
1921use rustc_target:: spec:: HasTargetSpec ;
2022use tracing:: debug;
2123
@@ -72,11 +74,11 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
7274 . map ( |( instance, function_coverage) | ( instance, function_coverage. into_finished ( ) ) )
7375 . collect :: < Vec < _ > > ( ) ;
7476
75- let all_file_names = function_coverage_entries
77+ let all_files = function_coverage_entries
7678 . iter ( )
7779 . map ( |( _, fn_cov) | fn_cov. function_coverage_info . body_span )
78- . map ( |span| span_file_name ( tcx, span) ) ;
79- let global_file_table = GlobalFileTable :: new ( all_file_names ) ;
80+ . map ( |span| tcx. sess . source_map ( ) . lookup_source_file ( span. lo ( ) ) ) ;
81+ let global_file_table = GlobalFileTable :: new ( all_files ) ;
8082
8183 // Encode all filenames referenced by coverage mappings in this CGU.
8284 let filenames_buffer = global_file_table. make_filenames_buffer ( tcx) ;
@@ -103,15 +105,8 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
103105 encode_mappings_for_function ( tcx, & global_file_table, & function_coverage) ;
104106
105107 if coverage_mapping_buffer. is_empty ( ) {
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- }
108+ debug ! ( "function has no mappings to embed; skipping" ) ;
109+ continue ;
115110 }
116111
117112 if !is_used {
@@ -148,54 +143,62 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
148143 }
149144}
150145
151- /// Maps "global" (per-CGU) file ID numbers to their underlying filenames .
146+ /// Maps "global" (per-CGU) file ID numbers to their underlying source files .
152147struct GlobalFileTable {
153- /// This "raw" table doesn't include the working dir, so a filename 's
148+ /// This "raw" table doesn't include the working dir, so a file 's
154149 /// global ID is its index in this set **plus one**.
155- raw_file_table : FxIndexSet < Symbol > ,
150+ raw_file_table : FxIndexMap < StableSourceFileId , Arc < SourceFile > > ,
156151}
157152
158153impl GlobalFileTable {
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 > > ( ) ;
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 > > > ( ) ;
163162
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 ( ) ) ) ;
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+ } ) ;
167167
168168 Self { raw_file_table }
169169 }
170170
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}" ) ;
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 ) ;
174174 } ) ;
175175 // The raw file table doesn't include an entry for the working dir
176176 // (which has ID 0), so add 1 to get the correct ID.
177177 GlobalFileId :: from_usize ( raw_id + 1 )
178178 }
179179
180180 fn make_filenames_buffer ( & self , tcx : TyCtxt < ' _ > ) -> Vec < u8 > {
181+ let mut table = Vec :: with_capacity ( self . raw_file_table . len ( ) + 1 ) ;
182+
181183 // LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
182184 // requires setting the first filename to the compilation directory.
183185 // Since rustc generates coverage maps with relative paths, the
184186 // compilation directory can be combined with the relative paths
185187 // to get absolute paths, if needed.
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)
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 ( ) ) )
199202 }
200203}
201204
@@ -208,7 +211,7 @@ rustc_index::newtype_index! {
208211 /// An index into a function's list of global file IDs. That underlying list
209212 /// of local-to-global mappings will be embedded in the function's record in
210213 /// the `__llvm_covfun` linker section.
211- pub ( crate ) struct LocalFileId { }
214+ struct LocalFileId { }
212215}
213216
214217/// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU)
@@ -234,13 +237,6 @@ impl VirtualFileMapping {
234237 }
235238}
236239
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-
244240/// Using the expressions and counter regions collected for a single function,
245241/// generate the variable-sized payload of its corresponding `__llvm_covfun`
246242/// entry. The payload is returned as a vector of bytes.
@@ -251,11 +247,13 @@ fn encode_mappings_for_function(
251247 global_file_table : & GlobalFileTable ,
252248 function_coverage : & FunctionCoverage < ' _ > ,
253249) -> Vec < u8 > {
254- let counter_regions = function_coverage. counter_regions ( ) ;
255- if counter_regions . is_empty ( ) {
250+ let mapping_spans = function_coverage. mapping_spans ( ) ;
251+ if mapping_spans . is_empty ( ) {
256252 return Vec :: new ( ) ;
257253 }
258254
255+ let fn_cov_info = function_coverage. function_coverage_info ;
256+
259257 let expressions = function_coverage. counter_expressions ( ) . collect :: < Vec < _ > > ( ) ;
260258
261259 let mut virtual_file_mapping = VirtualFileMapping :: default ( ) ;
@@ -265,42 +263,47 @@ fn encode_mappings_for_function(
265263 let mut mcdc_decision_regions = vec ! [ ] ;
266264
267265 // Currently a function's mappings must all be in the same file as its body span.
268- let file_name = span_file_name ( tcx, function_coverage. function_coverage_info . 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 ( ) ) ;
269268
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 ) ;
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 ) ;
272271
273272 // Associate that global file ID with a local file ID for this function.
274273 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:?}'" ) ;
276274
277- // For each counter/region pair in this function+file, convert it to a
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
278280 // form suitable for FFI.
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 ) ;
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 } ;
282284 match mapping_kind {
283285 MappingKind :: Code ( term) => {
284- code_regions. push ( ffi:: CodeRegion { span, counter : ffi:: Counter :: from_term ( term) } ) ;
286+ code_regions
287+ . push ( ffi:: CodeRegion { cov_span, counter : ffi:: Counter :: from_term ( term) } ) ;
285288 }
286289 MappingKind :: Branch { true_term, false_term } => {
287290 branch_regions. push ( ffi:: BranchRegion {
288- span ,
291+ cov_span ,
289292 true_counter : ffi:: Counter :: from_term ( true_term) ,
290293 false_counter : ffi:: Counter :: from_term ( false_term) ,
291294 } ) ;
292295 }
293296 MappingKind :: MCDCBranch { true_term, false_term, mcdc_params } => {
294297 mcdc_branch_regions. push ( ffi:: MCDCBranchRegion {
295- span ,
298+ cov_span ,
296299 true_counter : ffi:: Counter :: from_term ( true_term) ,
297300 false_counter : ffi:: Counter :: from_term ( false_term) ,
298301 mcdc_branch_params : ffi:: mcdc:: BranchParameters :: from ( mcdc_params) ,
299302 } ) ;
300303 }
301304 MappingKind :: MCDCDecision ( mcdc_decision_params) => {
302305 mcdc_decision_regions. push ( ffi:: MCDCDecisionRegion {
303- span ,
306+ cov_span ,
304307 mcdc_decision_params : ffi:: mcdc:: DecisionParameters :: from ( mcdc_decision_params) ,
305308 } ) ;
306309 }
0 commit comments