Skip to content

Commit

Permalink
Update some outdated descriptions of coverage instrumentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Zalathar authored and davidtwco committed Nov 4, 2023
1 parent cd0eabf commit f42a31f
Showing 1 changed file with 46 additions and 77 deletions.
123 changes: 46 additions & 77 deletions src/llvm-coverage-instrumentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,21 +73,21 @@ When compiling with `-C instrument-coverage`,
Coverage instrumentation is performed on the MIR with a [MIR pass][mir-passes]
called [`InstrumentCoverage`][mir-instrument-coverage]. This MIR pass analyzes
the control flow graph (CFG)--represented by MIR `BasicBlock`s--to identify
code branches, and injects additional [`Coverage`][coverage-statement]
statements into the `BasicBlock`s.
code branches, attaches [`FunctionCoverageInfo`] to the function's body,
and injects additional [`Coverage`][coverage-statement] statements into the
`BasicBlock`s.

A MIR `Coverage` statement is a virtual instruction that indicates a counter
should be incremented when its adjacent statements are executed, to count
a span of code ([`CodeRegion`][code-region]). It counts the number of times a
branch is executed, and also specifies the exact location of that code span in
the Rust source code.
branch is executed, and is referred to by coverage mappings in the function's
coverage-info struct.

Note that many of these `Coverage` statements will _not_ be converted into
Note that many coverage counters will _not_ be converted into
physical counters (or any other executable instructions) in the final binary.
Some of them will be (see [`CoverageKind::Counter`]),
Some of them will be (see [`CoverageKind::CounterIncrement`]),
but other counters can be computed on the fly, when generating a coverage
report, by mapping a `CodeRegion` to a
[`CoverageKind::Expression`].
report, by mapping a `CodeRegion` to a coverage-counter _expression_.

As an example:

Expand Down Expand Up @@ -121,18 +121,18 @@ determines when to break out of a loop (a `while` condition, or an `if` or
`match` with a `break`). In MIR, this is typically lowered to a `SwitchInt`,
with one branch to stay in the loop, and another branch to break out of the
loop. The branch that breaks out will almost always execute less often,
so `InstrumentCoverage` chooses to add a `Counter` to that branch, and an
`Expression(continue) = Counter(loop) - Counter(break)` to the branch that
so `InstrumentCoverage` chooses to add a `CounterIncrement` to that branch, and
uses an expression (`Counter(loop) - Counter(break)`) for the branch that
continues.

The `InstrumentCoverage` MIR pass is documented in
[more detail below][instrument-coverage-pass-details].

[mir-passes]: mir/passes.md
[mir-instrument-coverage]: https://github.com/rust-lang/rust/tree/master/compiler/rustc_mir_transform/src/coverage
[`FunctionCoverageInfo`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/coverage/struct.FunctionCoverageInfo.html
[code-region]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/coverage/struct.CodeRegion.html
[`CoverageKind::Counter`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/coverage/enum.CoverageKind.html#variant.Counter
[`CoverageKind::Expression`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/coverage/enum.CoverageKind.html#variant.Expression
[`CoverageKind::CounterIncrement`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/coverage/enum.CoverageKind.html#variant.CounterIncrement
[coverage-statement]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.StatementKind.html#variant.Coverage
[instrument-coverage-pass-details]: #implementation-details-of-the-instrumentcoverage-mir-pass

Expand All @@ -150,40 +150,38 @@ MIR `Statement` into some backend-specific action or instruction.
match statement.kind {
...
mir::StatementKind::Coverage(box ref coverage) => {
self.codegen_coverage(&mut bx, coverage.clone(), statement.source_info.scope);
bx
self.codegen_coverage(bx, coverage, statement.source_info.scope);
}
```

`codegen_coverage()` handles each `CoverageKind` as follows:
`codegen_coverage()` handles inlined statements and then forwards the coverage
statement to [`Builder::add_coverage`], which handles each `CoverageKind` as
follows:

- For all `CoverageKind`s, Coverage data (counter ID, expression equation
and ID, and code regions) are passed to the backend's `Builder`, to
populate data structures that will be used to generate the crate's
"Coverage Map". (See the [`FunctionCoverage`][function-coverage] `struct`.)
- For `CoverageKind::Counter`s, an instruction is injected in the backend

- For both `CounterIncrement` and `ExpressionUsed`, the underlying counter or
expression ID is passed through to the corresponding [`FunctionCoverage`]
struct to indicate that the corresponding regions of code were not removed
by MIR optimizations.
- For `CoverageKind::CounterIncrement`s, an instruction is injected in the backend
IR to increment the physical counter, by calling the `BuilderMethod`
[`instrprof_increment()`][instrprof-increment].

```rust
pub fn codegen_coverage(&self, bx: &mut Bx, coverage: Coverage, scope: SourceScope) {
fn add_coverage(&mut self, instance: Instance<'tcx>, coverage: &Coverage) {
...
let instance = ... // the scoped instance (current or inlined function)
let Coverage { kind, code_region } = coverage;
match kind {
CoverageKind::Counter { function_source_hash, id } => {
...
bx.add_coverage_counter(instance, id, code_region);
let Coverage { kind } = coverage;
match *kind {
CoverageKind::CounterIncrement { id } => {
func_coverage.mark_counter_id_seen(id);
...
bx.instrprof_increment(fn_name, hash, num_counters, index);
}
CoverageKind::Expression { id, lhs, op, rhs } => {
bx.add_coverage_counter_expression(instance, id, lhs, op, rhs, code_region);
CoverageKind::ExpressionUsed { id } => {
func_coverage.mark_expression_id_seen(id);
}
CoverageKind::Unreachable => {
bx.add_coverage_unreachable(
instance,
code_region.expect(...
}
}
```

> The function name `instrprof_increment()` is taken from the LLVM intrinsic
Expand All @@ -199,7 +197,8 @@ statements is only implemented for LLVM, at this time.
[backend-lowering-mir]: backend/lowering-mir.md
[codegen-statement]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/struct.FunctionCx.html#method.codegen_statement
[codegen-coverage]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/struct.FunctionCx.html#method.codegen_coverage
[function-coverage]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/coverageinfo/map_data/struct.FunctionCoverage.html
[`Builder::add_coverage`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/builder/struct.Builder.html#method.add_coverage
[`FunctionCoverage`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/coverageinfo/map_data/struct.FunctionCoverage.html
[instrprof-increment]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/traits/trait.BuilderMethods.html#tymethod.instrprof_increment

### Coverage Map Generation
Expand Down Expand Up @@ -327,9 +326,10 @@ Instrumentor::new(&self.name(), tcx, mir_body).inject_counters();
The `CoverageGraph` is a coverage-specific simplification of the MIR control
flow graph (CFG). Its nodes are [`BasicCoverageBlock`s][bcb], which
encompass one or more sequentially-executed MIR `BasicBlock`s
(with no internal branching), plus a `CoverageKind` counter (to
be added, via coverage analysis), and an optional set of additional counters
to count incoming edges (if there are more than one).
(with no internal branching).

Nodes and edges in the graph can have associated [`BcbCounter`]s, which are
stored in [`CoverageCounters`].

The `Instrumentor`'s `inject_counters()` uses the `CoverageGraph` to
compute the best places to inject coverage counters, as MIR `Statement`s,
Expand All @@ -338,16 +338,15 @@ with the following steps:
1. [`generate_coverage_spans()`][generate-coverage-spans] computes the minimum set of distinct,
non-branching code regions, from the MIR. These `CoverageSpan`s
represent a span of code that must be counted.
2. [`make_bcb_counters()`][make-bcb-counters] generates `CoverageKind::Counter`s and
`CoverageKind::Expression`s for each `CoverageSpan`, plus additional
`intermediate_expressions`[^intermediate-expressions], not associated with any `CodeRegion`, but
2. [`make_bcb_counters()`][make-bcb-counters] generates `BcbCounter::Counter`s and
`BcbCounter::Expression`s for each `CoverageSpan`, plus additional
_intermediate expressions_[^intermediate-expressions] that are not associated
with any `CodeRegion`, but
are required to compute a final `Expression` value for a `CodeRegion`.
3. Inject the new counters into the MIR, as new `StatementKind::Coverage`
statements. This is done by three distinct functions:
- `inject_coverage_span_counters()`
- `inject_indirect_counters()`
- `inject_intermediate_expression()`, called for each intermediate expression
returned from `make_bcb_counters()`
statements.
4. Attach all other necessary coverage information to the function's body as
[`FunctionCoverageInfo`].

[^intermediate-expressions]: Intermediate expressions are sometimes required
because `Expression`s are limited to binary additions or subtractions. For
Expand All @@ -359,7 +358,8 @@ intermediate expression for `B - C`.
[coverage-graph]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/graph/struct.CoverageGraph.html
[inject-counters]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/struct.Instrumentor.html#method.inject_counters
[bcb]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/graph/struct.BasicCoverageBlock.html
[debug]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/debug
[`BcbCounter`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/counters/enum.BcbCounter.html
[`CoverageCounters`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/counters/struct.CoverageCounters.html
[generate-coverage-spans]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/spans/struct.CoverageSpans.html#method.generate_coverage_spans
[make-bcb-counters]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/counters/struct.BcbCounters.html#method.make_bcb_counters

Expand Down Expand Up @@ -505,34 +505,3 @@ its `Counter` or `Expression`.

[bcb-counters]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/counters/struct.BcbCounters.html
[traverse-coverage-graph-with-loops]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/graph/struct.TraverseCoverageGraphWithLoops.html

### Injecting counters into a MIR `BasicBlock`

With the refined `CoverageSpan`s, and after all `Counter`s and `Expression`s are
created, the final step is to inject the `StatementKind::Coverage` statements
into the MIR. There are three distinct sources, handled by the following
functions:

- [`inject_coverage_span_counters()`][inject-coverage-span-counters] injects the
counter from each `CoverageSpan`'s BCB.
- [`inject_indirect_counters()`][inject-indirect-counters] injects counters
for any BCB not assigned to a `CoverageSpan`, and for all edge counters.
These counters don't have `CoverageSpan`s.
- [`inject_intermediate_expression()`][inject-intermediate-expression] injects
the intermediate expressions returned from `make_bcb_counters()`. These
counters aren't associated with any BCB, edge, or `CoverageSpan`.

These three functions inject the `Coverage` statements into the MIR.
`Counter`s and `Expression`s with `CoverageSpan`s add `Coverage` statements
to a corresponding `BasicBlock`, with a `CodeRegion` computed from the
refined `Span` and current `SourceMap`.

All other `Coverage` statements have a `CodeRegion` of `None`, but they
still must be injected because they contribute to other `Expression`s.

Finally, edge's with a `CoverageKind::Counter` require a new `BasicBlock`,
so the counter is only incremented when traversing the branch edge.

[inject-coverage-span-counters]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/struct.Instrumentor.html#method.inject_coverage_span_counters
[inject-indirect-counters]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/struct.Instrumentor.html#method.inject_indirect_counters
[inject-intermediate-expression]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/fn.inject_intermediate_expression.html

0 comments on commit f42a31f

Please sign in to comment.