Skip to content

Commit c0fd496

Browse files
authored
Rollup merge of rust-lang#119842 - Zalathar:kind, r=oli-obk
coverage: Add enums to accommodate other kinds of coverage mappings Extracted from rust-lang#118305. LLVM supports several different kinds of coverage mapping regions, but currently we only ever emit ordinary “code” regions. This PR performs the plumbing required to add other kinds of regions as enum variants, but does not add any specific variants other than `Code`. The main motivation for this change is branch coverage, but it will also allow separate experimentation with gap regions and skipped regions, which might help in producing more accurate and useful coverage reports. --- `@rustbot` label +A-code-coverage
2 parents 86c79bd + 124fff0 commit c0fd496

File tree

9 files changed

+122
-73
lines changed

9 files changed

+122
-73
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId};
1+
use rustc_middle::mir::coverage::{CodeRegion, CounterId, CovTerm, ExpressionId, MappingKind};
22

33
/// Must match the layout of `LLVMRustCounterKind`.
44
#[derive(Copy, Clone, Debug)]
@@ -149,6 +149,24 @@ pub struct CounterMappingRegion {
149149
}
150150

151151
impl CounterMappingRegion {
152+
pub(crate) fn from_mapping(
153+
mapping_kind: &MappingKind,
154+
local_file_id: u32,
155+
code_region: &CodeRegion,
156+
) -> Self {
157+
let &CodeRegion { file_name: _, start_line, start_col, end_line, end_col } = code_region;
158+
match *mapping_kind {
159+
MappingKind::Code(term) => Self::code_region(
160+
Counter::from_term(term),
161+
local_file_id,
162+
start_line,
163+
start_col,
164+
end_line,
165+
end_col,
166+
),
167+
}
168+
}
169+
152170
pub(crate) fn code_region(
153171
counter: Counter,
154172
file_id: u32,

compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs

+14-12
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use rustc_data_structures::captures::Captures;
44
use rustc_data_structures::fx::FxIndexSet;
55
use rustc_index::bit_set::BitSet;
66
use rustc_middle::mir::coverage::{
7-
CodeRegion, CounterId, CovTerm, Expression, ExpressionId, FunctionCoverageInfo, Mapping, Op,
7+
CodeRegion, CounterId, CovTerm, Expression, ExpressionId, FunctionCoverageInfo, Mapping,
8+
MappingKind, Op,
89
};
910
use rustc_middle::ty::Instance;
1011
use rustc_span::Symbol;
@@ -64,8 +65,8 @@ impl<'tcx> FunctionCoverageCollector<'tcx> {
6465
// For each expression ID that is directly used by one or more mappings,
6566
// mark it as not-yet-seen. This indicates that we expect to see a
6667
// corresponding `ExpressionUsed` statement during MIR traversal.
67-
for Mapping { term, .. } in &function_coverage_info.mappings {
68-
if let &CovTerm::Expression(id) = term {
68+
for term in function_coverage_info.mappings.iter().flat_map(|m| m.kind.terms()) {
69+
if let CovTerm::Expression(id) = term {
6970
expressions_seen.remove(id);
7071
}
7172
}
@@ -221,20 +222,21 @@ impl<'tcx> FunctionCoverage<'tcx> {
221222
/// that will be used by `mapgen` when preparing for FFI.
222223
pub(crate) fn counter_regions(
223224
&self,
224-
) -> impl Iterator<Item = (Counter, &CodeRegion)> + ExactSizeIterator {
225+
) -> impl Iterator<Item = (MappingKind, &CodeRegion)> + ExactSizeIterator {
225226
self.function_coverage_info.mappings.iter().map(move |mapping| {
226-
let &Mapping { term, ref code_region } = mapping;
227-
let counter = self.counter_for_term(term);
228-
(counter, code_region)
227+
let Mapping { kind, code_region } = mapping;
228+
let kind =
229+
kind.map_terms(|term| if self.is_zero_term(term) { CovTerm::Zero } else { term });
230+
(kind, code_region)
229231
})
230232
}
231233

232234
fn counter_for_term(&self, term: CovTerm) -> Counter {
233-
if is_zero_term(&self.counters_seen, &self.zero_expressions, term) {
234-
Counter::ZERO
235-
} else {
236-
Counter::from_term(term)
237-
}
235+
if self.is_zero_term(term) { Counter::ZERO } else { Counter::from_term(term) }
236+
}
237+
238+
fn is_zero_term(&self, term: CovTerm) -> bool {
239+
is_zero_term(&self.counters_seen, &self.zero_expressions, term)
238240
}
239241
}
240242

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

+6-12
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use rustc_hir::def_id::DefId;
1212
use rustc_index::IndexVec;
1313
use rustc_middle::bug;
1414
use rustc_middle::mir;
15-
use rustc_middle::mir::coverage::CodeRegion;
1615
use rustc_middle::ty::{self, TyCtxt};
1716
use rustc_span::def_id::DefIdSet;
1817
use rustc_span::Symbol;
@@ -237,7 +236,7 @@ fn encode_mappings_for_function(
237236
// Prepare file IDs for each filename, and prepare the mapping data so that
238237
// we can pass it through FFI to LLVM.
239238
for (file_name, counter_regions_for_file) in
240-
&counter_regions.group_by(|(_counter, region)| region.file_name)
239+
&counter_regions.group_by(|(_, region)| region.file_name)
241240
{
242241
// Look up the global file ID for this filename.
243242
let global_file_id = global_file_table.global_file_id_for_file_name(file_name);
@@ -248,17 +247,12 @@ fn encode_mappings_for_function(
248247

249248
// For each counter/region pair in this function+file, convert it to a
250249
// form suitable for FFI.
251-
for (counter, region) in counter_regions_for_file {
252-
let CodeRegion { file_name: _, start_line, start_col, end_line, end_col } = *region;
253-
254-
debug!("Adding counter {counter:?} to map for {region:?}");
255-
mapping_regions.push(CounterMappingRegion::code_region(
256-
counter,
250+
for (mapping_kind, region) in counter_regions_for_file {
251+
debug!("Adding counter {mapping_kind:?} to map for {region:?}");
252+
mapping_regions.push(CounterMappingRegion::from_mapping(
253+
&mapping_kind,
257254
local_file_id.as_u32(),
258-
start_line,
259-
start_col,
260-
end_line,
261-
end_col,
255+
region,
262256
));
263257
}
264258
}

compiler/rustc_middle/src/mir/coverage.rs

+26-8
Original file line numberDiff line numberDiff line change
@@ -158,18 +158,36 @@ pub struct Expression {
158158
pub rhs: CovTerm,
159159
}
160160

161+
#[derive(Clone, Debug)]
162+
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
163+
pub enum MappingKind {
164+
/// Associates a normal region of code with a counter/expression/zero.
165+
Code(CovTerm),
166+
}
167+
168+
impl MappingKind {
169+
/// Iterator over all coverage terms in this mapping kind.
170+
pub fn terms(&self) -> impl Iterator<Item = CovTerm> {
171+
let one = |a| std::iter::once(a);
172+
match *self {
173+
Self::Code(term) => one(term),
174+
}
175+
}
176+
177+
/// Returns a copy of this mapping kind, in which all coverage terms have
178+
/// been replaced with ones returned by the given function.
179+
pub fn map_terms(&self, map_fn: impl Fn(CovTerm) -> CovTerm) -> Self {
180+
match *self {
181+
Self::Code(term) => Self::Code(map_fn(term)),
182+
}
183+
}
184+
}
185+
161186
#[derive(Clone, Debug)]
162187
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
163188
pub struct Mapping {
189+
pub kind: MappingKind,
164190
pub code_region: CodeRegion,
165-
166-
/// Indicates whether this mapping uses a counter value, expression value,
167-
/// or zero value.
168-
///
169-
/// FIXME: When we add support for mapping kinds other than `Code`
170-
/// (e.g. branch regions, expansion regions), replace this with a dedicated
171-
/// mapping-kind enum.
172-
pub term: CovTerm,
173191
}
174192

175193
/// Stores per-function coverage information attached to a `mir::Body`,

compiler/rustc_middle/src/mir/pretty.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -493,8 +493,8 @@ fn write_function_coverage_info(
493493
for (id, expression) in expressions.iter_enumerated() {
494494
writeln!(w, "{INDENT}coverage {id:?} => {expression:?};")?;
495495
}
496-
for coverage::Mapping { term, code_region } in mappings {
497-
writeln!(w, "{INDENT}coverage {term:?} => {code_region:?};")?;
496+
for coverage::Mapping { kind, code_region } in mappings {
497+
writeln!(w, "{INDENT}coverage {kind:?} => {code_region:?};")?;
498498
}
499499
writeln!(w)?;
500500

compiler/rustc_mir_transform/src/coverage/mod.rs

+14-15
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ mod tests;
99

1010
use self::counters::{BcbCounter, CoverageCounters};
1111
use self::graph::{BasicCoverageBlock, CoverageGraph};
12-
use self::spans::CoverageSpans;
12+
use self::spans::{BcbMapping, BcbMappingKind, CoverageSpans};
1313

1414
use crate::MirPass;
1515

@@ -141,22 +141,21 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
141141
let file_name =
142142
Symbol::intern(&source_file.name.for_codegen(self.tcx.sess).to_string_lossy());
143143

144+
let term_for_bcb = |bcb| {
145+
coverage_counters
146+
.bcb_counter(bcb)
147+
.expect("all BCBs with spans were given counters")
148+
.as_term()
149+
};
150+
144151
coverage_spans
145-
.bcbs_with_coverage_spans()
146-
// For each BCB with spans, get a coverage term for its counter.
147-
.map(|(bcb, spans)| {
148-
let term = coverage_counters
149-
.bcb_counter(bcb)
150-
.expect("all BCBs with spans were given counters")
151-
.as_term();
152-
(term, spans)
153-
})
154-
// Flatten the spans into individual term/span pairs.
155-
.flat_map(|(term, spans)| spans.iter().map(move |&span| (term, span)))
156-
// Convert each span to a code region, and create the final mapping.
157-
.filter_map(|(term, span)| {
152+
.all_bcb_mappings()
153+
.filter_map(|&BcbMapping { kind: bcb_mapping_kind, span }| {
154+
let kind = match bcb_mapping_kind {
155+
BcbMappingKind::Code(bcb) => MappingKind::Code(term_for_bcb(bcb)),
156+
};
158157
let code_region = make_code_region(source_map, file_name, span, body_span)?;
159-
Some(Mapping { term, code_region })
158+
Some(Mapping { kind, code_region })
160159
})
161160
.collect::<Vec<_>>()
162161
}

compiler/rustc_mir_transform/src/coverage/spans.rs

+35-17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use rustc_data_structures::graph::WithNumNodes;
2-
use rustc_index::IndexVec;
2+
use rustc_index::bit_set::BitSet;
33
use rustc_middle::mir;
44
use rustc_span::{BytePos, Span, DUMMY_SP};
55

@@ -8,9 +8,21 @@ use crate::coverage::ExtractedHirInfo;
88

99
mod from_mir;
1010

11+
#[derive(Clone, Copy, Debug)]
12+
pub(super) enum BcbMappingKind {
13+
/// Associates an ordinary executable code span with its corresponding BCB.
14+
Code(BasicCoverageBlock),
15+
}
16+
17+
#[derive(Debug)]
18+
pub(super) struct BcbMapping {
19+
pub(super) kind: BcbMappingKind,
20+
pub(super) span: Span,
21+
}
22+
1123
pub(super) struct CoverageSpans {
12-
/// Map from BCBs to their list of coverage spans.
13-
bcb_to_spans: IndexVec<BasicCoverageBlock, Vec<Span>>,
24+
bcb_has_mappings: BitSet<BasicCoverageBlock>,
25+
mappings: Vec<BcbMapping>,
1426
}
1527

1628
impl CoverageSpans {
@@ -23,36 +35,42 @@ impl CoverageSpans {
2335
hir_info: &ExtractedHirInfo,
2436
basic_coverage_blocks: &CoverageGraph,
2537
) -> Option<Self> {
38+
let mut mappings = vec![];
39+
2640
let coverage_spans = CoverageSpansGenerator::generate_coverage_spans(
2741
mir_body,
2842
hir_info,
2943
basic_coverage_blocks,
3044
);
45+
mappings.extend(coverage_spans.into_iter().map(|CoverageSpan { bcb, span, .. }| {
46+
// Each span produced by the generator represents an ordinary code region.
47+
BcbMapping { kind: BcbMappingKind::Code(bcb), span }
48+
}));
3149

32-
if coverage_spans.is_empty() {
50+
if mappings.is_empty() {
3351
return None;
3452
}
3553

36-
// Group the coverage spans by BCB, with the BCBs in sorted order.
37-
let mut bcb_to_spans = IndexVec::from_elem_n(Vec::new(), basic_coverage_blocks.num_nodes());
38-
for CoverageSpan { bcb, span, .. } in coverage_spans {
39-
bcb_to_spans[bcb].push(span);
54+
// Identify which BCBs have one or more mappings.
55+
let mut bcb_has_mappings = BitSet::new_empty(basic_coverage_blocks.num_nodes());
56+
let mut insert = |bcb| {
57+
bcb_has_mappings.insert(bcb);
58+
};
59+
for &BcbMapping { kind, span: _ } in &mappings {
60+
match kind {
61+
BcbMappingKind::Code(bcb) => insert(bcb),
62+
}
4063
}
4164

42-
Some(Self { bcb_to_spans })
65+
Some(Self { bcb_has_mappings, mappings })
4366
}
4467

4568
pub(super) fn bcb_has_coverage_spans(&self, bcb: BasicCoverageBlock) -> bool {
46-
!self.bcb_to_spans[bcb].is_empty()
69+
self.bcb_has_mappings.contains(bcb)
4770
}
4871

49-
pub(super) fn bcbs_with_coverage_spans(
50-
&self,
51-
) -> impl Iterator<Item = (BasicCoverageBlock, &[Span])> {
52-
self.bcb_to_spans.iter_enumerated().filter_map(|(bcb, spans)| {
53-
// Only yield BCBs that have at least one coverage span.
54-
(!spans.is_empty()).then_some((bcb, spans.as_slice()))
55-
})
72+
pub(super) fn all_bcb_mappings(&self) -> impl Iterator<Item = &BcbMapping> {
73+
self.mappings.iter()
5674
}
5775
}
5876

tests/mir-opt/instrument_coverage.bar.InstrumentCoverage.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
fn bar() -> bool {
55
let mut _0: bool;
66

7-
+ coverage Counter(0) => /the/src/instrument_coverage.rs:21:1 - 23:2;
7+
+ coverage Code(Counter(0)) => /the/src/instrument_coverage.rs:21:1 - 23:2;
88
+
99
bb0: {
1010
+ Coverage::CounterIncrement(0);

tests/mir-opt/instrument_coverage.main.InstrumentCoverage.diff

+5-5
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99

1010
+ coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Add, rhs: Counter(1) };
1111
+ coverage ExpressionId(1) => Expression { lhs: Expression(0), op: Subtract, rhs: Counter(1) };
12-
+ coverage Counter(0) => /the/src/instrument_coverage.rs:12:1 - 12:11;
13-
+ coverage Expression(0) => /the/src/instrument_coverage.rs:13:5 - 14:17;
14-
+ coverage Expression(1) => /the/src/instrument_coverage.rs:15:13 - 15:18;
15-
+ coverage Expression(1) => /the/src/instrument_coverage.rs:18:1 - 18:2;
16-
+ coverage Counter(1) => /the/src/instrument_coverage.rs:16:10 - 16:11;
12+
+ coverage Code(Counter(0)) => /the/src/instrument_coverage.rs:12:1 - 12:11;
13+
+ coverage Code(Expression(0)) => /the/src/instrument_coverage.rs:13:5 - 14:17;
14+
+ coverage Code(Expression(1)) => /the/src/instrument_coverage.rs:15:13 - 15:18;
15+
+ coverage Code(Counter(1)) => /the/src/instrument_coverage.rs:16:10 - 16:11;
16+
+ coverage Code(Expression(1)) => /the/src/instrument_coverage.rs:18:1 - 18:2;
1717
+
1818
bb0: {
1919
+ Coverage::CounterIncrement(0);

0 commit comments

Comments
 (0)