Skip to content

Commit 4d0b22e

Browse files
committed
coverage: Convert CoverageMapGenerator to GlobalFileTable
Struct `CoverageMapGenerator` was only used to hold the global file table. Changing its methods to free functions unblocks other refactoring changes.
1 parent 8cd9bff commit 4d0b22e

File tree

1 file changed

+109
-93
lines changed
  • compiler/rustc_codegen_llvm/src/coverageinfo

1 file changed

+109
-93
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

Lines changed: 109 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)