@@ -4,7 +4,7 @@ use crate::llvm;
44
55use llvm:: coverageinfo:: CounterMappingRegion ;
66use rustc_codegen_ssa:: coverageinfo:: map:: { Counter , CounterExpression } ;
7- use rustc_codegen_ssa:: traits:: { BaseTypeMethods , ConstMethods } ;
7+ use rustc_codegen_ssa:: traits:: ConstMethods ;
88use rustc_data_structures:: fx:: FxIndexSet ;
99use rustc_llvm:: RustString ;
1010use rustc_middle:: mir:: coverage:: CodeRegion ;
@@ -15,9 +15,9 @@ use tracing::debug;
1515
1616/// Generates and exports the Coverage Map.
1717///
18- /// This Coverage Map complies with Coverage Mapping Format version 3 (zero-based encoded as 2 ),
19- /// as defined at [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/llvmorg-8.0.0 /llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format)
20- /// and published in Rust's current (July 2020) fork of LLVM. This version is supported by the
18+ /// This Coverage Map complies with Coverage Mapping Format version 4 (zero-based encoded as 3 ),
19+ /// as defined at [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12 /llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format)
20+ /// and published in Rust's current (November 2020) fork of LLVM. This version is supported by the
2121/// LLVM coverage tools (`llvm-profdata` and `llvm-cov`) bundled with Rust's fork of LLVM.
2222///
2323/// Consequently, Rust's bundled version of Clang also generates Coverage Maps compliant with
@@ -26,6 +26,13 @@ use tracing::debug;
2626/// undocumented details in Clang's implementation (that may or may not be important) were also
2727/// replicated for Rust's Coverage Map.
2828pub fn finalize < ' ll , ' tcx > ( cx : & CodegenCx < ' ll , ' tcx > ) {
29+ // Ensure LLVM supports Coverage Map Version 4 (encoded as a zero-based value: 3).
30+ // If not, the LLVM Version must be less than 11.
31+ let version = coverageinfo:: mapping_version ( ) ;
32+ if version != 3 {
33+ cx. tcx . sess . fatal ( "rustc option `-Z instrument-coverage` requires LLVM 11 or higher." ) ;
34+ }
35+
2936 let function_coverage_map = match cx. coverage_context ( ) {
3037 Some ( ctx) => ctx. take_function_coverage_map ( ) ,
3138 None => return ,
@@ -38,46 +45,50 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
3845 let mut mapgen = CoverageMapGenerator :: new ( ) ;
3946
4047 // Encode coverage mappings and generate function records
41- let mut function_records = Vec :: < & ' ll llvm:: Value > :: new ( ) ;
42- let coverage_mappings_buffer = llvm:: build_byte_buffer ( |coverage_mappings_buffer| {
43- for ( instance, function_coverage) in function_coverage_map. into_iter ( ) {
44- debug ! ( "Generate coverage map for: {:?}" , instance) ;
45-
46- let mangled_function_name = cx. tcx . symbol_name ( instance) . to_string ( ) ;
47- let function_source_hash = function_coverage. source_hash ( ) ;
48- let ( expressions, counter_regions) =
49- function_coverage. get_expressions_and_counter_regions ( ) ;
50-
51- let old_len = coverage_mappings_buffer. len ( ) ;
52- mapgen. write_coverage_mappings ( expressions, counter_regions, coverage_mappings_buffer) ;
53- let mapping_data_size = coverage_mappings_buffer. len ( ) - old_len;
54- debug_assert ! (
55- mapping_data_size > 0 ,
56- "Every `FunctionCoverage` should have at least one counter"
57- ) ;
58-
59- let function_record = mapgen. make_function_record (
60- cx,
61- mangled_function_name,
62- function_source_hash,
63- mapping_data_size,
64- ) ;
65- function_records. push ( function_record) ;
66- }
67- } ) ;
48+ let mut function_data = Vec :: new ( ) ;
49+ for ( instance, function_coverage) in function_coverage_map {
50+ debug ! ( "Generate coverage map for: {:?}" , instance) ;
51+
52+ let mangled_function_name = cx. tcx . symbol_name ( instance) . to_string ( ) ;
53+ let function_source_hash = function_coverage. source_hash ( ) ;
54+ let ( expressions, counter_regions) =
55+ function_coverage. get_expressions_and_counter_regions ( ) ;
56+
57+ let coverage_mapping_buffer = llvm:: build_byte_buffer ( |coverage_mapping_buffer| {
58+ mapgen. write_coverage_mapping ( expressions, counter_regions, coverage_mapping_buffer) ;
59+ } ) ;
60+ debug_assert ! (
61+ coverage_mapping_buffer. len( ) > 0 ,
62+ "Every `FunctionCoverage` should have at least one counter"
63+ ) ;
64+
65+ function_data. push ( ( mangled_function_name, function_source_hash, coverage_mapping_buffer) ) ;
66+ }
6867
6968 // Encode all filenames referenced by counters/expressions in this module
7069 let filenames_buffer = llvm:: build_byte_buffer ( |filenames_buffer| {
7170 coverageinfo:: write_filenames_section_to_buffer ( & mapgen. filenames , filenames_buffer) ;
7271 } ) ;
7372
73+ let filenames_size = filenames_buffer. len ( ) ;
74+ let filenames_val = cx. const_bytes ( & filenames_buffer[ ..] ) ;
75+ let filenames_ref = coverageinfo:: hash_bytes ( filenames_buffer) ;
76+
7477 // Generate the LLVM IR representation of the coverage map and store it in a well-known global
75- mapgen. save_generated_coverage_map (
76- cx,
77- function_records,
78- filenames_buffer,
79- coverage_mappings_buffer,
80- ) ;
78+ let cov_data_val = mapgen. generate_coverage_map ( cx, version, filenames_size, filenames_val) ;
79+
80+ for ( mangled_function_name, function_source_hash, coverage_mapping_buffer) in function_data {
81+ save_function_record (
82+ cx,
83+ mangled_function_name,
84+ function_source_hash,
85+ filenames_ref,
86+ coverage_mapping_buffer,
87+ ) ;
88+ }
89+
90+ // Save the coverage data value to LLVM IR
91+ coverageinfo:: save_cov_data_to_mod ( cx, cov_data_val) ;
8192}
8293
8394struct CoverageMapGenerator {
@@ -92,12 +103,12 @@ impl CoverageMapGenerator {
92103 /// Using the `expressions` and `counter_regions` collected for the current function, generate
93104 /// the `mapping_regions` and `virtual_file_mapping`, and capture any new filenames. Then use
94105 /// LLVM APIs to encode the `virtual_file_mapping`, `expressions`, and `mapping_regions` into
95- /// the given `coverage_mappings ` byte buffer, compliant with the LLVM Coverage Mapping format.
96- fn write_coverage_mappings (
106+ /// the given `coverage_mapping ` byte buffer, compliant with the LLVM Coverage Mapping format.
107+ fn write_coverage_mapping (
97108 & mut self ,
98109 expressions : Vec < CounterExpression > ,
99110 counter_regions : impl Iterator < Item = ( Counter , & ' a CodeRegion ) > ,
100- coverage_mappings_buffer : & RustString ,
111+ coverage_mapping_buffer : & RustString ,
101112 ) {
102113 let mut counter_regions = counter_regions. collect :: < Vec < _ > > ( ) ;
103114 if counter_regions. is_empty ( ) {
@@ -145,89 +156,75 @@ impl CoverageMapGenerator {
145156 virtual_file_mapping,
146157 expressions,
147158 mapping_regions,
148- coverage_mappings_buffer ,
159+ coverage_mapping_buffer ,
149160 ) ;
150161 }
151162
152- /// Generate and return the function record `Value`
153- fn make_function_record (
154- & mut self ,
155- cx : & CodegenCx < ' ll , ' tcx > ,
156- mangled_function_name : String ,
157- function_source_hash : u64 ,
158- mapping_data_size : usize ,
159- ) -> & ' ll llvm:: Value {
160- let name_ref = coverageinfo:: compute_hash ( & mangled_function_name) ;
161- let name_ref_val = cx. const_u64 ( name_ref) ;
162- let mapping_data_size_val = cx. const_u32 ( mapping_data_size as u32 ) ;
163- let func_hash_val = cx. const_u64 ( function_source_hash) ;
164- cx. const_struct (
165- & [ name_ref_val, mapping_data_size_val, func_hash_val] ,
166- /*packed=*/ true ,
167- )
168- }
169-
170- /// Combine the filenames and coverage mappings buffers, construct coverage map header and the
171- /// array of function records, and combine everything into the complete coverage map. Save the
172- /// coverage map data into the LLVM IR as a static global using a specific, well-known section
173- /// and name.
174- fn save_generated_coverage_map (
163+ /// Construct coverage map header and the array of function records, and combine them into the
164+ /// coverage map. Save the coverage map data into the LLVM IR as a static global using a
165+ /// specific, well-known section and name.
166+ fn generate_coverage_map (
175167 self ,
176168 cx : & CodegenCx < ' ll , ' tcx > ,
177- function_records : Vec < & ' ll llvm:: Value > ,
178- filenames_buffer : Vec < u8 > ,
179- mut coverage_mappings_buffer : Vec < u8 > ,
180- ) {
181- // Concatenate the encoded filenames and encoded coverage mappings, and add additional zero
182- // bytes as-needed to ensure 8-byte alignment.
183- let mut coverage_size = coverage_mappings_buffer. len ( ) ;
184- let filenames_size = filenames_buffer. len ( ) ;
185- let remaining_bytes =
186- ( filenames_size + coverage_size) % coverageinfo:: COVMAP_VAR_ALIGN_BYTES ;
187- if remaining_bytes > 0 {
188- let pad = coverageinfo:: COVMAP_VAR_ALIGN_BYTES - remaining_bytes;
189- coverage_mappings_buffer. append ( & mut [ 0 ] . repeat ( pad) ) ;
190- coverage_size += pad;
191- }
192- let filenames_and_coverage_mappings = [ filenames_buffer, coverage_mappings_buffer] . concat ( ) ;
193- let filenames_and_coverage_mappings_val =
194- cx. const_bytes ( & filenames_and_coverage_mappings[ ..] ) ;
195-
196- debug ! (
197- "cov map: n_records = {}, filenames_size = {}, coverage_size = {}, 0-based version = {}" ,
198- function_records. len( ) ,
199- filenames_size,
200- coverage_size,
201- coverageinfo:: mapping_version( )
202- ) ;
169+ version : u32 ,
170+ filenames_size : usize ,
171+ filenames_val : & ' ll llvm:: Value ,
172+ ) -> & ' ll llvm:: Value {
173+ debug ! ( "cov map: filenames_size = {}, 0-based version = {}" , filenames_size, version) ;
203174
204- // Create the coverage data header
205- let n_records_val = cx. const_u32 ( function_records. len ( ) as u32 ) ;
175+ // Create the coverage data header (Note, fields 0 and 2 are now always zero,
176+ // as of `llvm::coverage::CovMapVersion::Version4`.)
177+ let zero_was_n_records_val = cx. const_u32 ( 0 ) ;
206178 let filenames_size_val = cx. const_u32 ( filenames_size as u32 ) ;
207- let coverage_size_val = cx. const_u32 ( coverage_size as u32 ) ;
208- let version_val = cx. const_u32 ( coverageinfo :: mapping_version ( ) ) ;
179+ let zero_was_coverage_size_val = cx. const_u32 ( 0 ) ;
180+ let version_val = cx. const_u32 ( version ) ;
209181 let cov_data_header_val = cx. const_struct (
210- & [ n_records_val , filenames_size_val, coverage_size_val , version_val] ,
182+ & [ zero_was_n_records_val , filenames_size_val, zero_was_coverage_size_val , version_val] ,
211183 /*packed=*/ false ,
212184 ) ;
213185
214- // Create the function records array
215- let name_ref_from_u64 = cx. type_i64 ( ) ;
216- let mapping_data_size_from_u32 = cx. type_i32 ( ) ;
217- let func_hash_from_u64 = cx. type_i64 ( ) ;
218- let function_record_ty = cx. type_struct (
219- & [ name_ref_from_u64, mapping_data_size_from_u32, func_hash_from_u64] ,
220- /*packed=*/ true ,
221- ) ;
222- let function_records_val = cx. const_array ( function_record_ty, & function_records[ ..] ) ;
223-
224186 // Create the complete LLVM coverage data value to add to the LLVM IR
225- let cov_data_val = cx. const_struct (
226- & [ cov_data_header_val, function_records_val, filenames_and_coverage_mappings_val] ,
227- /*packed=*/ false ,
228- ) ;
229-
230- // Save the coverage data value to LLVM IR
231- coverageinfo:: save_map_to_mod ( cx, cov_data_val) ;
187+ cx. const_struct ( & [ cov_data_header_val, filenames_val] , /*packed=*/ false )
232188 }
233189}
190+
191+ /// Construct a function record and combine it with the function's coverage mapping data.
192+ /// Save the function record into the LLVM IR as a static global using a
193+ /// specific, well-known section and name.
194+ fn save_function_record (
195+ cx : & CodegenCx < ' ll , ' tcx > ,
196+ mangled_function_name : String ,
197+ function_source_hash : u64 ,
198+ filenames_ref : u64 ,
199+ coverage_mapping_buffer : Vec < u8 > ,
200+ ) {
201+ // Concatenate the encoded coverage mappings
202+ let coverage_mapping_size = coverage_mapping_buffer. len ( ) ;
203+ let coverage_mapping_val = cx. const_bytes ( & coverage_mapping_buffer[ ..] ) ;
204+
205+ let func_name_hash = coverageinfo:: hash_str ( & mangled_function_name) ;
206+ let func_name_hash_val = cx. const_u64 ( func_name_hash) ;
207+ let coverage_mapping_size_val = cx. const_u32 ( coverage_mapping_size as u32 ) ;
208+ let func_hash_val = cx. const_u64 ( function_source_hash) ;
209+ let filenames_ref_val = cx. const_u64 ( filenames_ref) ;
210+ let func_record_val = cx. const_struct (
211+ & [
212+ func_name_hash_val,
213+ coverage_mapping_size_val,
214+ func_hash_val,
215+ filenames_ref_val,
216+ coverage_mapping_val,
217+ ] ,
218+ /*packed=*/ true ,
219+ ) ;
220+
221+ // At the present time, the coverage map for Rust assumes every instrumented function `is_used`.
222+ // Note that Clang marks functions as "unused" in `CodeGenPGO::emitEmptyCounterMapping`. (See:
223+ // https://github.com/rust-lang/llvm-project/blob/de02a75e398415bad4df27b4547c25b896c8bf3b/clang%2Flib%2FCodeGen%2FCodeGenPGO.cpp#L877-L878
224+ // for example.)
225+ //
226+ // It's not yet clear if or how this may be applied to Rust in the future, but the `is_used`
227+ // argument is available and handled similarly.
228+ let is_used = true ;
229+ coverageinfo:: save_func_record_to_mod ( cx, func_name_hash, func_record_val, is_used) ;
230+ }
0 commit comments