@@ -55,7 +55,7 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
5555 return ;
5656 }
5757
58- let mut mapgen = CoverageMapGenerator :: new ( tcx) ;
58+ let mut global_file_table = GlobalFileTable :: new ( tcx) ;
5959
6060 // Encode coverage mappings and generate function records
6161 let mut function_data = Vec :: new ( ) ;
@@ -70,7 +70,12 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
7070 function_coverage. get_expressions_and_counter_regions ( ) ;
7171
7272 let coverage_mapping_buffer = llvm:: build_byte_buffer ( |coverage_mapping_buffer| {
73- mapgen. write_coverage_mapping ( expressions, counter_regions, coverage_mapping_buffer) ;
73+ write_coverage_mapping (
74+ & mut global_file_table,
75+ expressions,
76+ counter_regions,
77+ coverage_mapping_buffer,
78+ ) ;
7479 } ) ;
7580
7681 if coverage_mapping_buffer. is_empty ( ) {
@@ -89,19 +94,14 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
8994 }
9095
9196 // Encode all filenames referenced by counters/expressions in this module
92- let filenames_buffer = llvm:: build_byte_buffer ( |filenames_buffer| {
93- coverageinfo:: write_filenames_section_to_buffer (
94- mapgen. filenames . iter ( ) . map ( Symbol :: as_str) ,
95- filenames_buffer,
96- ) ;
97- } ) ;
97+ let filenames_buffer = global_file_table. into_filenames_buffer ( ) ;
9898
9999 let filenames_size = filenames_buffer. len ( ) ;
100100 let filenames_val = cx. const_bytes ( & filenames_buffer) ;
101101 let filenames_ref = coverageinfo:: hash_bytes ( & filenames_buffer) ;
102102
103103 // Generate the LLVM IR representation of the coverage map and store it in a well-known global
104- let cov_data_val = mapgen . generate_coverage_map ( cx, version, filenames_size, filenames_val) ;
104+ let cov_data_val = generate_coverage_map ( cx, version, filenames_size, filenames_val) ;
105105
106106 let covfun_section_name = coverageinfo:: covfun_section_name ( cx) ;
107107 for ( mangled_function_name, source_hash, is_used, coverage_mapping_buffer) in function_data {
@@ -120,13 +120,13 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
120120 coverageinfo:: save_cov_data_to_mod ( cx, cov_data_val) ;
121121}
122122
123- struct CoverageMapGenerator {
124- filenames : FxIndexSet < Symbol > ,
123+ struct GlobalFileTable {
124+ global_file_table : FxIndexSet < Symbol > ,
125125}
126126
127- impl CoverageMapGenerator {
127+ impl GlobalFileTable {
128128 fn new ( tcx : TyCtxt < ' _ > ) -> Self {
129- let mut filenames = FxIndexSet :: default ( ) ;
129+ let mut global_file_table = FxIndexSet :: default ( ) ;
130130 // LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
131131 // requires setting the first filename to the compilation directory.
132132 // Since rustc generates coverage maps with relative paths, the
@@ -135,94 +135,110 @@ impl CoverageMapGenerator {
135135 let working_dir = Symbol :: intern (
136136 & tcx. sess . opts . working_dir . remapped_path_if_available ( ) . to_string_lossy ( ) ,
137137 ) ;
138- filenames . insert ( working_dir) ;
139- Self { filenames }
138+ global_file_table . insert ( working_dir) ;
139+ Self { global_file_table }
140140 }
141141
142- /// Using the `expressions` and `counter_regions` collected for the current function, generate
143- /// the `mapping_regions` and `virtual_file_mapping`, and capture any new filenames. Then use
144- /// LLVM APIs to encode the `virtual_file_mapping`, `expressions`, and `mapping_regions` into
145- /// the given `coverage_mapping` byte buffer, compliant with the LLVM Coverage Mapping format.
146- fn write_coverage_mapping < ' a > (
147- & mut self ,
148- expressions : Vec < CounterExpression > ,
149- counter_regions : impl Iterator < Item = ( Counter , & ' a CodeRegion ) > ,
150- coverage_mapping_buffer : & RustString ,
151- ) {
152- let mut counter_regions = counter_regions. collect :: < Vec < _ > > ( ) ;
153- if counter_regions. is_empty ( ) {
154- return ;
155- }
142+ fn global_file_id_for_file_name ( & mut self , file_name : Symbol ) -> u32 {
143+ let ( global_file_id, _) = self . global_file_table . insert_full ( file_name) ;
144+ global_file_id as u32
145+ }
156146
157- let mut virtual_file_mapping = Vec :: new ( ) ;
158- let mut mapping_regions = Vec :: new ( ) ;
159- let mut current_file_name = None ;
160- let mut current_file_id = 0 ;
161-
162- // Convert the list of (Counter, CodeRegion) pairs to an array of `CounterMappingRegion`, sorted
163- // by filename and position. Capture any new files to compute the `CounterMappingRegion`s
164- // `file_id` (indexing files referenced by the current function), and construct the
165- // function-specific `virtual_file_mapping` from `file_id` to its index in the module's
166- // `filenames` array.
167- counter_regions. sort_by_key ( |( _counter, region) | * region) ;
168- for ( counter, region) in counter_regions {
169- let CodeRegion { file_name, start_line, start_col, end_line, end_col } = * region;
170- let same_file = current_file_name. is_some_and ( |p| p == file_name) ;
171- if !same_file {
172- if current_file_name. is_some ( ) {
173- current_file_id += 1 ;
174- }
175- current_file_name = Some ( file_name) ;
176- debug ! ( " file_id: {} = '{:?}'" , current_file_id, file_name) ;
177- let ( filenames_index, _) = self . filenames . insert_full ( file_name) ;
178- virtual_file_mapping. push ( filenames_index as u32 ) ;
179- }
180- debug ! ( "Adding counter {:?} to map for {:?}" , counter, region) ;
181- mapping_regions. push ( CounterMappingRegion :: code_region (
182- counter,
183- current_file_id,
184- start_line,
185- start_col,
186- end_line,
187- end_col,
188- ) ) ;
189- }
147+ fn into_filenames_buffer ( self ) -> Vec < u8 > {
148+ // This method takes `self` so that the caller can't accidentally
149+ // modify the original file table after encoding it into a buffer.
190150
191- // Encode and append the current function's coverage mapping data
192- coverageinfo:: write_mapping_to_buffer (
193- virtual_file_mapping,
194- expressions,
195- mapping_regions,
196- coverage_mapping_buffer,
197- ) ;
151+ llvm:: build_byte_buffer ( |buffer| {
152+ coverageinfo:: write_filenames_section_to_buffer (
153+ self . global_file_table . iter ( ) . map ( Symbol :: as_str) ,
154+ buffer,
155+ ) ;
156+ } )
198157 }
158+ }
199159
200- /// Construct coverage map header and the array of function records, and combine them into the
201- /// coverage map. Save the coverage map data into the LLVM IR as a static global using a
202- /// specific, well-known section and name.
203- fn generate_coverage_map < ' ll > (
204- self ,
205- cx : & CodegenCx < ' ll , ' _ > ,
206- version : u32 ,
207- filenames_size : usize ,
208- filenames_val : & ' ll llvm:: Value ,
209- ) -> & ' ll llvm:: Value {
210- debug ! ( "cov map: filenames_size = {}, 0-based version = {}" , filenames_size, version) ;
211-
212- // Create the coverage data header (Note, fields 0 and 2 are now always zero,
213- // as of `llvm::coverage::CovMapVersion::Version4`.)
214- let zero_was_n_records_val = cx. const_u32 ( 0 ) ;
215- let filenames_size_val = cx. const_u32 ( filenames_size as u32 ) ;
216- let zero_was_coverage_size_val = cx. const_u32 ( 0 ) ;
217- let version_val = cx. const_u32 ( version) ;
218- let cov_data_header_val = cx. const_struct (
219- & [ zero_was_n_records_val, filenames_size_val, zero_was_coverage_size_val, version_val] ,
220- /*packed=*/ false ,
221- ) ;
160+ /// Using the `expressions` and `counter_regions` collected for the current function, generate
161+ /// the `mapping_regions` and `virtual_file_mapping`, and capture any new filenames. Then use
162+ /// LLVM APIs to encode the `virtual_file_mapping`, `expressions`, and `mapping_regions` into
163+ /// the given `coverage_mapping` byte buffer, compliant with the LLVM Coverage Mapping format.
164+ fn write_coverage_mapping < ' a > (
165+ global_file_table : & mut GlobalFileTable ,
166+ expressions : Vec < CounterExpression > ,
167+ counter_regions : impl Iterator < Item = ( Counter , & ' a CodeRegion ) > ,
168+ coverage_mapping_buffer : & RustString ,
169+ ) {
170+ let mut counter_regions = counter_regions. collect :: < Vec < _ > > ( ) ;
171+ if counter_regions. is_empty ( ) {
172+ return ;
173+ }
222174
223- // Create the complete LLVM coverage data value to add to the LLVM IR
224- cx. const_struct ( & [ cov_data_header_val, filenames_val] , /*packed=*/ false )
175+ let mut virtual_file_mapping = Vec :: new ( ) ;
176+ let mut mapping_regions = Vec :: new ( ) ;
177+ let mut current_file_name = None ;
178+ let mut current_file_id = 0 ;
179+
180+ // Convert the list of (Counter, CodeRegion) pairs to an array of `CounterMappingRegion`, sorted
181+ // by filename and position. Capture any new files to compute the `CounterMappingRegion`s
182+ // `file_id` (indexing files referenced by the current function), and construct the
183+ // function-specific `virtual_file_mapping` from `file_id` to its index in the module's
184+ // `filenames` array.
185+ counter_regions. sort_by_key ( |( _counter, region) | * region) ;
186+ for ( counter, region) in counter_regions {
187+ let CodeRegion { file_name, start_line, start_col, end_line, end_col } = * region;
188+ let same_file = current_file_name. is_some_and ( |p| p == file_name) ;
189+ if !same_file {
190+ if current_file_name. is_some ( ) {
191+ current_file_id += 1 ;
192+ }
193+ current_file_name = Some ( file_name) ;
194+ debug ! ( " file_id: {} = '{:?}'" , current_file_id, file_name) ;
195+ let global_file_id = global_file_table. global_file_id_for_file_name ( file_name) ;
196+ virtual_file_mapping. push ( global_file_id) ;
197+ }
198+ debug ! ( "Adding counter {:?} to map for {:?}" , counter, region) ;
199+ mapping_regions. push ( CounterMappingRegion :: code_region (
200+ counter,
201+ current_file_id,
202+ start_line,
203+ start_col,
204+ end_line,
205+ end_col,
206+ ) ) ;
225207 }
208+
209+ // Encode and append the current function's coverage mapping data
210+ coverageinfo:: write_mapping_to_buffer (
211+ virtual_file_mapping,
212+ expressions,
213+ mapping_regions,
214+ coverage_mapping_buffer,
215+ ) ;
216+ }
217+
218+ /// Construct coverage map header and the array of function records, and combine them into the
219+ /// coverage map. Save the coverage map data into the LLVM IR as a static global using a
220+ /// specific, well-known section and name.
221+ fn generate_coverage_map < ' ll > (
222+ cx : & CodegenCx < ' ll , ' _ > ,
223+ version : u32 ,
224+ filenames_size : usize ,
225+ filenames_val : & ' ll llvm:: Value ,
226+ ) -> & ' ll llvm:: Value {
227+ debug ! ( "cov map: filenames_size = {}, 0-based version = {}" , filenames_size, version) ;
228+
229+ // Create the coverage data header (Note, fields 0 and 2 are now always zero,
230+ // as of `llvm::coverage::CovMapVersion::Version4`.)
231+ let zero_was_n_records_val = cx. const_u32 ( 0 ) ;
232+ let filenames_size_val = cx. const_u32 ( filenames_size as u32 ) ;
233+ let zero_was_coverage_size_val = cx. const_u32 ( 0 ) ;
234+ let version_val = cx. const_u32 ( version) ;
235+ let cov_data_header_val = cx. const_struct (
236+ & [ zero_was_n_records_val, filenames_size_val, zero_was_coverage_size_val, version_val] ,
237+ /*packed=*/ false ,
238+ ) ;
239+
240+ // Create the complete LLVM coverage data value to add to the LLVM IR
241+ cx. const_struct ( & [ cov_data_header_val, filenames_val] , /*packed=*/ false )
226242}
227243
228244/// Construct a function record and combine it with the function's coverage mapping data.
0 commit comments