@@ -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