coverage: Add enums to accommodate other kinds of coverage mappings

This commit is contained in:
Zalathar 2023-12-28 14:07:18 +11:00
parent c5932182ad
commit 124fff0777
9 changed files with 94 additions and 49 deletions

View file

@ -1,4 +1,4 @@
use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId}; use rustc_middle::mir::coverage::{CodeRegion, CounterId, CovTerm, ExpressionId, MappingKind};
/// Must match the layout of `LLVMRustCounterKind`. /// Must match the layout of `LLVMRustCounterKind`.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
@ -149,6 +149,24 @@ pub struct CounterMappingRegion {
} }
impl CounterMappingRegion { impl CounterMappingRegion {
pub(crate) fn from_mapping(
mapping_kind: &MappingKind,
local_file_id: u32,
code_region: &CodeRegion,
) -> Self {
let &CodeRegion { file_name: _, start_line, start_col, end_line, end_col } = code_region;
match *mapping_kind {
MappingKind::Code(term) => Self::code_region(
Counter::from_term(term),
local_file_id,
start_line,
start_col,
end_line,
end_col,
),
}
}
pub(crate) fn code_region( pub(crate) fn code_region(
counter: Counter, counter: Counter,
file_id: u32, file_id: u32,

View file

@ -4,7 +4,8 @@ use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::fx::FxIndexSet;
use rustc_index::bit_set::BitSet; use rustc_index::bit_set::BitSet;
use rustc_middle::mir::coverage::{ use rustc_middle::mir::coverage::{
CodeRegion, CounterId, CovTerm, Expression, ExpressionId, FunctionCoverageInfo, Mapping, Op, CodeRegion, CounterId, CovTerm, Expression, ExpressionId, FunctionCoverageInfo, Mapping,
MappingKind, Op,
}; };
use rustc_middle::ty::Instance; use rustc_middle::ty::Instance;
use rustc_span::Symbol; use rustc_span::Symbol;
@ -64,8 +65,8 @@ impl<'tcx> FunctionCoverageCollector<'tcx> {
// For each expression ID that is directly used by one or more mappings, // For each expression ID that is directly used by one or more mappings,
// mark it as not-yet-seen. This indicates that we expect to see a // mark it as not-yet-seen. This indicates that we expect to see a
// corresponding `ExpressionUsed` statement during MIR traversal. // corresponding `ExpressionUsed` statement during MIR traversal.
for Mapping { term, .. } in &function_coverage_info.mappings { for term in function_coverage_info.mappings.iter().flat_map(|m| m.kind.terms()) {
if let &CovTerm::Expression(id) = term { if let CovTerm::Expression(id) = term {
expressions_seen.remove(id); expressions_seen.remove(id);
} }
} }
@ -221,20 +222,21 @@ impl<'tcx> FunctionCoverage<'tcx> {
/// that will be used by `mapgen` when preparing for FFI. /// that will be used by `mapgen` when preparing for FFI.
pub(crate) fn counter_regions( pub(crate) fn counter_regions(
&self, &self,
) -> impl Iterator<Item = (Counter, &CodeRegion)> + ExactSizeIterator { ) -> impl Iterator<Item = (MappingKind, &CodeRegion)> + ExactSizeIterator {
self.function_coverage_info.mappings.iter().map(move |mapping| { self.function_coverage_info.mappings.iter().map(move |mapping| {
let &Mapping { term, ref code_region } = mapping; let Mapping { kind, code_region } = mapping;
let counter = self.counter_for_term(term); let kind =
(counter, code_region) kind.map_terms(|term| if self.is_zero_term(term) { CovTerm::Zero } else { term });
(kind, code_region)
}) })
} }
fn counter_for_term(&self, term: CovTerm) -> Counter { fn counter_for_term(&self, term: CovTerm) -> Counter {
if is_zero_term(&self.counters_seen, &self.zero_expressions, term) { if self.is_zero_term(term) { Counter::ZERO } else { Counter::from_term(term) }
Counter::ZERO }
} else {
Counter::from_term(term) fn is_zero_term(&self, term: CovTerm) -> bool {
} is_zero_term(&self.counters_seen, &self.zero_expressions, term)
} }
} }

View file

@ -12,7 +12,6 @@ use rustc_hir::def_id::DefId;
use rustc_index::IndexVec; use rustc_index::IndexVec;
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::mir; use rustc_middle::mir;
use rustc_middle::mir::coverage::CodeRegion;
use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::ty::{self, TyCtxt};
use rustc_span::def_id::DefIdSet; use rustc_span::def_id::DefIdSet;
use rustc_span::Symbol; use rustc_span::Symbol;
@ -237,7 +236,7 @@ fn encode_mappings_for_function(
// Prepare file IDs for each filename, and prepare the mapping data so that // Prepare file IDs for each filename, and prepare the mapping data so that
// we can pass it through FFI to LLVM. // we can pass it through FFI to LLVM.
for (file_name, counter_regions_for_file) in for (file_name, counter_regions_for_file) in
&counter_regions.group_by(|(_counter, region)| region.file_name) &counter_regions.group_by(|(_, region)| region.file_name)
{ {
// Look up the global file ID for this filename. // Look up the global file ID for this filename.
let global_file_id = global_file_table.global_file_id_for_file_name(file_name); let global_file_id = global_file_table.global_file_id_for_file_name(file_name);
@ -248,17 +247,12 @@ fn encode_mappings_for_function(
// For each counter/region pair in this function+file, convert it to a // For each counter/region pair in this function+file, convert it to a
// form suitable for FFI. // form suitable for FFI.
for (counter, region) in counter_regions_for_file { for (mapping_kind, region) in counter_regions_for_file {
let CodeRegion { file_name: _, start_line, start_col, end_line, end_col } = *region; debug!("Adding counter {mapping_kind:?} to map for {region:?}");
mapping_regions.push(CounterMappingRegion::from_mapping(
debug!("Adding counter {counter:?} to map for {region:?}"); &mapping_kind,
mapping_regions.push(CounterMappingRegion::code_region(
counter,
local_file_id.as_u32(), local_file_id.as_u32(),
start_line, region,
start_col,
end_line,
end_col,
)); ));
} }
} }

View file

@ -160,16 +160,34 @@ pub struct Expression {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] #[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
pub struct Mapping { pub enum MappingKind {
pub code_region: CodeRegion, /// Associates a normal region of code with a counter/expression/zero.
Code(CovTerm),
}
/// Indicates whether this mapping uses a counter value, expression value, impl MappingKind {
/// or zero value. /// Iterator over all coverage terms in this mapping kind.
/// pub fn terms(&self) -> impl Iterator<Item = CovTerm> {
/// FIXME: When we add support for mapping kinds other than `Code` let one = |a| std::iter::once(a);
/// (e.g. branch regions, expansion regions), replace this with a dedicated match *self {
/// mapping-kind enum. Self::Code(term) => one(term),
pub term: CovTerm, }
}
/// Returns a copy of this mapping kind, in which all coverage terms have
/// been replaced with ones returned by the given function.
pub fn map_terms(&self, map_fn: impl Fn(CovTerm) -> CovTerm) -> Self {
match *self {
Self::Code(term) => Self::Code(map_fn(term)),
}
}
}
#[derive(Clone, Debug)]
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
pub struct Mapping {
pub kind: MappingKind,
pub code_region: CodeRegion,
} }
/// Stores per-function coverage information attached to a `mir::Body`, /// Stores per-function coverage information attached to a `mir::Body`,

View file

@ -493,8 +493,8 @@ fn write_function_coverage_info(
for (id, expression) in expressions.iter_enumerated() { for (id, expression) in expressions.iter_enumerated() {
writeln!(w, "{INDENT}coverage {id:?} => {expression:?};")?; writeln!(w, "{INDENT}coverage {id:?} => {expression:?};")?;
} }
for coverage::Mapping { term, code_region } in mappings { for coverage::Mapping { kind, code_region } in mappings {
writeln!(w, "{INDENT}coverage {term:?} => {code_region:?};")?; writeln!(w, "{INDENT}coverage {kind:?} => {code_region:?};")?;
} }
writeln!(w)?; writeln!(w)?;

View file

@ -9,7 +9,7 @@ mod tests;
use self::counters::{BcbCounter, CoverageCounters}; use self::counters::{BcbCounter, CoverageCounters};
use self::graph::{BasicCoverageBlock, CoverageGraph}; use self::graph::{BasicCoverageBlock, CoverageGraph};
use self::spans::{BcbMapping, CoverageSpans}; use self::spans::{BcbMapping, BcbMappingKind, CoverageSpans};
use crate::MirPass; use crate::MirPass;
@ -150,10 +150,12 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
coverage_spans coverage_spans
.all_bcb_mappings() .all_bcb_mappings()
.filter_map(|&BcbMapping { bcb, span }| { .filter_map(|&BcbMapping { kind: bcb_mapping_kind, span }| {
let term = term_for_bcb(bcb); let kind = match bcb_mapping_kind {
BcbMappingKind::Code(bcb) => MappingKind::Code(term_for_bcb(bcb)),
};
let code_region = make_code_region(source_map, file_name, span, body_span)?; let code_region = make_code_region(source_map, file_name, span, body_span)?;
Some(Mapping { term, code_region }) Some(Mapping { kind, code_region })
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }

View file

@ -8,9 +8,15 @@ use crate::coverage::ExtractedHirInfo;
mod from_mir; mod from_mir;
#[derive(Clone, Copy, Debug)]
pub(super) enum BcbMappingKind {
/// Associates an ordinary executable code span with its corresponding BCB.
Code(BasicCoverageBlock),
}
#[derive(Debug)] #[derive(Debug)]
pub(super) struct BcbMapping { pub(super) struct BcbMapping {
pub(super) bcb: BasicCoverageBlock, pub(super) kind: BcbMappingKind,
pub(super) span: Span, pub(super) span: Span,
} }
@ -38,7 +44,7 @@ impl CoverageSpans {
); );
mappings.extend(coverage_spans.into_iter().map(|CoverageSpan { bcb, span, .. }| { mappings.extend(coverage_spans.into_iter().map(|CoverageSpan { bcb, span, .. }| {
// Each span produced by the generator represents an ordinary code region. // Each span produced by the generator represents an ordinary code region.
BcbMapping { bcb, span } BcbMapping { kind: BcbMappingKind::Code(bcb), span }
})); }));
if mappings.is_empty() { if mappings.is_empty() {
@ -47,8 +53,13 @@ impl CoverageSpans {
// Identify which BCBs have one or more mappings. // Identify which BCBs have one or more mappings.
let mut bcb_has_mappings = BitSet::new_empty(basic_coverage_blocks.num_nodes()); let mut bcb_has_mappings = BitSet::new_empty(basic_coverage_blocks.num_nodes());
for &BcbMapping { bcb, span: _ } in &mappings { let mut insert = |bcb| {
bcb_has_mappings.insert(bcb); bcb_has_mappings.insert(bcb);
};
for &BcbMapping { kind, span: _ } in &mappings {
match kind {
BcbMappingKind::Code(bcb) => insert(bcb),
}
} }
Some(Self { bcb_has_mappings, mappings }) Some(Self { bcb_has_mappings, mappings })

View file

@ -4,7 +4,7 @@
fn bar() -> bool { fn bar() -> bool {
let mut _0: bool; let mut _0: bool;
+ coverage Counter(0) => /the/src/instrument_coverage.rs:21:1 - 23:2; + coverage Code(Counter(0)) => /the/src/instrument_coverage.rs:21:1 - 23:2;
+ +
bb0: { bb0: {
+ Coverage::CounterIncrement(0); + Coverage::CounterIncrement(0);

View file

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