@@ -53,31 +53,20 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
5353 None => return ,
5454 } ;
5555
56- // The order of entries in this global file table needs to be deterministic,
57- // and ideally should also be independent of the details of stable-hashing,
58- // because coverage tests snapshots (`.cov-map`) can observe the order and
59- // would need to be re-blessed if it changes. As long as those requirements
60- // are satisfied, the order can be arbitrary.
61- let mut global_file_table = GlobalFileTable :: new ( ) ;
62-
6356 let mut covfun_records = instances_used
6457 . iter ( )
6558 . copied ( )
6659 // Sort by symbol name, so that the global file table is built in an
6760 // order that doesn't depend on the stable-hash-based order in which
6861 // instances were visited during codegen.
6962 . sorted_by_cached_key ( |& instance| tcx. symbol_name ( instance) . name )
70- . filter_map ( |instance| prepare_covfun_record ( tcx, & mut global_file_table , instance, true ) )
63+ . filter_map ( |instance| prepare_covfun_record ( tcx, instance, true ) )
7164 . collect :: < Vec < _ > > ( ) ;
7265
7366 // In a single designated CGU, also prepare covfun records for functions
7467 // in this crate that were instrumented for coverage, but are unused.
7568 if cx. codegen_unit . is_code_coverage_dead_code_cgu ( ) {
76- unused:: prepare_covfun_records_for_unused_functions (
77- cx,
78- & mut global_file_table,
79- & mut covfun_records,
80- ) ;
69+ unused:: prepare_covfun_records_for_unused_functions ( cx, & mut covfun_records) ;
8170 }
8271
8372 // If there are no covfun records for this CGU, don't generate a covmap record.
@@ -89,68 +78,88 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
8978 return ;
9079 }
9180
92- // Encode all filenames referenced by coverage mappings in this CGU.
93- let filenames_buffer = global_file_table. make_filenames_buffer ( tcx) ;
94- // The `llvm-cov` tool uses this hash to associate each covfun record with
95- // its corresponding filenames table, since the final binary will typically
96- // contain multiple covmap records from different compilation units.
97- let filenames_hash = llvm_cov:: hash_bytes ( & filenames_buffer) ;
81+ // Prepare the global file table for this CGU, containing all paths needed
82+ // by one or more covfun records.
83+ let global_file_table =
84+ GlobalFileTable :: build ( tcx, covfun_records. iter ( ) . flat_map ( |c| c. all_source_files ( ) ) ) ;
9885
9986 for covfun in & covfun_records {
100- covfun:: generate_covfun_record ( cx, filenames_hash , covfun)
87+ covfun:: generate_covfun_record ( cx, & global_file_table , covfun)
10188 }
10289
10390 // Generate the coverage map header, which contains the filenames used by
10491 // this CGU's coverage mappings, and store it in a well-known global.
10592 // (This is skipped if we returned early due to having no covfun records.)
106- generate_covmap_record ( cx, covmap_version, & filenames_buffer) ;
93+ generate_covmap_record ( cx, covmap_version, & global_file_table . filenames_buffer ) ;
10794}
10895
109- /// Maps "global" (per-CGU) file ID numbers to their underlying source files.
96+ /// Maps "global" (per-CGU) file ID numbers to their underlying source file paths.
97+ #[ derive( Debug ) ]
11098struct GlobalFileTable {
11199 /// This "raw" table doesn't include the working dir, so a file's
112100 /// global ID is its index in this set **plus one**.
113- raw_file_table : FxIndexMap < StableSourceFileId , Arc < SourceFile > > ,
101+ raw_file_table : FxIndexMap < StableSourceFileId , String > ,
102+
103+ /// The file table in encoded form (possibly compressed), which can be
104+ /// included directly in this CGU's `__llvm_covmap` record.
105+ filenames_buffer : Vec < u8 > ,
106+
107+ /// Truncated hash of the bytes in `filenames_buffer`.
108+ ///
109+ /// The `llvm-cov` tool uses this hash to associate each covfun record with
110+ /// its corresponding filenames table, since the final binary will typically
111+ /// contain multiple covmap records from different compilation units.
112+ filenames_hash : u64 ,
114113}
115114
116115impl GlobalFileTable {
117- fn new ( ) -> Self {
118- Self { raw_file_table : FxIndexMap :: default ( ) }
119- }
116+ /// Builds a "global file table" for this CGU, mapping numeric IDs to
117+ /// path strings.
118+ fn build < ' a > ( tcx : TyCtxt < ' _ > , all_files : impl Iterator < Item = & ' a SourceFile > ) -> Self {
119+ let mut raw_file_table = FxIndexMap :: default ( ) ;
120+
121+ for file in all_files {
122+ raw_file_table. entry ( file. stable_id ) . or_insert_with ( || {
123+ file. name
124+ . for_scope ( tcx. sess , RemapPathScopeComponents :: MACRO )
125+ . to_string_lossy ( )
126+ . into_owned ( )
127+ } ) ;
128+ }
129+
130+ // FIXME(Zalathar): Consider sorting the file table here, but maybe
131+ // only after adding filename support to coverage-dump, so that the
132+ // table order isn't directly visible in `.coverage-map` snapshots.
133+
134+ let mut table = Vec :: with_capacity ( raw_file_table. len ( ) + 1 ) ;
135+
136+ // Since version 6 of the LLVM coverage mapping format, the first entry
137+ // in the global file table is treated as a base directory, used to
138+ // resolve any other entries that are stored as relative paths.
139+ let base_dir = tcx
140+ . sess
141+ . opts
142+ . working_dir
143+ . for_scope ( tcx. sess , RemapPathScopeComponents :: MACRO )
144+ . to_string_lossy ( ) ;
145+ table. push ( base_dir. as_ref ( ) ) ;
120146
121- fn global_file_id_for_file ( & mut self , file : & Arc < SourceFile > ) -> GlobalFileId {
122- // Ensure the given file has a table entry, and get its index.
123- let entry = self . raw_file_table . entry ( file. stable_id ) ;
124- let raw_id = entry. index ( ) ;
125- entry. or_insert_with ( || Arc :: clone ( file) ) ;
126-
127- // The raw file table doesn't include an entry for the working dir
128- // (which has ID 0), so add 1 to get the correct ID.
129- GlobalFileId :: from_usize ( raw_id + 1 )
130- }
147+ // Add the regular entries after the base directory.
148+ table. extend ( raw_file_table. values ( ) . map ( |name| name. as_str ( ) ) ) ;
131149
132- fn make_filenames_buffer ( & self , tcx : TyCtxt < ' _ > ) -> Vec < u8 > {
133- let mut table = Vec :: with_capacity ( self . raw_file_table . len ( ) + 1 ) ;
134-
135- // LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
136- // requires setting the first filename to the compilation directory.
137- // Since rustc generates coverage maps with relative paths, the
138- // compilation directory can be combined with the relative paths
139- // to get absolute paths, if needed.
140- table. push (
141- tcx. sess
142- . opts
143- . working_dir
144- . for_scope ( tcx. sess , RemapPathScopeComponents :: MACRO )
145- . to_string_lossy ( ) ,
146- ) ;
150+ // Encode the file table into a buffer, and get the hash of its encoded
151+ // bytes, so that we can embed that hash in `__llvm_covfun` records.
152+ let filenames_buffer = llvm_cov:: write_filenames_to_buffer ( & table) ;
153+ let filenames_hash = llvm_cov:: hash_bytes ( & filenames_buffer) ;
147154
148- // Add the regular entries after the base directory.
149- table. extend ( self . raw_file_table . values ( ) . map ( |file| {
150- file. name . for_scope ( tcx. sess , RemapPathScopeComponents :: MACRO ) . to_string_lossy ( )
151- } ) ) ;
155+ Self { raw_file_table, filenames_buffer, filenames_hash }
156+ }
152157
153- llvm_cov:: write_filenames_to_buffer ( & table)
158+ fn get_existing_id ( & self , file : & SourceFile ) -> Option < GlobalFileId > {
159+ let raw_id = self . raw_file_table . get_index_of ( & file. stable_id ) ?;
160+ // The raw file table doesn't include an entry for the base dir
161+ // (which has ID 0), so add 1 to get the correct ID.
162+ Some ( GlobalFileId :: from_usize ( raw_id + 1 ) )
154163 }
155164}
156165
@@ -166,26 +175,31 @@ rustc_index::newtype_index! {
166175 struct LocalFileId { }
167176}
168177
169- /// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU)
170- /// file IDs .
178+ /// Holds a mapping from "local" (per-function) file IDs to their corresponding
179+ /// source files .
171180#[ derive( Debug , Default ) ]
172181struct VirtualFileMapping {
173- local_to_global : IndexVec < LocalFileId , GlobalFileId > ,
174- global_to_local : FxIndexMap < GlobalFileId , LocalFileId > ,
182+ local_file_table : IndexVec < LocalFileId , Arc < SourceFile > > ,
175183}
176184
177185impl VirtualFileMapping {
178- fn local_id_for_global ( & mut self , global_file_id : GlobalFileId ) -> LocalFileId {
179- * self
180- . global_to_local
181- . entry ( global_file_id)
182- . or_insert_with ( || self . local_to_global . push ( global_file_id) )
186+ fn push_file ( & mut self , source_file : & Arc < SourceFile > ) -> LocalFileId {
187+ self . local_file_table . push ( Arc :: clone ( source_file) )
183188 }
184189
185- fn to_vec ( & self ) -> Vec < u32 > {
186- // This clone could be avoided by transmuting `&[GlobalFileId]` to `&[u32]`,
187- // but it isn't hot or expensive enough to justify the extra unsafety.
188- self . local_to_global . iter ( ) . map ( |& global| GlobalFileId :: as_u32 ( global) ) . collect ( )
190+ /// Resolves all of the filenames in this local file mapping to a list of
191+ /// global file IDs in its CGU, for inclusion in this function's
192+ /// `__llvm_covfun` record.
193+ ///
194+ /// The global file IDs are returned as `u32` to make FFI easier.
195+ fn resolve_all ( & self , global_file_table : & GlobalFileTable ) -> Option < Vec < u32 > > {
196+ self . local_file_table
197+ . iter ( )
198+ . map ( |file| try {
199+ let id = global_file_table. get_existing_id ( file) ?;
200+ GlobalFileId :: as_u32 ( id)
201+ } )
202+ . collect :: < Option < Vec < _ > > > ( )
189203 }
190204}
191205
0 commit comments