@@ -4,7 +4,7 @@ use crate::llvm;
4
4
5
5
use llvm:: coverageinfo:: CounterMappingRegion ;
6
6
use rustc_codegen_ssa:: coverageinfo:: map:: { Counter , CounterExpression } ;
7
- use rustc_codegen_ssa:: traits:: { BaseTypeMethods , ConstMethods } ;
7
+ use rustc_codegen_ssa:: traits:: ConstMethods ;
8
8
use rustc_data_structures:: fx:: FxIndexSet ;
9
9
use rustc_llvm:: RustString ;
10
10
use rustc_middle:: mir:: coverage:: CodeRegion ;
@@ -15,9 +15,9 @@ use tracing::debug;
15
15
16
16
/// Generates and exports the Coverage Map.
17
17
///
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
21
21
/// LLVM coverage tools (`llvm-profdata` and `llvm-cov`) bundled with Rust's fork of LLVM.
22
22
///
23
23
/// Consequently, Rust's bundled version of Clang also generates Coverage Maps compliant with
@@ -26,6 +26,13 @@ use tracing::debug;
26
26
/// undocumented details in Clang's implementation (that may or may not be important) were also
27
27
/// replicated for Rust's Coverage Map.
28
28
pub 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
+
29
36
let function_coverage_map = match cx. coverage_context ( ) {
30
37
Some ( ctx) => ctx. take_function_coverage_map ( ) ,
31
38
None => return ,
@@ -38,46 +45,50 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
38
45
let mut mapgen = CoverageMapGenerator :: new ( ) ;
39
46
40
47
// 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
+ }
68
67
69
68
// Encode all filenames referenced by counters/expressions in this module
70
69
let filenames_buffer = llvm:: build_byte_buffer ( |filenames_buffer| {
71
70
coverageinfo:: write_filenames_section_to_buffer ( & mapgen. filenames , filenames_buffer) ;
72
71
} ) ;
73
72
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
+
74
77
// 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) ;
81
92
}
82
93
83
94
struct CoverageMapGenerator {
@@ -92,12 +103,12 @@ impl CoverageMapGenerator {
92
103
/// Using the `expressions` and `counter_regions` collected for the current function, generate
93
104
/// the `mapping_regions` and `virtual_file_mapping`, and capture any new filenames. Then use
94
105
/// 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 (
97
108
& mut self ,
98
109
expressions : Vec < CounterExpression > ,
99
110
counter_regions : impl Iterator < Item = ( Counter , & ' a CodeRegion ) > ,
100
- coverage_mappings_buffer : & RustString ,
111
+ coverage_mapping_buffer : & RustString ,
101
112
) {
102
113
let mut counter_regions = counter_regions. collect :: < Vec < _ > > ( ) ;
103
114
if counter_regions. is_empty ( ) {
@@ -145,89 +156,75 @@ impl CoverageMapGenerator {
145
156
virtual_file_mapping,
146
157
expressions,
147
158
mapping_regions,
148
- coverage_mappings_buffer ,
159
+ coverage_mapping_buffer ,
149
160
) ;
150
161
}
151
162
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 (
175
167
self ,
176
168
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) ;
203
174
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 ) ;
206
178
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 ) ;
209
181
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] ,
211
183
/*packed=*/ false ,
212
184
) ;
213
185
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
-
224
186
// 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 )
232
188
}
233
189
}
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