2020-07-29 06:09:16 +00:00
|
|
|
pub use super::ffi::*;
|
|
|
|
|
2020-07-29 00:45:58 +00:00
|
|
|
use rustc_index::vec::IndexVec;
|
2020-08-15 11:42:13 +00:00
|
|
|
use rustc_middle::mir::coverage::{
|
|
|
|
CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionIndex,
|
|
|
|
MappedExpressionIndex, Op,
|
|
|
|
};
|
2020-07-25 04:14:28 +00:00
|
|
|
use rustc_middle::ty::Instance;
|
|
|
|
use rustc_middle::ty::TyCtxt;
|
2020-07-02 18:27:15 +00:00
|
|
|
|
2020-07-25 04:14:28 +00:00
|
|
|
#[derive(Clone, Debug)]
|
2020-08-15 11:42:13 +00:00
|
|
|
pub struct ExpressionRegion {
|
2020-07-29 00:45:58 +00:00
|
|
|
lhs: ExpressionOperandId,
|
2020-08-15 11:42:13 +00:00
|
|
|
op: Op,
|
2020-07-29 00:45:58 +00:00
|
|
|
rhs: ExpressionOperandId,
|
2020-08-15 11:42:13 +00:00
|
|
|
region: CodeRegion,
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Collects all of the coverage regions associated with (a) injected counters, (b) counter
|
|
|
|
/// expressions (additions or subtraction), and (c) unreachable regions (always counted as zero),
|
2020-07-25 04:14:28 +00:00
|
|
|
/// for a given Function. Counters and counter expressions have non-overlapping `id`s because they
|
|
|
|
/// can both be operands in an expression. This struct also stores the `function_source_hash`,
|
|
|
|
/// computed during instrumentation, and forwarded with counters.
|
2020-06-22 06:29:08 +00:00
|
|
|
///
|
2020-07-25 04:14:28 +00:00
|
|
|
/// Note, it may be important to understand LLVM's definitions of `unreachable` regions versus "gap
|
|
|
|
/// regions" (or "gap areas"). A gap region is a code region within a counted region (either counter
|
|
|
|
/// or expression), but the line or lines in the gap region are not executable (such as lines with
|
|
|
|
/// only whitespace or comments). According to LLVM Code Coverage Mapping documentation, "A count
|
|
|
|
/// for a gap area is only used as the line execution count if there are no other regions on a
|
|
|
|
/// line."
|
2020-08-15 11:42:13 +00:00
|
|
|
pub struct FunctionCoverage {
|
2020-07-02 18:27:15 +00:00
|
|
|
source_hash: u64,
|
2020-08-15 11:42:13 +00:00
|
|
|
counters: IndexVec<CounterValueReference, Option<CodeRegion>>,
|
|
|
|
expressions: IndexVec<InjectedExpressionIndex, Option<ExpressionRegion>>,
|
|
|
|
unreachable_regions: Vec<CodeRegion>,
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 11:42:13 +00:00
|
|
|
impl FunctionCoverage {
|
|
|
|
pub fn new<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self {
|
2020-07-25 04:14:28 +00:00
|
|
|
let coverageinfo = tcx.coverageinfo(instance.def_id());
|
2020-07-02 18:27:15 +00:00
|
|
|
Self {
|
|
|
|
source_hash: 0, // will be set with the first `add_counter()`
|
2020-07-29 00:45:58 +00:00
|
|
|
counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize),
|
|
|
|
expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize),
|
2020-07-25 04:14:28 +00:00
|
|
|
unreachable_regions: Vec::new(),
|
2020-07-02 18:27:15 +00:00
|
|
|
}
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
|
|
|
|
2020-07-25 04:14:28 +00:00
|
|
|
/// Adds a code region to be counted by an injected counter intrinsic.
|
|
|
|
/// The source_hash (computed during coverage instrumentation) should also be provided, and
|
|
|
|
/// should be the same for all counters in a given function.
|
2020-08-15 11:42:13 +00:00
|
|
|
pub fn add_counter(&mut self, source_hash: u64, id: CounterValueReference, region: CodeRegion) {
|
2020-07-25 04:14:28 +00:00
|
|
|
if self.source_hash == 0 {
|
|
|
|
self.source_hash = source_hash;
|
|
|
|
} else {
|
|
|
|
debug_assert_eq!(source_hash, self.source_hash);
|
|
|
|
}
|
2020-08-15 11:42:13 +00:00
|
|
|
self.counters[id].replace(region).expect_none("add_counter called with duplicate `id`");
|
2020-07-02 18:27:15 +00:00
|
|
|
}
|
|
|
|
|
2020-07-25 04:14:28 +00:00
|
|
|
/// Both counters and "counter expressions" (or simply, "expressions") can be operands in other
|
|
|
|
/// expressions. Expression IDs start from `u32::MAX` and go down, so the range of expression
|
|
|
|
/// IDs will not overlap with the range of counter IDs. Counters and expressions can be added in
|
|
|
|
/// any order, and expressions can still be assigned contiguous (though descending) IDs, without
|
|
|
|
/// knowing what the last counter ID will be.
|
|
|
|
///
|
|
|
|
/// When storing the expression data in the `expressions` vector in the `FunctionCoverage`
|
|
|
|
/// struct, its vector index is computed, from the given expression ID, by subtracting from
|
|
|
|
/// `u32::MAX`.
|
|
|
|
///
|
|
|
|
/// Since the expression operands (`lhs` and `rhs`) can reference either counters or
|
|
|
|
/// expressions, an operand that references an expression also uses its original ID, descending
|
|
|
|
/// from `u32::MAX`. Theses operands are translated only during code generation, after all
|
|
|
|
/// counters and expressions have been added.
|
2020-07-02 18:27:15 +00:00
|
|
|
pub fn add_counter_expression(
|
|
|
|
&mut self,
|
2020-08-15 11:42:13 +00:00
|
|
|
expression_id: InjectedExpressionIndex,
|
|
|
|
lhs: ExpressionOperandId,
|
|
|
|
op: Op,
|
|
|
|
rhs: ExpressionOperandId,
|
|
|
|
region: CodeRegion,
|
2020-06-22 06:29:08 +00:00
|
|
|
) {
|
2020-08-15 11:42:13 +00:00
|
|
|
let expression_index = self.expression_index(u32::from(expression_id));
|
2020-07-25 04:14:28 +00:00
|
|
|
self.expressions[expression_index]
|
2020-08-02 03:03:59 +00:00
|
|
|
.replace(ExpressionRegion { lhs, op, rhs, region })
|
2020-07-25 04:14:28 +00:00
|
|
|
.expect_none("add_counter_expression called with duplicate `id_descending_from_max`");
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
|
|
|
|
2020-07-25 04:14:28 +00:00
|
|
|
/// Add a region that will be marked as "unreachable", with a constant "zero counter".
|
2020-08-15 11:42:13 +00:00
|
|
|
pub fn add_unreachable_region(&mut self, region: CodeRegion) {
|
2020-08-02 03:03:59 +00:00
|
|
|
self.unreachable_regions.push(region)
|
2020-07-02 18:27:15 +00:00
|
|
|
}
|
|
|
|
|
2020-07-25 04:14:28 +00:00
|
|
|
/// Return the source hash, generated from the HIR node structure, and used to indicate whether
|
|
|
|
/// or not the source code structure changed between different compilations.
|
2020-07-02 18:27:15 +00:00
|
|
|
pub fn source_hash(&self) -> u64 {
|
|
|
|
self.source_hash
|
|
|
|
}
|
|
|
|
|
2020-07-25 04:14:28 +00:00
|
|
|
/// Generate an array of CounterExpressions, and an iterator over all `Counter`s and their
|
|
|
|
/// associated `Regions` (from which the LLVM-specific `CoverageMapGenerator` will create
|
|
|
|
/// `CounterMappingRegion`s.
|
2020-08-15 11:42:13 +00:00
|
|
|
pub fn get_expressions_and_counter_regions<'a>(
|
|
|
|
&'a self,
|
|
|
|
) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &'a CodeRegion)>) {
|
2020-07-02 18:27:15 +00:00
|
|
|
assert!(self.source_hash != 0);
|
2020-07-25 04:14:28 +00:00
|
|
|
|
|
|
|
let counter_regions = self.counter_regions();
|
2020-07-29 00:45:58 +00:00
|
|
|
let (counter_expressions, expression_regions) = self.expressions_with_regions();
|
2020-07-25 04:14:28 +00:00
|
|
|
let unreachable_regions = self.unreachable_regions();
|
|
|
|
|
|
|
|
let counter_regions =
|
|
|
|
counter_regions.chain(expression_regions.into_iter().chain(unreachable_regions));
|
2020-07-29 00:45:58 +00:00
|
|
|
(counter_expressions, counter_regions)
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 11:42:13 +00:00
|
|
|
fn counter_regions<'a>(&'a self) -> impl Iterator<Item = (Counter, &'a CodeRegion)> {
|
2020-07-29 00:45:58 +00:00
|
|
|
self.counters.iter_enumerated().filter_map(|(index, entry)| {
|
2020-07-25 04:14:28 +00:00
|
|
|
// Option::map() will return None to filter out missing counters. This may happen
|
|
|
|
// if, for example, a MIR-instrumented counter is removed during an optimization.
|
2020-07-29 00:45:58 +00:00
|
|
|
entry.as_ref().map(|region| {
|
|
|
|
(Counter::counter_value_reference(index as CounterValueReference), region)
|
|
|
|
})
|
2020-07-25 04:14:28 +00:00
|
|
|
})
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
|
|
|
|
2020-07-25 04:14:28 +00:00
|
|
|
fn expressions_with_regions(
|
2020-08-15 11:42:13 +00:00
|
|
|
&'a self,
|
|
|
|
) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &'a CodeRegion)>) {
|
2020-07-25 04:14:28 +00:00
|
|
|
let mut counter_expressions = Vec::with_capacity(self.expressions.len());
|
|
|
|
let mut expression_regions = Vec::with_capacity(self.expressions.len());
|
2020-07-29 00:45:58 +00:00
|
|
|
let mut new_indexes =
|
|
|
|
IndexVec::from_elem_n(MappedExpressionIndex::from(u32::MAX), self.expressions.len());
|
|
|
|
// Note, the initial value shouldn't matter since every index in use in `self.expressions`
|
|
|
|
// will be set, and after that, `new_indexes` will only be accessed using those same
|
|
|
|
// indexes.
|
2020-07-25 04:14:28 +00:00
|
|
|
|
|
|
|
// Note that an `ExpressionRegion`s at any given index can include other expressions as
|
|
|
|
// operands, but expression operands can only come from the subset of expressions having
|
|
|
|
// `expression_index`s lower than the referencing `ExpressionRegion`. Therefore, it is
|
|
|
|
// reasonable to look up the new index of an expression operand while the `new_indexes`
|
|
|
|
// vector is only complete up to the current `ExpressionIndex`.
|
2020-07-29 00:45:58 +00:00
|
|
|
let id_to_counter =
|
|
|
|
|new_indexes: &IndexVec<InjectedExpressionIndex, MappedExpressionIndex>,
|
|
|
|
id: ExpressionOperandId| {
|
|
|
|
if id.index() < self.counters.len() {
|
|
|
|
let index = CounterValueReference::from(id.index());
|
|
|
|
self.counters
|
|
|
|
.get(index)
|
|
|
|
.unwrap() // pre-validated
|
|
|
|
.as_ref()
|
|
|
|
.map(|_| Counter::counter_value_reference(index))
|
|
|
|
} else {
|
2020-08-15 11:42:13 +00:00
|
|
|
let index = self.expression_index(u32::from(id));
|
2020-07-29 00:45:58 +00:00
|
|
|
self.expressions
|
|
|
|
.get(index)
|
|
|
|
.expect("expression id is out of range")
|
|
|
|
.as_ref()
|
|
|
|
.map(|_| Counter::expression(new_indexes[index]))
|
|
|
|
}
|
|
|
|
};
|
2020-07-25 04:14:28 +00:00
|
|
|
|
|
|
|
for (original_index, expression_region) in
|
2020-07-29 00:45:58 +00:00
|
|
|
self.expressions.iter_enumerated().filter_map(|(original_index, entry)| {
|
2020-07-25 04:14:28 +00:00
|
|
|
// Option::map() will return None to filter out missing expressions. This may happen
|
|
|
|
// if, for example, a MIR-instrumented expression is removed during an optimization.
|
|
|
|
entry.as_ref().map(|region| (original_index, region))
|
|
|
|
})
|
|
|
|
{
|
|
|
|
let region = &expression_region.region;
|
|
|
|
let ExpressionRegion { lhs, op, rhs, .. } = *expression_region;
|
|
|
|
|
|
|
|
if let Some(Some((lhs_counter, rhs_counter))) =
|
|
|
|
id_to_counter(&new_indexes, lhs).map(|lhs_counter| {
|
|
|
|
id_to_counter(&new_indexes, rhs).map(|rhs_counter| (lhs_counter, rhs_counter))
|
|
|
|
})
|
|
|
|
{
|
|
|
|
// Both operands exist. `Expression` operands exist in `self.expressions` and have
|
|
|
|
// been assigned a `new_index`.
|
2020-07-29 00:45:58 +00:00
|
|
|
let mapped_expression_index =
|
|
|
|
MappedExpressionIndex::from(counter_expressions.len());
|
2020-08-15 11:42:13 +00:00
|
|
|
counter_expressions.push(CounterExpression::new(
|
|
|
|
lhs_counter,
|
|
|
|
match op {
|
|
|
|
Op::Add => ExprKind::Add,
|
|
|
|
Op::Subtract => ExprKind::Subtract,
|
|
|
|
},
|
|
|
|
rhs_counter,
|
|
|
|
));
|
2020-07-29 00:45:58 +00:00
|
|
|
new_indexes[original_index] = mapped_expression_index;
|
|
|
|
expression_regions.push((Counter::expression(mapped_expression_index), region));
|
2020-07-02 18:27:15 +00:00
|
|
|
}
|
|
|
|
}
|
2020-07-25 04:14:28 +00:00
|
|
|
(counter_expressions, expression_regions.into_iter())
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
2020-07-02 18:27:15 +00:00
|
|
|
|
2020-08-15 11:42:13 +00:00
|
|
|
fn unreachable_regions<'a>(&'a self) -> impl Iterator<Item = (Counter, &'a CodeRegion)> {
|
2020-07-25 04:14:28 +00:00
|
|
|
self.unreachable_regions.iter().map(|region| (Counter::zero(), region))
|
|
|
|
}
|
|
|
|
|
2020-08-15 11:42:13 +00:00
|
|
|
fn expression_index(&self, id_descending_from_max: u32) -> InjectedExpressionIndex {
|
|
|
|
debug_assert!(id_descending_from_max >= self.counters.len() as u32);
|
|
|
|
InjectedExpressionIndex::from(u32::MAX - id_descending_from_max)
|
2020-07-25 04:14:28 +00:00
|
|
|
}
|
2020-07-02 18:27:15 +00:00
|
|
|
}
|