1- use crate :: llvm;
2-
31use crate :: common:: CodegenCx ;
42use crate :: coverageinfo;
3+ use crate :: llvm;
54
5+ use llvm:: coverageinfo:: CounterMappingRegion ;
66use log:: debug;
7- use rustc_codegen_ssa:: coverageinfo:: map:: * ;
8- use rustc_codegen_ssa:: traits:: { BaseTypeMethods , ConstMethods , MiscMethods } ;
7+ use rustc_codegen_ssa:: coverageinfo:: map:: { Counter , CounterExpression , Region } ;
8+ use rustc_codegen_ssa:: traits:: { BaseTypeMethods , ConstMethods } ;
99use rustc_data_structures:: fx:: FxHashMap ;
1010use rustc_llvm:: RustString ;
11- use rustc_middle:: ty:: Instance ;
12- use rustc_middle:: { bug, mir} ;
1311
14- use std:: collections:: BTreeMap ;
1512use std:: ffi:: CString ;
16- use std:: path:: PathBuf ;
17-
18- // FIXME(richkadel): Complete all variations of generating and exporting the coverage map to LLVM.
19- // The current implementation is an initial foundation with basic capabilities (Counters, but not
20- // CounterExpressions, etc.).
2113
2214/// Generates and exports the Coverage Map.
2315///
@@ -32,174 +24,123 @@ use std::path::PathBuf;
3224/// undocumented details in Clang's implementation (that may or may not be important) were also
3325/// replicated for Rust's Coverage Map.
3426pub fn finalize < ' ll , ' tcx > ( cx : & CodegenCx < ' ll , ' tcx > ) {
35- let mut coverage_writer = CoverageMappingWriter :: new ( cx) ;
36-
3727 let function_coverage_map = cx. coverage_context ( ) . take_function_coverage_map ( ) ;
28+ if function_coverage_map. is_empty ( ) {
29+ // This module has no functions with coverage instrumentation
30+ return ;
31+ }
32+
33+ let mut mapgen = CoverageMapGenerator :: new ( ) ;
3834
3935 // Encode coverage mappings and generate function records
4036 let mut function_records = Vec :: < & ' ll llvm:: Value > :: new ( ) ;
4137 let coverage_mappings_buffer = llvm:: build_byte_buffer ( |coverage_mappings_buffer| {
4238 for ( instance, function_coverage) in function_coverage_map. into_iter ( ) {
43- if let Some ( function_record) = coverage_writer. write_function_mappings_and_record (
44- instance,
45- function_coverage,
46- coverage_mappings_buffer,
47- ) {
48- function_records. push ( function_record) ;
49- }
39+ debug ! ( "Generate coverage map for: {:?}" , instance) ;
40+
41+ let mangled_function_name = cx. tcx . symbol_name ( instance) . to_string ( ) ;
42+ let function_source_hash = function_coverage. source_hash ( ) ;
43+ let ( expressions, counter_regions) =
44+ function_coverage. get_expressions_and_counter_regions ( ) ;
45+
46+ let old_len = coverage_mappings_buffer. len ( ) ;
47+ mapgen. write_coverage_mappings ( expressions, counter_regions, coverage_mappings_buffer) ;
48+ let mapping_data_size = coverage_mappings_buffer. len ( ) - old_len;
49+ debug_assert ! (
50+ mapping_data_size > 0 ,
51+ "Every `FunctionCoverage` should have at least one counter"
52+ ) ;
53+
54+ let function_record = mapgen. make_function_record (
55+ cx,
56+ mangled_function_name,
57+ function_source_hash,
58+ mapping_data_size,
59+ ) ;
60+ function_records. push ( function_record) ;
5061 }
5162 } ) ;
5263
53- // Encode all filenames covered in this module, ordered by `file_id`
64+ // Encode all filenames referenced by counters/expressions in this module
5465 let filenames_buffer = llvm:: build_byte_buffer ( |filenames_buffer| {
55- coverageinfo:: write_filenames_section_to_buffer (
56- & coverage_writer. filenames ,
57- filenames_buffer,
58- ) ;
66+ coverageinfo:: write_filenames_section_to_buffer ( & mapgen. filenames , filenames_buffer) ;
5967 } ) ;
6068
61- if coverage_mappings_buffer. len ( ) > 0 {
62- // Generate the LLVM IR representation of the coverage map and store it in a well-known
63- // global constant.
64- coverage_writer. write_coverage_map (
65- function_records,
66- filenames_buffer,
67- coverage_mappings_buffer,
68- ) ;
69- }
69+ // Generate the LLVM IR representation of the coverage map and store it in a well-known global
70+ mapgen. save_generated_coverage_map (
71+ cx,
72+ function_records,
73+ filenames_buffer,
74+ coverage_mappings_buffer,
75+ ) ;
7076}
7177
72- struct CoverageMappingWriter < ' a , ' ll , ' tcx > {
73- cx : & ' a CodegenCx < ' ll , ' tcx > ,
78+ struct CoverageMapGenerator {
7479 filenames : Vec < CString > ,
7580 filename_to_index : FxHashMap < CString , u32 > ,
7681}
7782
78- impl < ' a , ' ll , ' tcx > CoverageMappingWriter < ' a , ' ll , ' tcx > {
79- fn new ( cx : & ' a CodegenCx < ' ll , ' tcx > ) -> Self {
80- Self { cx , filenames : Vec :: new ( ) , filename_to_index : FxHashMap :: < CString , u32 > :: default ( ) }
83+ impl CoverageMapGenerator {
84+ fn new ( ) -> Self {
85+ Self { filenames : Vec :: new ( ) , filename_to_index : FxHashMap :: default ( ) }
8186 }
8287
83- /// For the given function, get the coverage region data, stream it to the given buffer, and
84- /// then generate and return a new function record.
85- fn write_function_mappings_and_record (
88+ /// Using the `expressions` and `counter_regions` collected for the current function, generate
89+ /// the `mapping_regions` and `virtual_file_mapping`, and capture any new filenames. Then use
90+ /// LLVM APIs to encode the `virtual_file_mapping`, `expressions`, and `mapping_regions` into
91+ /// the given `coverage_mappings` byte buffer, compliant with the LLVM Coverage Mapping format.
92+ fn write_coverage_mappings (
8693 & mut self ,
87- instance : Instance < ' tcx > ,
88- mut function_coverage : FunctionCoverage ,
94+ expressions : Vec < CounterExpression > ,
95+ counter_regions : impl Iterator < Item = ( Counter , & ' a Region ) > ,
8996 coverage_mappings_buffer : & RustString ,
90- ) -> Option < & ' ll llvm:: Value > {
91- let cx = self . cx ;
92- let coverageinfo: & mir:: CoverageInfo = cx. tcx . coverageinfo ( instance. def_id ( ) ) ;
93- debug ! (
94- "Generate coverage map for: {:?}, num_counters: {}, num_expressions: {}" ,
95- instance, coverageinfo. num_counters, coverageinfo. num_expressions
96- ) ;
97- debug_assert ! ( coverageinfo. num_counters > 0 ) ;
98-
99- let regions_in_file_order = function_coverage. regions_in_file_order ( cx. sess ( ) . source_map ( ) ) ;
100- if regions_in_file_order. len ( ) == 0 {
101- return None ;
97+ ) {
98+ let mut counter_regions = counter_regions. collect :: < Vec < _ > > ( ) ;
99+ if counter_regions. is_empty ( ) {
100+ return ;
102101 }
103102
104- // Stream the coverage mapping regions for the function (`instance`) to the buffer, and
105- // compute the data byte size used.
106- let old_len = coverage_mappings_buffer. len ( ) ;
107- self . regions_to_mappings ( regions_in_file_order, coverage_mappings_buffer) ;
108- let mapping_data_size = coverage_mappings_buffer. len ( ) - old_len;
109- debug_assert ! ( mapping_data_size > 0 ) ;
110-
111- let mangled_function_name = cx. tcx . symbol_name ( instance) . to_string ( ) ;
112- let name_ref = coverageinfo:: compute_hash ( & mangled_function_name) ;
113- let function_source_hash = function_coverage. source_hash ( ) ;
114-
115- // Generate and return the function record
116- let name_ref_val = cx. const_u64 ( name_ref) ;
117- let mapping_data_size_val = cx. const_u32 ( mapping_data_size as u32 ) ;
118- let func_hash_val = cx. const_u64 ( function_source_hash) ;
119- Some ( cx. const_struct (
120- & [ name_ref_val, mapping_data_size_val, func_hash_val] ,
121- /*packed=*/ true ,
122- ) )
123- }
124-
125- /// For each coverage region, extract its coverage data from the earlier coverage analysis.
126- /// Use LLVM APIs to convert the data into buffered bytes compliant with the LLVM Coverage
127- /// Mapping format.
128- fn regions_to_mappings (
129- & mut self ,
130- regions_in_file_order : BTreeMap < PathBuf , BTreeMap < CoverageLoc , ( usize , CoverageKind ) > > ,
131- coverage_mappings_buffer : & RustString ,
132- ) {
133103 let mut virtual_file_mapping = Vec :: new ( ) ;
134- let mut mapping_regions = coverageinfo:: SmallVectorCounterMappingRegion :: new ( ) ;
135- let mut expressions = coverageinfo:: SmallVectorCounterExpression :: new ( ) ;
136-
137- for ( file_id, ( file_path, file_coverage_regions) ) in
138- regions_in_file_order. into_iter ( ) . enumerate ( )
139- {
140- let file_id = file_id as u32 ;
141- let filename = CString :: new ( file_path. to_string_lossy ( ) . to_string ( ) )
142- . expect ( "null error converting filename to C string" ) ;
143- debug ! ( " file_id: {} = '{:?}'" , file_id, filename) ;
144- let filenames_index = match self . filename_to_index . get ( & filename) {
145- Some ( index) => * index,
146- None => {
147- let index = self . filenames . len ( ) as u32 ;
148- self . filenames . push ( filename. clone ( ) ) ;
149- self . filename_to_index . insert ( filename, index) ;
150- index
104+ let mut mapping_regions = Vec :: new ( ) ;
105+ let mut current_file_path = None ;
106+ let mut current_file_id = 0 ;
107+
108+ // Convert the list of (Counter, Region) pairs to an array of `CounterMappingRegion`, sorted
109+ // by filename and position. Capture any new files to compute the `CounterMappingRegion`s
110+ // `file_id` (indexing files referenced by the current function), and construct the
111+ // function-specific `virtual_file_mapping` from `file_id` to its index in the module's
112+ // `filenames` array.
113+ counter_regions. sort_unstable_by_key ( |( _counter, region) | * region) ;
114+ for ( counter, region) in counter_regions {
115+ let ( file_path, start_line, start_col, end_line, end_col) = region. file_start_and_end ( ) ;
116+ let same_file = current_file_path. as_ref ( ) . map_or ( false , |p| p == file_path) ;
117+ if !same_file {
118+ if current_file_path. is_some ( ) {
119+ current_file_id += 1 ;
151120 }
152- } ;
153- virtual_file_mapping. push ( filenames_index) ;
154-
155- let mut mapping_indexes = vec ! [ 0 as u32 ; file_coverage_regions. len( ) ] ;
156- for ( mapping_index, ( region_id, _) ) in file_coverage_regions. values ( ) . enumerate ( ) {
157- mapping_indexes[ * region_id] = mapping_index as u32 ;
158- }
159-
160- for ( region_loc, ( region_id, region_kind) ) in file_coverage_regions. into_iter ( ) {
161- let mapping_index = mapping_indexes[ region_id] ;
162- match region_kind {
163- CoverageKind :: Counter => {
164- debug ! (
165- " Counter {}, file_id: {}, region_loc: {}" ,
166- mapping_index, file_id, region_loc
167- ) ;
168- mapping_regions. push_from (
169- mapping_index,
170- file_id,
171- region_loc. start_line ,
172- region_loc. start_col ,
173- region_loc. end_line ,
174- region_loc. end_col ,
175- ) ;
176- }
177- CoverageKind :: CounterExpression ( lhs, op, rhs) => {
178- debug ! (
179- " CounterExpression {} = {} {:?} {}, file_id: {}, region_loc: {:?}" ,
180- mapping_index, lhs, op, rhs, file_id, region_loc,
181- ) ;
182- mapping_regions. push_from (
183- mapping_index,
184- file_id,
185- region_loc. start_line ,
186- region_loc. start_col ,
187- region_loc. end_line ,
188- region_loc. end_col ,
189- ) ;
190- expressions. push_from ( op, lhs, rhs) ;
191- }
192- CoverageKind :: Unreachable => {
193- debug ! (
194- " Unreachable region, file_id: {}, region_loc: {:?}" ,
195- file_id, region_loc,
196- ) ;
197- bug ! ( "Unreachable region not expected and not yet handled!" )
198- // FIXME(richkadel): implement and call
199- // mapping_regions.push_from(...) for unreachable regions
121+ current_file_path = Some ( file_path. clone ( ) ) ;
122+ let filename = CString :: new ( file_path. to_string_lossy ( ) . to_string ( ) )
123+ . expect ( "null error converting filename to C string" ) ;
124+ debug ! ( " file_id: {} = '{:?}'" , current_file_id, filename) ;
125+ let filenames_index = match self . filename_to_index . get ( & filename) {
126+ Some ( index) => * index,
127+ None => {
128+ let index = self . filenames . len ( ) as u32 ;
129+ self . filenames . push ( filename. clone ( ) ) ;
130+ self . filename_to_index . insert ( filename. clone ( ) , index) ;
131+ index
200132 }
201- }
133+ } ;
134+ virtual_file_mapping. push ( filenames_index) ;
202135 }
136+ mapping_regions. push ( CounterMappingRegion :: code_region (
137+ counter,
138+ current_file_id,
139+ start_line,
140+ start_col,
141+ end_line,
142+ end_col,
143+ ) ) ;
203144 }
204145
205146 // Encode and append the current function's coverage mapping data
@@ -211,14 +152,35 @@ impl<'a, 'll, 'tcx> CoverageMappingWriter<'a, 'll, 'tcx> {
211152 ) ;
212153 }
213154
214- fn write_coverage_map (
155+ /// Generate and return the function record `Value`
156+ fn make_function_record (
157+ & mut self ,
158+ cx : & CodegenCx < ' ll , ' tcx > ,
159+ mangled_function_name : String ,
160+ function_source_hash : u64 ,
161+ mapping_data_size : usize ,
162+ ) -> & ' ll llvm:: Value {
163+ let name_ref = coverageinfo:: compute_hash ( & mangled_function_name) ;
164+ let name_ref_val = cx. const_u64 ( name_ref) ;
165+ let mapping_data_size_val = cx. const_u32 ( mapping_data_size as u32 ) ;
166+ let func_hash_val = cx. const_u64 ( function_source_hash) ;
167+ cx. const_struct (
168+ & [ name_ref_val, mapping_data_size_val, func_hash_val] ,
169+ /*packed=*/ true ,
170+ )
171+ }
172+
173+ /// Combine the filenames and coverage mappings buffers, construct coverage map header and the
174+ /// array of function records, and combine everything into the complete coverage map. Save the
175+ /// coverage map data into the LLVM IR as a static global using a specific, well-known section
176+ /// and name.
177+ fn save_generated_coverage_map (
215178 self ,
179+ cx : & CodegenCx < ' ll , ' tcx > ,
216180 function_records : Vec < & ' ll llvm:: Value > ,
217181 filenames_buffer : Vec < u8 > ,
218182 mut coverage_mappings_buffer : Vec < u8 > ,
219183 ) {
220- let cx = self . cx ;
221-
222184 // Concatenate the encoded filenames and encoded coverage mappings, and add additional zero
223185 // bytes as-needed to ensure 8-byte alignment.
224186 let mut coverage_size = coverage_mappings_buffer. len ( ) ;
0 commit comments