Skip to content

Commit bb9e33a

Browse files
committed
coverage: Store intermediate region tables in CovfunRecord
This defers the call to `llvm_cov::write_function_mappings_to_buffer` until just before its enclosing global variable is created.
1 parent 9830831 commit bb9e33a

File tree

4 files changed

+75
-55
lines changed

4 files changed

+75
-55
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs

+28
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,34 @@ impl CoverageSpan {
152152
}
153153
}
154154

155+
/// Holds tables of the various region types in one struct.
156+
///
157+
/// Don't pass this struct across FFI; pass the individual region tables as
158+
/// pointer/length pairs instead.
159+
///
160+
/// Each field name has a `_regions` suffix for improved readability after
161+
/// exhaustive destructing, which ensures that all region types are handled.
162+
#[derive(Clone, Debug, Default)]
163+
pub(crate) struct Regions {
164+
pub(crate) code_regions: Vec<CodeRegion>,
165+
pub(crate) branch_regions: Vec<BranchRegion>,
166+
pub(crate) mcdc_branch_regions: Vec<MCDCBranchRegion>,
167+
pub(crate) mcdc_decision_regions: Vec<MCDCDecisionRegion>,
168+
}
169+
170+
impl Regions {
171+
/// Returns true if none of this structure's tables contain any regions.
172+
pub(crate) fn has_no_regions(&self) -> bool {
173+
let Self { code_regions, branch_regions, mcdc_branch_regions, mcdc_decision_regions } =
174+
self;
175+
176+
code_regions.is_empty()
177+
&& branch_regions.is_empty()
178+
&& mcdc_branch_regions.is_empty()
179+
&& mcdc_decision_regions.is_empty()
180+
}
181+
}
182+
155183
/// Must match the layout of `LLVMRustCoverageCodeRegion`.
156184
#[derive(Clone, Debug)]
157185
#[repr(C)]

compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,10 @@ pub(crate) fn write_filenames_to_buffer<'a>(
6262
pub(crate) fn write_function_mappings_to_buffer(
6363
virtual_file_mapping: &[u32],
6464
expressions: &[ffi::CounterExpression],
65-
code_regions: &[ffi::CodeRegion],
66-
branch_regions: &[ffi::BranchRegion],
67-
mcdc_branch_regions: &[ffi::MCDCBranchRegion],
68-
mcdc_decision_regions: &[ffi::MCDCDecisionRegion],
65+
regions: &ffi::Regions,
6966
) -> Vec<u8> {
67+
let ffi::Regions { code_regions, branch_regions, mcdc_branch_regions, mcdc_decision_regions } =
68+
regions;
7069
llvm::build_byte_buffer(|buffer| unsafe {
7170
llvm::LLVMRustCoverageWriteFunctionMappingsToBuffer(
7271
virtual_file_mapping.as_ptr(),

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ rustc_index::newtype_index! {
194194

195195
/// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU)
196196
/// file IDs.
197-
#[derive(Default)]
197+
#[derive(Debug, Default)]
198198
struct VirtualFileMapping {
199199
local_to_global: IndexVec<LocalFileId, GlobalFileId>,
200200
global_to_local: FxIndexMap<GlobalFileId, LocalFileId>,
@@ -208,10 +208,8 @@ impl VirtualFileMapping {
208208
.or_insert_with(|| self.local_to_global.push(global_file_id))
209209
}
210210

211-
fn into_vec(self) -> Vec<u32> {
212-
// This conversion should be optimized away to ~zero overhead.
213-
// In any case, it's probably not hot enough to worry about.
214-
self.local_to_global.into_iter().map(|global| global.as_u32()).collect()
211+
fn to_vec(&self) -> Vec<u32> {
212+
self.local_to_global.iter().map(|&global| GlobalFileId::as_u32(global)).collect()
215213
}
216214
}
217215

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs

+41-46
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ pub(crate) struct CovfunRecord<'tcx> {
2929
mangled_function_name: &'tcx str,
3030
source_hash: u64,
3131
is_used: bool,
32-
coverage_mapping_buffer: Vec<u8>,
32+
33+
virtual_file_mapping: VirtualFileMapping,
34+
expressions: Vec<ffi::CounterExpression>,
35+
regions: ffi::Regions,
3336
}
3437

3538
impl<'tcx> CovfunRecord<'tcx> {
@@ -46,61 +49,54 @@ pub(crate) fn prepare_covfun_record<'tcx>(
4649
instance: Instance<'tcx>,
4750
function_coverage: &FunctionCoverage<'tcx>,
4851
) -> Option<CovfunRecord<'tcx>> {
49-
let mangled_function_name = tcx.symbol_name(instance).name;
50-
let source_hash = function_coverage.source_hash();
51-
let is_used = function_coverage.is_used();
52-
53-
let coverage_mapping_buffer =
54-
encode_mappings_for_function(tcx, global_file_table, function_coverage);
55-
56-
if coverage_mapping_buffer.is_empty() {
57-
if function_coverage.is_used() {
58-
bug!(
59-
"A used function should have had coverage mapping data but did not: {}",
60-
mangled_function_name
61-
);
52+
let mut covfun = CovfunRecord {
53+
mangled_function_name: tcx.symbol_name(instance).name,
54+
source_hash: function_coverage.source_hash(),
55+
is_used: function_coverage.is_used(),
56+
virtual_file_mapping: VirtualFileMapping::default(),
57+
expressions: function_coverage.counter_expressions().collect::<Vec<_>>(),
58+
regions: ffi::Regions::default(),
59+
};
60+
61+
fill_region_tables(tcx, global_file_table, function_coverage, &mut covfun);
62+
63+
if covfun.regions.has_no_regions() {
64+
if covfun.is_used {
65+
bug!("a used function should have had coverage mapping data but did not: {covfun:?}");
6266
} else {
63-
debug!("unused function had no coverage mapping data: {}", mangled_function_name);
67+
debug!(?covfun, "unused function had no coverage mapping data");
6468
return None;
6569
}
6670
}
6771

68-
Some(CovfunRecord { mangled_function_name, source_hash, is_used, coverage_mapping_buffer })
72+
Some(covfun)
6973
}
7074

71-
/// Using the expressions and counter regions collected for a single function,
72-
/// generate the variable-sized payload of its corresponding `__llvm_covfun`
73-
/// entry. The payload is returned as a vector of bytes.
74-
///
75-
/// Newly-encountered filenames will be added to the global file table.
76-
fn encode_mappings_for_function(
77-
tcx: TyCtxt<'_>,
75+
/// Populates the mapping region tables in the current function's covfun record.
76+
fn fill_region_tables<'tcx>(
77+
tcx: TyCtxt<'tcx>,
7878
global_file_table: &GlobalFileTable,
79-
function_coverage: &FunctionCoverage<'_>,
80-
) -> Vec<u8> {
79+
function_coverage: &FunctionCoverage<'tcx>,
80+
covfun: &mut CovfunRecord<'tcx>,
81+
) {
8182
let counter_regions = function_coverage.counter_regions();
8283
if counter_regions.is_empty() {
83-
return Vec::new();
84+
return;
8485
}
8586

86-
let expressions = function_coverage.counter_expressions().collect::<Vec<_>>();
87-
88-
let mut virtual_file_mapping = VirtualFileMapping::default();
89-
let mut code_regions = vec![];
90-
let mut branch_regions = vec![];
91-
let mut mcdc_branch_regions = vec![];
92-
let mut mcdc_decision_regions = vec![];
93-
9487
// Currently a function's mappings must all be in the same file as its body span.
9588
let file_name = span_file_name(tcx, function_coverage.function_coverage_info.body_span);
9689

9790
// Look up the global file ID for that filename.
9891
let global_file_id = global_file_table.global_file_id_for_file_name(file_name);
9992

10093
// Associate that global file ID with a local file ID for this function.
101-
let local_file_id = virtual_file_mapping.local_id_for_global(global_file_id);
94+
let local_file_id = covfun.virtual_file_mapping.local_id_for_global(global_file_id);
10295
debug!(" file id: {local_file_id:?} => {global_file_id:?} = '{file_name:?}'");
10396

97+
let ffi::Regions { code_regions, branch_regions, mcdc_branch_regions, mcdc_decision_regions } =
98+
&mut covfun.regions;
99+
104100
// For each counter/region pair in this function+file, convert it to a
105101
// form suitable for FFI.
106102
for (mapping_kind, region) in counter_regions {
@@ -133,16 +129,6 @@ fn encode_mappings_for_function(
133129
}
134130
}
135131
}
136-
137-
// Encode the function's coverage mappings into a buffer.
138-
llvm_cov::write_function_mappings_to_buffer(
139-
&virtual_file_mapping.into_vec(),
140-
&expressions,
141-
&code_regions,
142-
&branch_regions,
143-
&mcdc_branch_regions,
144-
&mcdc_decision_regions,
145-
)
146132
}
147133

148134
/// Generates the contents of the covfun record for this function, which
@@ -157,9 +143,18 @@ pub(crate) fn generate_covfun_record<'tcx>(
157143
mangled_function_name,
158144
source_hash,
159145
is_used,
160-
ref coverage_mapping_buffer, // Previously-encoded coverage mappings
146+
ref virtual_file_mapping,
147+
ref expressions,
148+
ref regions,
161149
} = covfun;
162150

151+
// Encode the function's coverage mappings into a buffer.
152+
let coverage_mapping_buffer = llvm_cov::write_function_mappings_to_buffer(
153+
&virtual_file_mapping.to_vec(),
154+
expressions,
155+
regions,
156+
);
157+
163158
// Concatenate the encoded coverage mappings
164159
let coverage_mapping_size = coverage_mapping_buffer.len();
165160
let coverage_mapping_val = cx.const_bytes(&coverage_mapping_buffer);

0 commit comments

Comments
 (0)