Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update some outdated descriptions of coverage instrumentation #1809

Merged
merged 1 commit into from
Nov 4, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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).
Comment on lines -330 to -332
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The extra fields mentioned here were moved out into side-tables in CoverageCounters in rust-lang/rust#114354.

(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()`
Comment on lines -346 to -350
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The three inject function mentioned here were removed by rust-lang/rust#116917, and replaced with a much simpler pair of loops in create_mappings_and_inject_coverage_statements.

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