1
Fork 0

coverage: Store each BCB's code regions in one coverage statement

If a BCB has more than one code region, those extra regions can now all be
stored in the same coverage statement, instead of being stored in additional
statements.
This commit is contained in:
Zalathar 2023-09-19 14:15:33 +10:00
parent ee9d00f6b8
commit 86a66c8171
20 changed files with 846 additions and 975 deletions

View file

@ -215,11 +215,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
}
}
/// Inject a counter for each coverage span. There can be multiple coverage spans for a given
/// BCB, but only one actual counter needs to be incremented per BCB. `bcb_counters` maps each
/// `bcb` to its `Counter`, when injected. Subsequent coverage spans for a BCB that already has
/// a `Counter` will inject an `Expression` instead, and compute its value by adding `ZERO` to
/// the BCB `Counter` value.
/// Injects a single [`StatementKind::Coverage`] for each BCB that has one
/// or more coverage spans.
fn inject_coverage_span_counters(&mut self, coverage_spans: &CoverageSpans) {
let tcx = self.tcx;
let source_map = tcx.sess.source_map();
@ -227,7 +224,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
let file_name = Symbol::intern(&self.source_file.name.prefer_remapped().to_string_lossy());
let mut bcb_counters = IndexVec::from_elem_n(None, self.basic_coverage_blocks.num_nodes());
for (bcb, span) in coverage_spans.bcb_span_pairs() {
for (bcb, spans) in coverage_spans.bcbs_with_coverage_spans() {
let counter_kind = if let Some(&counter_operand) = bcb_counters[bcb].as_ref() {
self.coverage_counters.make_identity_counter(counter_operand)
} else if let Some(counter_kind) = self.coverage_counters.take_bcb_counter(bcb) {
@ -237,20 +234,24 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
bug!("Every BasicCoverageBlock should have a Counter or Expression");
};
let code_region = make_code_region(source_map, file_name, span, body_span);
// Convert the coverage spans into a vector of code regions to be
// associated with this BCB's coverage statement.
let code_regions = spans
.iter()
.map(|&span| make_code_region(source_map, file_name, span, body_span))
.collect::<Vec<_>>();
inject_statement(
self.mir_body,
self.make_mir_coverage_kind(&counter_kind),
self.bcb_leader_bb(bcb),
vec![code_region],
code_regions,
);
}
}
/// `inject_coverage_span_counters()` looped through the coverage spans and injected the
/// counter from the coverage span's `BasicCoverageBlock`, removing it from the BCB in the
/// process (via `take_counter()`).
/// At this point, any BCB with coverage counters has already had its counter injected
/// into MIR, and had its counter removed from `coverage_counters` (via `take_counter()`).
///
/// Any other counter associated with a `BasicCoverageBlock`, or its incoming edge, but not
/// associated with a coverage span, should only exist if the counter is an `Expression`

View file

@ -1,7 +1,7 @@
use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB};
use rustc_data_structures::graph::WithNumNodes;
use rustc_index::bit_set::BitSet;
use rustc_index::IndexVec;
use rustc_middle::mir::{
self, AggregateKind, BasicBlock, FakeReadCause, Rvalue, Statement, StatementKind, Terminator,
TerminatorKind,
@ -12,8 +12,8 @@ use rustc_span::{BytePos, ExpnKind, MacroKind, Span, Symbol};
use std::cell::OnceCell;
pub(super) struct CoverageSpans {
coverage_spans: Vec<CoverageSpan>,
bcbs_with_coverage_spans: BitSet<BasicCoverageBlock>,
/// Map from BCBs to their list of coverage spans.
bcb_to_spans: IndexVec<BasicCoverageBlock, Vec<Span>>,
}
impl CoverageSpans {
@ -30,20 +30,26 @@ impl CoverageSpans {
basic_coverage_blocks,
);
let mut bcbs_with_coverage_spans = BitSet::new_empty(basic_coverage_blocks.num_nodes());
for coverage_span in &coverage_spans {
bcbs_with_coverage_spans.insert(coverage_span.bcb);
// Group the coverage spans by BCB, with the BCBs in sorted order.
let mut bcb_to_spans = IndexVec::from_elem_n(Vec::new(), basic_coverage_blocks.num_nodes());
for CoverageSpan { bcb, span, .. } in coverage_spans {
bcb_to_spans[bcb].push(span);
}
Self { coverage_spans, bcbs_with_coverage_spans }
Self { bcb_to_spans }
}
pub(super) fn bcb_has_coverage_spans(&self, bcb: BasicCoverageBlock) -> bool {
self.bcbs_with_coverage_spans.contains(bcb)
!self.bcb_to_spans[bcb].is_empty()
}
pub(super) fn bcb_span_pairs(&self) -> impl Iterator<Item = (BasicCoverageBlock, Span)> + '_ {
self.coverage_spans.iter().map(|&CoverageSpan { bcb, span, .. }| (bcb, span))
pub(super) fn bcbs_with_coverage_spans(
&self,
) -> impl Iterator<Item = (BasicCoverageBlock, &[Span])> {
self.bcb_to_spans.iter_enumerated().filter_map(|(bcb, spans)| {
// Only yield BCBs that have at least one coverage span.
(!spans.is_empty()).then_some((bcb, spans.as_slice()))
})
}
}