Auto merge of #78267 - richkadel:llvm-coverage-counters-2.0.3r1, r=tmandry

Working expression optimization, and some improvements to branch-level source coverage

This replaces PR #78040 after reorganizing the original commits (by request) into a more logical sequence of major changes.

Most of the work is in the MIR `transform/coverage/` directory (originally, `transform/instrument_coverage.rs`).

Note this PR includes some significant additional debugging capabilities, to help myself and any future developer working on coverage improvements or issues.

In particular, there's a new Graphviz (.dot file) output for the coverage graph (the `BasicCoverageBlock` control flow graph) that provides ways to get some very good insight into the relationships between the MIR, the coverage graph BCBs, coverage spans, and counters. (There are also some cool debugging options, available via environment variable, to alter how some data in the graph appears.)

And the code for this Graphviz view is actually generic... it can be used by any implementation of the Rust `Graph` traits.

Finally (for now), I also now output information from `llvm-cov` that shows the actual counters and spans it found in the coverage map, and their counts (from the `--debug` flag). I found this to be enormously helpful in debugging some coverage issues, so I kept it in the test results as well for additional context.

`@tmandry` `@wesleywiser`

r? `@tmandry`

Here's an example of the new coverage graph:

* Within each `BasicCoverageBlock` (BCB), you can see each `CoverageSpan` and its contributing statements (MIR `Statement`s and/or `Terminator`s)
* Each `CoverageSpan` has a `Counter` or and `Expression`, and `Expression`s show their Add/Subtract operation with nested operations. (This can be changed to show the Counter and Expression IDs instead, or in addition to, the BCB.)
* The terminators of all MIR `BasicBlock`s in the BCB, including one final `Terminator`
* If an "edge counter" is required (because we need to count an edge between blocks, in some cases) the edge's Counter or Expression is shown next to its label. (Not shown in the example below.) (FYI, Edge Counters are converted into a new MIR `BasicBlock` with `Goto`)

<img width="1116" alt="Screen Shot 2020-10-17 at 12 23 29 AM" src="https://user-images.githubusercontent.com/3827298/96331095-616cb480-100f-11eb-8212-60f2d433e2d8.png">

r? `@tmandry`
FYI: `@wesleywiser`
This commit is contained in:
bors 2020-11-06 06:59:44 +00:00
commit 8532e742fc
234 changed files with 14075 additions and 2124 deletions

View file

@ -7,6 +7,10 @@ use std::cmp::Ord;
use std::fmt::{self, Debug, Formatter};
rustc_index::newtype_index! {
/// An ExpressionOperandId value is assigned directly from either a
/// CounterValueReference.as_u32() (which ascend from 1) or an ExpressionOperandId.as_u32()
/// (which _*descend*_ from u32::MAX). Id value `0` (zero) represents a virtual counter with a
/// constant value of `0`.
pub struct ExpressionOperandId {
derive [HashStable]
DEBUG_FORMAT = "ExpressionOperandId({})",
@ -42,6 +46,20 @@ impl CounterValueReference {
}
rustc_index::newtype_index! {
/// InjectedExpressionId.as_u32() converts to ExpressionOperandId.as_u32()
///
/// Values descend from u32::MAX.
pub struct InjectedExpressionId {
derive [HashStable]
DEBUG_FORMAT = "InjectedExpressionId({})",
MAX = 0xFFFF_FFFF,
}
}
rustc_index::newtype_index! {
/// InjectedExpressionIndex.as_u32() translates to u32::MAX - ExpressionOperandId.as_u32()
///
/// Values ascend from 0.
pub struct InjectedExpressionIndex {
derive [HashStable]
DEBUG_FORMAT = "InjectedExpressionIndex({})",
@ -50,6 +68,9 @@ rustc_index::newtype_index! {
}
rustc_index::newtype_index! {
/// MappedExpressionIndex values ascend from zero, and are recalculated indexes based on their
/// array position in the LLVM coverage map "Expressions" array, which is assembled during the
/// "mapgen" process. They cannot be computed algorithmically, from the other `newtype_index`s.
pub struct MappedExpressionIndex {
derive [HashStable]
DEBUG_FORMAT = "MappedExpressionIndex({})",
@ -64,21 +85,21 @@ impl From<CounterValueReference> for ExpressionOperandId {
}
}
impl From<InjectedExpressionIndex> for ExpressionOperandId {
impl From<InjectedExpressionId> for ExpressionOperandId {
#[inline]
fn from(v: InjectedExpressionIndex) -> ExpressionOperandId {
fn from(v: InjectedExpressionId) -> ExpressionOperandId {
ExpressionOperandId::from(v.as_u32())
}
}
#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, HashStable, TypeFoldable)]
#[derive(Clone, PartialEq, TyEncodable, TyDecodable, HashStable, TypeFoldable)]
pub enum CoverageKind {
Counter {
function_source_hash: u64,
id: CounterValueReference,
},
Expression {
id: InjectedExpressionIndex,
id: InjectedExpressionId,
lhs: ExpressionOperandId,
op: Op,
rhs: ExpressionOperandId,
@ -88,12 +109,47 @@ pub enum CoverageKind {
impl CoverageKind {
pub fn as_operand_id(&self) -> ExpressionOperandId {
use CoverageKind::*;
match *self {
CoverageKind::Counter { id, .. } => ExpressionOperandId::from(id),
CoverageKind::Expression { id, .. } => ExpressionOperandId::from(id),
CoverageKind::Unreachable => {
bug!("Unreachable coverage cannot be part of an expression")
}
Counter { id, .. } => ExpressionOperandId::from(id),
Expression { id, .. } => ExpressionOperandId::from(id),
Unreachable => bug!("Unreachable coverage cannot be part of an expression"),
}
}
pub fn is_counter(&self) -> bool {
match self {
Self::Counter { .. } => true,
_ => false,
}
}
pub fn is_expression(&self) -> bool {
match self {
Self::Expression { .. } => true,
_ => false,
}
}
pub fn is_unreachable(&self) -> bool {
*self == Self::Unreachable
}
}
impl Debug for CoverageKind {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
use CoverageKind::*;
match self {
Counter { id, .. } => write!(fmt, "Counter({:?})", id.index()),
Expression { id, lhs, op, rhs } => write!(
fmt,
"Expression({:?}) = {} {} {}",
id.index(),
lhs.index(),
if *op == Op::Add { "+" } else { "-" },
rhs.index(),
),
Unreachable => write!(fmt, "Unreachable"),
}
}
}