Skip to content

Commit e1a2bab

Browse files
committed
coverage: Prepare mappings separately from injecting statements
These two tasks historically needed to be interleaved, but after various recent changes (including rust-lang#116046 and rust-lang#116917) they can now be fully separated.
1 parent ddca534 commit e1a2bab

File tree

2 files changed

+44
-29
lines changed

2 files changed

+44
-29
lines changed

compiler/rustc_mir_transform/src/coverage/mod.rs

+37-27
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ mod spans;
88
mod tests;
99

1010
use self::counters::{BcbCounter, CoverageCounters};
11-
use self::graph::CoverageGraph;
11+
use self::graph::{BasicCoverageBlock, CoverageGraph};
1212
use self::spans::CoverageSpans;
1313

1414
use crate::MirPass;
@@ -106,7 +106,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
106106
self.coverage_counters
107107
.make_bcb_counters(&self.basic_coverage_blocks, bcb_has_coverage_spans);
108108

109-
let mappings = self.create_mappings_and_inject_coverage_statements(&coverage_spans);
109+
let mappings = self.create_mappings(&coverage_spans);
110+
self.inject_coverage_statements(bcb_has_coverage_spans);
110111

111112
self.mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
112113
function_source_hash: self.hir_info.function_source_hash,
@@ -116,13 +117,12 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
116117
}));
117118
}
118119

119-
/// For each [`BcbCounter`] associated with a BCB node or BCB edge, create
120-
/// any corresponding mappings (for BCB nodes only), and inject any necessary
121-
/// coverage statements into MIR.
122-
fn create_mappings_and_inject_coverage_statements(
123-
&mut self,
124-
coverage_spans: &CoverageSpans,
125-
) -> Vec<Mapping> {
120+
/// For each coverage span extracted from MIR, create a corresponding
121+
/// mapping.
122+
///
123+
/// Precondition: All BCBs corresponding to those spans have been given
124+
/// coverage counters.
125+
fn create_mappings(&self, coverage_spans: &CoverageSpans) -> Vec<Mapping> {
126126
let source_map = self.tcx.sess.source_map();
127127
let body_span = self.hir_info.body_span;
128128

@@ -131,30 +131,42 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
131131
let file_name =
132132
Symbol::intern(&source_file.name.for_codegen(self.tcx.sess).to_string_lossy());
133133

134-
let mut mappings = Vec::new();
134+
coverage_spans
135+
.bcbs_with_coverage_spans()
136+
// For each BCB with spans, get a coverage term for its counter.
137+
.map(|(bcb, spans)| {
138+
let term = self
139+
.coverage_counters
140+
.bcb_counter(bcb)
141+
.expect("all BCBs with spans were given counters")
142+
.as_term();
143+
(term, spans)
144+
})
145+
// Flatten the spans into individual term/span pairs.
146+
.flat_map(|(term, spans)| spans.iter().map(move |&span| (term, span)))
147+
// Convert each span to a code region, and create the final mapping.
148+
.map(|(term, span)| {
149+
let code_region = make_code_region(source_map, file_name, span, body_span);
150+
Mapping { term, code_region }
151+
})
152+
.collect::<Vec<_>>()
153+
}
135154

136-
// Process the counters and spans associated with BCB nodes.
155+
/// For each BCB node or BCB edge that has an associated coverage counter,
156+
/// inject any necessary coverage statements into MIR.
157+
fn inject_coverage_statements(
158+
&mut self,
159+
bcb_has_coverage_spans: impl Fn(BasicCoverageBlock) -> bool,
160+
) {
161+
// Process the counters associated with BCB nodes.
137162
for (bcb, counter_kind) in self.coverage_counters.bcb_node_counters() {
138-
let spans = coverage_spans.spans_for_bcb(bcb);
139-
let has_mappings = !spans.is_empty();
140-
141-
// If this BCB has any coverage spans, add corresponding mappings to
142-
// the mappings table.
143-
if has_mappings {
144-
let term = counter_kind.as_term();
145-
mappings.extend(spans.iter().map(|&span| {
146-
let code_region = make_code_region(source_map, file_name, span, body_span);
147-
Mapping { code_region, term }
148-
}));
149-
}
150-
151163
let do_inject = match counter_kind {
152164
// Counter-increment statements always need to be injected.
153165
BcbCounter::Counter { .. } => true,
154166
// The only purpose of expression-used statements is to detect
155167
// when a mapping is unreachable, so we only inject them for
156168
// expressions with one or more mappings.
157-
BcbCounter::Expression { .. } => has_mappings,
169+
BcbCounter::Expression { .. } => bcb_has_coverage_spans(bcb),
158170
};
159171
if do_inject {
160172
inject_statement(
@@ -192,8 +204,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
192204
// Inject a counter into the newly-created BB.
193205
inject_statement(self.mir_body, self.make_mir_coverage_kind(counter_kind), new_bb);
194206
}
195-
196-
mappings
197207
}
198208

199209
fn make_mir_coverage_kind(&self, counter_kind: &BcbCounter) -> CoverageKind {

compiler/rustc_mir_transform/src/coverage/spans.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,13 @@ impl CoverageSpans {
4848
!self.bcb_to_spans[bcb].is_empty()
4949
}
5050

51-
pub(super) fn spans_for_bcb(&self, bcb: BasicCoverageBlock) -> &[Span] {
52-
&self.bcb_to_spans[bcb]
51+
pub(super) fn bcbs_with_coverage_spans(
52+
&self,
53+
) -> impl Iterator<Item = (BasicCoverageBlock, &[Span])> {
54+
self.bcb_to_spans.iter_enumerated().filter_map(|(bcb, spans)| {
55+
// Only yield BCBs that have at least one coverage span.
56+
(!spans.is_empty()).then_some((bcb, spans.as_slice()))
57+
})
5358
}
5459
}
5560

0 commit comments

Comments
 (0)