Skip to content

Commit e985ae5

Browse files
committed
coverage: Build the global file table ahead of time
1 parent 86b55cc commit e985ae5

File tree

4 files changed

+51
-25
lines changed

4 files changed

+51
-25
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3596,6 +3596,7 @@ version = "0.0.0"
35963596
dependencies = [
35973597
"bitflags 1.3.2",
35983598
"cstr",
3599+
"itertools",
35993600
"libc",
36003601
"measureme",
36013602
"object",

compiler/rustc_codegen_llvm/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ test = false
99
[dependencies]
1010
bitflags = "1.0"
1111
cstr = "0.2"
12+
itertools = "0.10.5"
1213
libc = "0.2"
1314
measureme = "10.0.0"
1415
object = { version = "0.32.0", default-features = false, features = [

compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use rustc_middle::mir::coverage::{
77
CodeRegion, CounterId, CovTerm, Expression, ExpressionId, FunctionCoverageInfo, Mapping, Op,
88
};
99
use rustc_middle::ty::Instance;
10+
use rustc_span::Symbol;
1011

1112
/// Holds all of the coverage mapping data associated with a function instance,
1213
/// collected during traversal of `Coverage` statements in the function's MIR.
@@ -189,6 +190,11 @@ impl<'tcx> FunctionCoverage<'tcx> {
189190
if self.is_used { self.function_coverage_info.function_source_hash } else { 0 }
190191
}
191192

193+
/// Returns an iterator over all filenames used by this function's mappings.
194+
pub(crate) fn all_file_names(&self) -> impl Iterator<Item = Symbol> + Captures<'_> {
195+
self.function_coverage_info.mappings.iter().map(|mapping| mapping.code_region.file_name)
196+
}
197+
192198
/// Convert this function's coverage expression data into a form that can be
193199
/// passed through FFI to LLVM.
194200
pub(crate) fn counter_expressions(

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::coverageinfo::ffi::CounterMappingRegion;
44
use crate::coverageinfo::map_data::{FunctionCoverage, FunctionCoverageCollector};
55
use crate::llvm;
66

7+
use itertools::Itertools as _;
78
use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods};
89
use rustc_data_structures::fx::FxIndexSet;
910
use rustc_hir::def::DefKind;
@@ -57,20 +58,26 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
5758
return;
5859
}
5960

60-
let mut global_file_table = GlobalFileTable::new(tcx);
61+
let function_coverage_entries = function_coverage_map
62+
.into_iter()
63+
.map(|(instance, function_coverage)| (instance, function_coverage.into_finished()))
64+
.collect::<Vec<_>>();
65+
66+
let all_file_names =
67+
function_coverage_entries.iter().flat_map(|(_, fn_cov)| fn_cov.all_file_names());
68+
let global_file_table = GlobalFileTable::new(all_file_names);
6169

6270
// Encode coverage mappings and generate function records
6371
let mut function_data = Vec::new();
64-
for (instance, function_coverage) in function_coverage_map {
65-
let function_coverage = function_coverage.into_finished();
72+
for (instance, function_coverage) in function_coverage_entries {
6673
debug!("Generate function coverage for {}, {:?}", cx.codegen_unit.name(), instance);
6774

6875
let mangled_function_name = tcx.symbol_name(instance).name;
6976
let source_hash = function_coverage.source_hash();
7077
let is_used = function_coverage.is_used();
7178

7279
let coverage_mapping_buffer =
73-
encode_mappings_for_function(&mut global_file_table, &function_coverage);
80+
encode_mappings_for_function(&global_file_table, &function_coverage);
7481

7582
if coverage_mapping_buffer.is_empty() {
7683
if function_coverage.is_used() {
@@ -88,7 +95,7 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
8895
}
8996

9097
// Encode all filenames referenced by counters/expressions in this module
91-
let filenames_buffer = global_file_table.into_filenames_buffer();
98+
let filenames_buffer = global_file_table.make_filenames_buffer(tcx);
9299

93100
let filenames_size = filenames_buffer.len();
94101
let filenames_val = cx.const_bytes(&filenames_buffer);
@@ -139,37 +146,48 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
139146
coverageinfo::save_cov_data_to_mod(cx, cov_data_val);
140147
}
141148

149+
/// Maps "global" (per-CGU) file ID numbers to their underlying filenames.
142150
struct GlobalFileTable {
143-
global_file_table: FxIndexSet<Symbol>,
151+
/// This "raw" table doesn't include the working dir, so a filename's
152+
/// global ID is its index in this set **plus one**.
153+
raw_file_table: FxIndexSet<Symbol>,
144154
}
145155

146156
impl GlobalFileTable {
147-
fn new(tcx: TyCtxt<'_>) -> Self {
148-
let mut global_file_table = FxIndexSet::default();
157+
fn new(all_file_names: impl IntoIterator<Item = Symbol>) -> Self {
158+
// Collect all of the filenames into a set. Filenames usually come in
159+
// contiguous runs, so we can dedup adjacent ones to save work.
160+
let mut raw_file_table = all_file_names.into_iter().dedup().collect::<FxIndexSet<Symbol>>();
161+
162+
// Sort the file table by its actual string values, not the arbitrary
163+
// ordering of its symbols.
164+
raw_file_table.sort_unstable_by(|a, b| a.as_str().cmp(b.as_str()));
165+
166+
Self { raw_file_table }
167+
}
168+
169+
fn global_file_id_for_file_name(&self, file_name: Symbol) -> u32 {
170+
let raw_id = self.raw_file_table.get_index_of(&file_name).unwrap_or_else(|| {
171+
bug!("file name not found in prepared global file table: {file_name}");
172+
});
173+
// The raw file table doesn't include an entry for the working dir
174+
// (which has ID 0), so add 1 to get the correct ID.
175+
(raw_id + 1) as u32
176+
}
177+
178+
fn make_filenames_buffer(&self, tcx: TyCtxt<'_>) -> Vec<u8> {
149179
// LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
150180
// requires setting the first filename to the compilation directory.
151181
// Since rustc generates coverage maps with relative paths, the
152182
// compilation directory can be combined with the relative paths
153183
// to get absolute paths, if needed.
154184
use rustc_session::RemapFileNameExt;
155-
let working_dir =
156-
Symbol::intern(&tcx.sess.opts.working_dir.for_codegen(&tcx.sess).to_string_lossy());
157-
global_file_table.insert(working_dir);
158-
Self { global_file_table }
159-
}
160-
161-
fn global_file_id_for_file_name(&mut self, file_name: Symbol) -> u32 {
162-
let (global_file_id, _) = self.global_file_table.insert_full(file_name);
163-
global_file_id as u32
164-
}
165-
166-
fn into_filenames_buffer(self) -> Vec<u8> {
167-
// This method takes `self` so that the caller can't accidentally
168-
// modify the original file table after encoding it into a buffer.
185+
let working_dir: &str = &tcx.sess.opts.working_dir.for_codegen(&tcx.sess).to_string_lossy();
169186

170187
llvm::build_byte_buffer(|buffer| {
171188
coverageinfo::write_filenames_section_to_buffer(
172-
self.global_file_table.iter().map(Symbol::as_str),
189+
// Insert the working dir at index 0, before the other filenames.
190+
std::iter::once(working_dir).chain(self.raw_file_table.iter().map(Symbol::as_str)),
173191
buffer,
174192
);
175193
})
@@ -182,7 +200,7 @@ impl GlobalFileTable {
182200
///
183201
/// Newly-encountered filenames will be added to the global file table.
184202
fn encode_mappings_for_function(
185-
global_file_table: &mut GlobalFileTable,
203+
global_file_table: &GlobalFileTable,
186204
function_coverage: &FunctionCoverage<'_>,
187205
) -> Vec<u8> {
188206
let mut counter_regions = function_coverage.counter_regions().collect::<Vec<_>>();
@@ -203,7 +221,7 @@ fn encode_mappings_for_function(
203221
for counter_regions_for_file in
204222
counter_regions.group_by(|(_, a), (_, b)| a.file_name == b.file_name)
205223
{
206-
// Look up (or allocate) the global file ID for this filename.
224+
// Look up the global file ID for this filename.
207225
let file_name = counter_regions_for_file[0].1.file_name;
208226
let global_file_id = global_file_table.global_file_id_for_file_name(file_name);
209227

0 commit comments

Comments
 (0)