11use crate :: common:: CodegenCx ;
22use crate :: coverageinfo;
3- use crate :: coverageinfo:: ffi:: { Counter , CounterExpression , CounterMappingRegion } ;
3+ use crate :: coverageinfo:: ffi:: CounterMappingRegion ;
4+ use crate :: coverageinfo:: map_data:: FunctionCoverage ;
45use crate :: llvm;
56
67use rustc_codegen_ssa:: traits:: ConstMethods ;
78use rustc_data_structures:: fx:: FxIndexSet ;
89use rustc_hir:: def:: DefKind ;
910use rustc_hir:: def_id:: DefId ;
10- use rustc_llvm :: RustString ;
11+ use rustc_index :: IndexVec ;
1112use rustc_middle:: bug;
1213use rustc_middle:: middle:: codegen_fn_attrs:: CodegenFnAttrFlags ;
1314use rustc_middle:: mir:: coverage:: CodeRegion ;
@@ -55,7 +56,7 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
5556 return ;
5657 }
5758
58- let mut mapgen = CoverageMapGenerator :: new ( tcx) ;
59+ let mut global_file_table = GlobalFileTable :: new ( tcx) ;
5960
6061 // Encode coverage mappings and generate function records
6162 let mut function_data = Vec :: new ( ) ;
@@ -64,12 +65,9 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
6465 let mangled_function_name = tcx. symbol_name ( instance) . name ;
6566 let source_hash = function_coverage. source_hash ( ) ;
6667 let is_used = function_coverage. is_used ( ) ;
67- let ( expressions, counter_regions) =
68- function_coverage. get_expressions_and_counter_regions ( ) ;
6968
70- let coverage_mapping_buffer = llvm:: build_byte_buffer ( |coverage_mapping_buffer| {
71- mapgen. write_coverage_mapping ( expressions, counter_regions, coverage_mapping_buffer) ;
72- } ) ;
69+ let coverage_mapping_buffer =
70+ encode_mappings_for_function ( & mut global_file_table, & function_coverage) ;
7371
7472 if coverage_mapping_buffer. is_empty ( ) {
7573 if function_coverage. is_used ( ) {
@@ -87,19 +85,14 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
8785 }
8886
8987 // Encode all filenames referenced by counters/expressions in this module
90- let filenames_buffer = llvm:: build_byte_buffer ( |filenames_buffer| {
91- coverageinfo:: write_filenames_section_to_buffer (
92- mapgen. filenames . iter ( ) . map ( Symbol :: as_str) ,
93- filenames_buffer,
94- ) ;
95- } ) ;
88+ let filenames_buffer = global_file_table. into_filenames_buffer ( ) ;
9689
9790 let filenames_size = filenames_buffer. len ( ) ;
9891 let filenames_val = cx. const_bytes ( & filenames_buffer) ;
9992 let filenames_ref = coverageinfo:: hash_bytes ( & filenames_buffer) ;
10093
10194 // Generate the LLVM IR representation of the coverage map and store it in a well-known global
102- let cov_data_val = mapgen . generate_coverage_map ( cx, version, filenames_size, filenames_val) ;
95+ let cov_data_val = generate_coverage_map ( cx, version, filenames_size, filenames_val) ;
10396
10497 let covfun_section_name = coverageinfo:: covfun_section_name ( cx) ;
10598 for ( mangled_function_name, source_hash, is_used, coverage_mapping_buffer) in function_data {
@@ -118,13 +111,13 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
118111 coverageinfo:: save_cov_data_to_mod ( cx, cov_data_val) ;
119112}
120113
121- struct CoverageMapGenerator {
122- filenames : FxIndexSet < Symbol > ,
114+ struct GlobalFileTable {
115+ global_file_table : FxIndexSet < Symbol > ,
123116}
124117
125- impl CoverageMapGenerator {
118+ impl GlobalFileTable {
126119 fn new ( tcx : TyCtxt < ' _ > ) -> Self {
127- let mut filenames = FxIndexSet :: default ( ) ;
120+ let mut global_file_table = FxIndexSet :: default ( ) ;
128121 // LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
129122 // requires setting the first filename to the compilation directory.
130123 // Since rustc generates coverage maps with relative paths, the
@@ -133,94 +126,114 @@ impl CoverageMapGenerator {
133126 let working_dir = Symbol :: intern (
134127 & tcx. sess . opts . working_dir . remapped_path_if_available ( ) . to_string_lossy ( ) ,
135128 ) ;
136- filenames . insert ( working_dir) ;
137- Self { filenames }
129+ global_file_table . insert ( working_dir) ;
130+ Self { global_file_table }
138131 }
139132
140- /// Using the `expressions` and `counter_regions` collected for the current function, generate
141- /// the `mapping_regions` and `virtual_file_mapping`, and capture any new filenames. Then use
142- /// LLVM APIs to encode the `virtual_file_mapping`, `expressions`, and `mapping_regions` into
143- /// the given `coverage_mapping` byte buffer, compliant with the LLVM Coverage Mapping format.
144- fn write_coverage_mapping < ' a > (
145- & mut self ,
146- expressions : Vec < CounterExpression > ,
147- counter_regions : impl Iterator < Item = ( Counter , & ' a CodeRegion ) > ,
148- coverage_mapping_buffer : & RustString ,
149- ) {
150- let mut counter_regions = counter_regions. collect :: < Vec < _ > > ( ) ;
151- if counter_regions. is_empty ( ) {
152- return ;
153- }
133+ fn global_file_id_for_file_name ( & mut self , file_name : Symbol ) -> u32 {
134+ let ( global_file_id, _) = self . global_file_table . insert_full ( file_name) ;
135+ global_file_id as u32
136+ }
154137
155- let mut virtual_file_mapping = Vec :: new ( ) ;
156- let mut mapping_regions = Vec :: new ( ) ;
157- let mut current_file_name = None ;
158- let mut current_file_id = 0 ;
159-
160- // Convert the list of (Counter, CodeRegion) pairs to an array of `CounterMappingRegion`, sorted
161- // by filename and position. Capture any new files to compute the `CounterMappingRegion`s
162- // `file_id` (indexing files referenced by the current function), and construct the
163- // function-specific `virtual_file_mapping` from `file_id` to its index in the module's
164- // `filenames` array.
165- counter_regions. sort_unstable_by_key ( |( _counter, region) | * region) ;
166- for ( counter, region) in counter_regions {
167- let CodeRegion { file_name, start_line, start_col, end_line, end_col } = * region;
168- let same_file = current_file_name. is_some_and ( |p| p == file_name) ;
169- if !same_file {
170- if current_file_name. is_some ( ) {
171- current_file_id += 1 ;
172- }
173- current_file_name = Some ( file_name) ;
174- debug ! ( " file_id: {} = '{:?}'" , current_file_id, file_name) ;
175- let ( filenames_index, _) = self . filenames . insert_full ( file_name) ;
176- virtual_file_mapping. push ( filenames_index as u32 ) ;
177- }
178- debug ! ( "Adding counter {:?} to map for {:?}" , counter, region) ;
138+ fn into_filenames_buffer ( self ) -> Vec < u8 > {
139+ // This method takes `self` so that the caller can't accidentally
140+ // modify the original file table after encoding it into a buffer.
141+
142+ llvm:: build_byte_buffer ( |buffer| {
143+ coverageinfo:: write_filenames_section_to_buffer (
144+ self . global_file_table . iter ( ) . map ( Symbol :: as_str) ,
145+ buffer,
146+ ) ;
147+ } )
148+ }
149+ }
150+
151+ /// Using the expressions and counter regions collected for a single function,
152+ /// generate the variable-sized payload of its corresponding `__llvm_covfun`
153+ /// entry. The payload is returned as a vector of bytes.
154+ ///
155+ /// Newly-encountered filenames will be added to the global file table.
156+ fn encode_mappings_for_function (
157+ global_file_table : & mut GlobalFileTable ,
158+ function_coverage : & FunctionCoverage < ' _ > ,
159+ ) -> Vec < u8 > {
160+ let ( expressions, counter_regions) = function_coverage. get_expressions_and_counter_regions ( ) ;
161+
162+ let mut counter_regions = counter_regions. collect :: < Vec < _ > > ( ) ;
163+ if counter_regions. is_empty ( ) {
164+ return Vec :: new ( ) ;
165+ }
166+
167+ let mut virtual_file_mapping = IndexVec :: < u32 , u32 > :: new ( ) ;
168+ let mut mapping_regions = Vec :: with_capacity ( counter_regions. len ( ) ) ;
169+
170+ // Sort the list of (counter, region) mapping pairs by region, so that they
171+ // can be grouped by filename. Prepare file IDs for each filename, and
172+ // prepare the mapping data so that we can pass it through FFI to LLVM.
173+ counter_regions. sort_by_key ( |( _counter, region) | * region) ;
174+ for counter_regions_for_file in
175+ counter_regions. group_by ( |( _, a) , ( _, b) | a. file_name == b. file_name )
176+ {
177+ // Look up (or allocate) the global file ID for this filename.
178+ let file_name = counter_regions_for_file[ 0 ] . 1 . file_name ;
179+ let global_file_id = global_file_table. global_file_id_for_file_name ( file_name) ;
180+
181+ // Associate that global file ID with a local file ID for this function.
182+ let local_file_id: u32 = virtual_file_mapping. push ( global_file_id) ;
183+ debug ! ( " file id: local {local_file_id} => global {global_file_id} = '{file_name:?}'" ) ;
184+
185+ // For each counter/region pair in this function+file, convert it to a
186+ // form suitable for FFI.
187+ for & ( counter, region) in counter_regions_for_file {
188+ let CodeRegion { file_name : _, start_line, start_col, end_line, end_col } = * region;
189+
190+ debug ! ( "Adding counter {counter:?} to map for {region:?}" ) ;
179191 mapping_regions. push ( CounterMappingRegion :: code_region (
180192 counter,
181- current_file_id ,
193+ local_file_id ,
182194 start_line,
183195 start_col,
184196 end_line,
185197 end_col,
186198 ) ) ;
187199 }
200+ }
188201
189- // Encode and append the current function's coverage mapping data
202+ // Encode the function's coverage mappings into a buffer.
203+ llvm:: build_byte_buffer ( |buffer| {
190204 coverageinfo:: write_mapping_to_buffer (
191- virtual_file_mapping,
205+ virtual_file_mapping. raw ,
192206 expressions,
193207 mapping_regions,
194- coverage_mapping_buffer ,
208+ buffer ,
195209 ) ;
196- }
210+ } )
211+ }
197212
198- /// Construct coverage map header and the array of function records, and combine them into the
199- /// coverage map. Save the coverage map data into the LLVM IR as a static global using a
200- /// specific, well-known section and name.
201- fn generate_coverage_map < ' ll > (
202- self ,
203- cx : & CodegenCx < ' ll , ' _ > ,
204- version : u32 ,
205- filenames_size : usize ,
206- filenames_val : & ' ll llvm:: Value ,
207- ) -> & ' ll llvm:: Value {
208- debug ! ( "cov map: filenames_size = {}, 0-based version = {}" , filenames_size, version) ;
209-
210- // Create the coverage data header (Note, fields 0 and 2 are now always zero,
211- // as of `llvm::coverage::CovMapVersion::Version4`.)
212- let zero_was_n_records_val = cx. const_u32 ( 0 ) ;
213- let filenames_size_val = cx. const_u32 ( filenames_size as u32 ) ;
214- let zero_was_coverage_size_val = cx. const_u32 ( 0 ) ;
215- let version_val = cx. const_u32 ( version) ;
216- let cov_data_header_val = cx. const_struct (
217- & [ zero_was_n_records_val, filenames_size_val, zero_was_coverage_size_val, version_val] ,
218- /*packed=*/ false ,
219- ) ;
213+ /// Construct coverage map header and the array of function records, and combine them into the
214+ /// coverage map. Save the coverage map data into the LLVM IR as a static global using a
215+ /// specific, well-known section and name.
216+ fn generate_coverage_map < ' ll > (
217+ cx : & CodegenCx < ' ll , ' _ > ,
218+ version : u32 ,
219+ filenames_size : usize ,
220+ filenames_val : & ' ll llvm:: Value ,
221+ ) -> & ' ll llvm:: Value {
222+ debug ! ( "cov map: filenames_size = {}, 0-based version = {}" , filenames_size, version) ;
223+
224+ // Create the coverage data header (Note, fields 0 and 2 are now always zero,
225+ // as of `llvm::coverage::CovMapVersion::Version4`.)
226+ let zero_was_n_records_val = cx. const_u32 ( 0 ) ;
227+ let filenames_size_val = cx. const_u32 ( filenames_size as u32 ) ;
228+ let zero_was_coverage_size_val = cx. const_u32 ( 0 ) ;
229+ let version_val = cx. const_u32 ( version) ;
230+ let cov_data_header_val = cx. const_struct (
231+ & [ zero_was_n_records_val, filenames_size_val, zero_was_coverage_size_val, version_val] ,
232+ /*packed=*/ false ,
233+ ) ;
220234
221- // Create the complete LLVM coverage data value to add to the LLVM IR
222- cx. const_struct ( & [ cov_data_header_val, filenames_val] , /*packed=*/ false )
223- }
235+ // Create the complete LLVM coverage data value to add to the LLVM IR
236+ cx. const_struct ( & [ cov_data_header_val, filenames_val] , /*packed=*/ false )
224237}
225238
226239/// Construct a function record and combine it with the function's coverage mapping data.
0 commit comments