summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_mir_transform/src/coverage
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:44 +0000
commitc23a457e72abe608715ac76f076f47dc42af07a5 (patch)
tree2772049aaf84b5c9d0ed12ec8d86812f7a7904b6 /compiler/rustc_mir_transform/src/coverage
parentReleasing progress-linux version 1.73.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-c23a457e72abe608715ac76f076f47dc42af07a5.tar.xz
rustc-c23a457e72abe608715ac76f076f47dc42af07a5.zip
Merging upstream version 1.74.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_mir_transform/src/coverage')
-rw-r--r--compiler/rustc_mir_transform/src/coverage/counters.rs101
-rw-r--r--compiler/rustc_mir_transform/src/coverage/debug.rs802
-rw-r--r--compiler/rustc_mir_transform/src/coverage/graph.rs19
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mod.rs158
-rw-r--r--compiler/rustc_mir_transform/src/coverage/query.rs123
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans.rs101
6 files changed, 129 insertions, 1175 deletions
diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs
index 3d442e5dc..d56d4ad4f 100644
--- a/compiler/rustc_mir_transform/src/coverage/counters.rs
+++ b/compiler/rustc_mir_transform/src/coverage/counters.rs
@@ -1,10 +1,8 @@
use super::Error;
-use super::debug;
use super::graph;
use super::spans;
-use debug::{DebugCounters, NESTED_INDENT};
use graph::{BasicCoverageBlock, BcbBranch, CoverageGraph, TraverseCoverageGraphWithLoops};
use spans::CoverageSpan;
@@ -16,6 +14,8 @@ use rustc_middle::mir::coverage::*;
use std::fmt::{self, Debug};
+const NESTED_INDENT: &str = " ";
+
/// The coverage counter or counter expression associated with a particular
/// BCB node or BCB edge.
#[derive(Clone)]
@@ -75,8 +75,6 @@ pub(super) struct CoverageCounters {
/// BCB/edge, but are needed as operands to more complex expressions.
/// These are always [`BcbCounter::Expression`].
pub(super) intermediate_expressions: Vec<BcbCounter>,
-
- pub debug_counters: DebugCounters,
}
impl CoverageCounters {
@@ -91,17 +89,9 @@ impl CoverageCounters {
bcb_edge_counters: FxHashMap::default(),
bcb_has_incoming_edge_counters: BitSet::new_empty(num_bcbs),
intermediate_expressions: Vec::new(),
-
- debug_counters: DebugCounters::new(),
}
}
- /// Activate the `DebugCounters` data structures, to provide additional debug formatting
- /// features when formatting [`BcbCounter`] (counter) values.
- pub fn enable_debug(&mut self) {
- self.debug_counters.enable();
- }
-
/// Makes [`BcbCounter`] `Counter`s and `Expressions` for the `BasicCoverageBlock`s directly or
/// indirectly associated with `CoverageSpans`, and accumulates additional `Expression`s
/// representing intermediate values.
@@ -113,44 +103,18 @@ impl CoverageCounters {
MakeBcbCounters::new(self, basic_coverage_blocks).make_bcb_counters(coverage_spans)
}
- fn make_counter<F>(&mut self, debug_block_label_fn: F) -> BcbCounter
- where
- F: Fn() -> Option<String>,
- {
- let counter = BcbCounter::Counter { id: self.next_counter() };
- if self.debug_counters.is_enabled() {
- self.debug_counters.add_counter(&counter, (debug_block_label_fn)());
- }
- counter
+ fn make_counter(&mut self) -> BcbCounter {
+ let id = self.next_counter();
+ BcbCounter::Counter { id }
}
- fn make_expression<F>(
- &mut self,
- lhs: Operand,
- op: Op,
- rhs: Operand,
- debug_block_label_fn: F,
- ) -> BcbCounter
- where
- F: Fn() -> Option<String>,
- {
+ fn make_expression(&mut self, lhs: Operand, op: Op, rhs: Operand) -> BcbCounter {
let id = self.next_expression();
- let expression = BcbCounter::Expression { id, lhs, op, rhs };
- if self.debug_counters.is_enabled() {
- self.debug_counters.add_counter(&expression, (debug_block_label_fn)());
- }
- expression
+ BcbCounter::Expression { id, lhs, op, rhs }
}
pub fn make_identity_counter(&mut self, counter_operand: Operand) -> BcbCounter {
- let some_debug_block_label = if self.debug_counters.is_enabled() {
- self.debug_counters.some_block_label(counter_operand).cloned()
- } else {
- None
- };
- self.make_expression(counter_operand, Op::Add, Operand::Zero, || {
- some_debug_block_label.clone()
- })
+ self.make_expression(counter_operand, Op::Add, Operand::Zero)
}
/// Counter IDs start from one and go up.
@@ -367,12 +331,8 @@ impl<'a> MakeBcbCounters<'a> {
branch_counter_operand,
Op::Add,
sumup_counter_operand,
- || None,
- );
- debug!(
- " [new intermediate expression: {}]",
- self.format_counter(&intermediate_expression)
);
+ debug!(" [new intermediate expression: {:?}]", intermediate_expression);
let intermediate_expression_operand = intermediate_expression.as_operand();
self.coverage_counters.intermediate_expressions.push(intermediate_expression);
some_sumup_counter_operand.replace(intermediate_expression_operand);
@@ -394,9 +354,8 @@ impl<'a> MakeBcbCounters<'a> {
branching_counter_operand,
Op::Subtract,
sumup_counter_operand,
- || Some(format!("{expression_branch:?}")),
);
- debug!("{:?} gets an expression: {}", expression_branch, self.format_counter(&expression));
+ debug!("{:?} gets an expression: {:?}", expression_branch, expression);
let bcb = expression_branch.target_bcb;
if expression_branch.is_only_path_to_target() {
self.coverage_counters.set_bcb_counter(bcb, expression)?;
@@ -418,10 +377,10 @@ impl<'a> MakeBcbCounters<'a> {
// If the BCB already has a counter, return it.
if let Some(counter_kind) = &self.coverage_counters.bcb_counters[bcb] {
debug!(
- "{}{:?} already has a counter: {}",
+ "{}{:?} already has a counter: {:?}",
NESTED_INDENT.repeat(debug_indent_level),
bcb,
- self.format_counter(counter_kind),
+ counter_kind,
);
return Ok(counter_kind.as_operand());
}
@@ -431,22 +390,22 @@ impl<'a> MakeBcbCounters<'a> {
// program results in a tight infinite loop, but it should still compile.
let one_path_to_target = self.bcb_has_one_path_to_target(bcb);
if one_path_to_target || self.bcb_predecessors(bcb).contains(&bcb) {
- let counter_kind = self.coverage_counters.make_counter(|| Some(format!("{bcb:?}")));
+ let counter_kind = self.coverage_counters.make_counter();
if one_path_to_target {
debug!(
- "{}{:?} gets a new counter: {}",
+ "{}{:?} gets a new counter: {:?}",
NESTED_INDENT.repeat(debug_indent_level),
bcb,
- self.format_counter(&counter_kind),
+ counter_kind,
);
} else {
debug!(
"{}{:?} has itself as its own predecessor. It can't be part of its own \
- Expression sum, so it will get its own new counter: {}. (Note, the compiled \
+ Expression sum, so it will get its own new counter: {:?}. (Note, the compiled \
code will generate an infinite loop.)",
NESTED_INDENT.repeat(debug_indent_level),
bcb,
- self.format_counter(&counter_kind),
+ counter_kind,
);
}
return self.coverage_counters.set_bcb_counter(bcb, counter_kind);
@@ -481,12 +440,11 @@ impl<'a> MakeBcbCounters<'a> {
sumup_edge_counter_operand,
Op::Add,
edge_counter_operand,
- || None,
);
debug!(
- "{}new intermediate expression: {}",
+ "{}new intermediate expression: {:?}",
NESTED_INDENT.repeat(debug_indent_level),
- self.format_counter(&intermediate_expression)
+ intermediate_expression
);
let intermediate_expression_operand = intermediate_expression.as_operand();
self.coverage_counters.intermediate_expressions.push(intermediate_expression);
@@ -497,13 +455,12 @@ impl<'a> MakeBcbCounters<'a> {
first_edge_counter_operand,
Op::Add,
some_sumup_edge_counter_operand.unwrap(),
- || Some(format!("{bcb:?}")),
);
debug!(
- "{}{:?} gets a new counter (sum of predecessor counters): {}",
+ "{}{:?} gets a new counter (sum of predecessor counters): {:?}",
NESTED_INDENT.repeat(debug_indent_level),
bcb,
- self.format_counter(&counter_kind)
+ counter_kind
);
self.coverage_counters.set_bcb_counter(bcb, counter_kind)
}
@@ -534,24 +491,23 @@ impl<'a> MakeBcbCounters<'a> {
self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb))
{
debug!(
- "{}Edge {:?}->{:?} already has a counter: {}",
+ "{}Edge {:?}->{:?} already has a counter: {:?}",
NESTED_INDENT.repeat(debug_indent_level),
from_bcb,
to_bcb,
- self.format_counter(counter_kind)
+ counter_kind
);
return Ok(counter_kind.as_operand());
}
// Make a new counter to count this edge.
- let counter_kind =
- self.coverage_counters.make_counter(|| Some(format!("{from_bcb:?}->{to_bcb:?}")));
+ let counter_kind = self.coverage_counters.make_counter();
debug!(
- "{}Edge {:?}->{:?} gets a new counter: {}",
+ "{}Edge {:?}->{:?} gets a new counter: {:?}",
NESTED_INDENT.repeat(debug_indent_level),
from_bcb,
to_bcb,
- self.format_counter(&counter_kind)
+ counter_kind
);
self.coverage_counters.set_bcb_edge_counter(from_bcb, to_bcb, counter_kind)
}
@@ -710,9 +666,4 @@ impl<'a> MakeBcbCounters<'a> {
fn bcb_dominates(&self, dom: BasicCoverageBlock, node: BasicCoverageBlock) -> bool {
self.basic_coverage_blocks.dominates(dom, node)
}
-
- #[inline]
- fn format_counter(&self, counter_kind: &BcbCounter) -> String {
- self.coverage_counters.debug_counters.format_counter(counter_kind)
- }
}
diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs
deleted file mode 100644
index af616c498..000000000
--- a/compiler/rustc_mir_transform/src/coverage/debug.rs
+++ /dev/null
@@ -1,802 +0,0 @@
-//! The `InstrumentCoverage` MIR pass implementation includes debugging tools and options
-//! to help developers understand and/or improve the analysis and instrumentation of a MIR.
-//!
-//! To enable coverage, include the rustc command line option:
-//!
-//! * `-C instrument-coverage`
-//!
-//! MIR Dump Files, with additional `CoverageGraph` graphviz and `CoverageSpan` spanview
-//! ------------------------------------------------------------------------------------
-//!
-//! Additional debugging options include:
-//!
-//! * `-Z dump-mir=InstrumentCoverage` - Generate `.mir` files showing the state of the MIR,
-//! before and after the `InstrumentCoverage` pass, for each compiled function.
-//!
-//! * `-Z dump-mir-graphviz` - If `-Z dump-mir` is also enabled for the current MIR node path,
-//! each MIR dump is accompanied by a before-and-after graphical view of the MIR, in Graphviz
-//! `.dot` file format (which can be visually rendered as a graph using any of a number of free
-//! Graphviz viewers and IDE extensions).
-//!
-//! For the `InstrumentCoverage` pass, this option also enables generation of an additional
-//! Graphviz `.dot` file for each function, rendering the `CoverageGraph`: the control flow
-//! graph (CFG) of `BasicCoverageBlocks` (BCBs), as nodes, internally labeled to show the
-//! `CoverageSpan`-based MIR elements each BCB represents (`BasicBlock`s, `Statement`s and
-//! `Terminator`s), assigned coverage counters and/or expressions, and edge counters, as needed.
-//!
-//! (Note the additional option, `-Z graphviz-dark-mode`, can be added, to change the rendered
-//! output from its default black-on-white background to a dark color theme, if desired.)
-//!
-//! * `-Z dump-mir-spanview` - If `-Z dump-mir` is also enabled for the current MIR node path,
-//! each MIR dump is accompanied by a before-and-after `.html` document showing the function's
-//! original source code, highlighted by it's MIR spans, at the `statement`-level (by default),
-//! `terminator` only, or encompassing span for the `Terminator` plus all `Statement`s, in each
-//! `block` (`BasicBlock`).
-//!
-//! For the `InstrumentCoverage` pass, this option also enables generation of an additional
-//! spanview `.html` file for each function, showing the aggregated `CoverageSpan`s that will
-//! require counters (or counter expressions) for accurate coverage analysis.
-//!
-//! Debug Logging
-//! -------------
-//!
-//! The `InstrumentCoverage` pass includes debug logging messages at various phases and decision
-//! points, which can be enabled via environment variable:
-//!
-//! ```shell
-//! RUSTC_LOG=rustc_mir_transform::transform::coverage=debug
-//! ```
-//!
-//! Other module paths with coverage-related debug logs may also be of interest, particularly for
-//! debugging the coverage map data, injected as global variables in the LLVM IR (during rustc's
-//! code generation pass). For example:
-//!
-//! ```shell
-//! RUSTC_LOG=rustc_mir_transform::transform::coverage,rustc_codegen_ssa::coverageinfo,rustc_codegen_llvm::coverageinfo=debug
-//! ```
-//!
-//! Coverage Debug Options
-//! ---------------------------------
-//!
-//! Additional debugging options can be enabled using the environment variable:
-//!
-//! ```shell
-//! RUSTC_COVERAGE_DEBUG_OPTIONS=<options>
-//! ```
-//!
-//! These options are comma-separated, and specified in the format `option-name=value`. For example:
-//!
-//! ```shell
-//! $ RUSTC_COVERAGE_DEBUG_OPTIONS=counter-format=id+operation,allow-unused-expressions=yes cargo build
-//! ```
-//!
-//! Coverage debug options include:
-//!
-//! * `allow-unused-expressions=yes` or `no` (default: `no`)
-//!
-//! The `InstrumentCoverage` algorithms _should_ only create and assign expressions to a
-//! `BasicCoverageBlock`, or an incoming edge, if that expression is either (a) required to
-//! count a `CoverageSpan`, or (b) a dependency of some other required counter expression.
-//!
-//! If an expression is generated that does not map to a `CoverageSpan` or dependency, this
-//! probably indicates there was a bug in the algorithm that creates and assigns counters
-//! and expressions.
-//!
-//! When this kind of bug is encountered, the rustc compiler will panic by default. Setting:
-//! `allow-unused-expressions=yes` will log a warning message instead of panicking (effectively
-//! ignoring the unused expressions), which may be helpful when debugging the root cause of
-//! the problem.
-//!
-//! * `counter-format=<choices>`, where `<choices>` can be any plus-separated combination of `id`,
-//! `block`, and/or `operation` (default: `block+operation`)
-//!
-//! This option effects both the `CoverageGraph` (graphviz `.dot` files) and debug logging, when
-//! generating labels for counters and expressions.
-//!
-//! Depending on the values and combinations, counters can be labeled by:
-//!
-//! * `id` - counter or expression ID (ascending counter IDs, starting at 1, or descending
-//! expression IDs, starting at `u32:MAX`)
-//! * `block` - the `BasicCoverageBlock` label (for example, `bcb0`) or edge label (for
-//! example `bcb0->bcb1`), for counters or expressions assigned to count a
-//! `BasicCoverageBlock` or edge. Intermediate expressions (not directly associated with
-//! a BCB or edge) will be labeled by their expression ID, unless `operation` is also
-//! specified.
-//! * `operation` - applied to expressions only, labels include the left-hand-side counter
-//! or expression label (lhs operand), the operator (`+` or `-`), and the right-hand-side
-//! counter or expression (rhs operand). Expression operand labels are generated
-//! recursively, generating labels with nested operations, enclosed in parentheses
-//! (for example: `bcb2 + (bcb0 - bcb1)`).
-
-use super::counters::{BcbCounter, CoverageCounters};
-use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph};
-use super::spans::CoverageSpan;
-
-use itertools::Itertools;
-use rustc_middle::mir::create_dump_file;
-use rustc_middle::mir::generic_graphviz::GraphvizWriter;
-use rustc_middle::mir::spanview::{self, SpanViewable};
-
-use rustc_data_structures::fx::FxHashMap;
-use rustc_middle::mir::coverage::*;
-use rustc_middle::mir::{self, BasicBlock};
-use rustc_middle::ty::TyCtxt;
-use rustc_span::Span;
-
-use std::iter;
-use std::ops::Deref;
-use std::sync::OnceLock;
-
-pub const NESTED_INDENT: &str = " ";
-
-const RUSTC_COVERAGE_DEBUG_OPTIONS: &str = "RUSTC_COVERAGE_DEBUG_OPTIONS";
-
-pub(super) fn debug_options<'a>() -> &'a DebugOptions {
- static DEBUG_OPTIONS: OnceLock<DebugOptions> = OnceLock::new();
-
- &DEBUG_OPTIONS.get_or_init(DebugOptions::from_env)
-}
-
-/// Parses and maintains coverage-specific debug options captured from the environment variable
-/// "RUSTC_COVERAGE_DEBUG_OPTIONS", if set.
-#[derive(Debug, Clone)]
-pub(super) struct DebugOptions {
- pub allow_unused_expressions: bool,
- counter_format: ExpressionFormat,
-}
-
-impl DebugOptions {
- fn from_env() -> Self {
- let mut allow_unused_expressions = true;
- let mut counter_format = ExpressionFormat::default();
-
- if let Ok(env_debug_options) = std::env::var(RUSTC_COVERAGE_DEBUG_OPTIONS) {
- for setting_str in env_debug_options.replace(' ', "").replace('-', "_").split(',') {
- let (option, value) = match setting_str.split_once('=') {
- None => (setting_str, None),
- Some((k, v)) => (k, Some(v)),
- };
- match option {
- "allow_unused_expressions" => {
- allow_unused_expressions = bool_option_val(option, value);
- debug!(
- "{} env option `allow_unused_expressions` is set to {}",
- RUSTC_COVERAGE_DEBUG_OPTIONS, allow_unused_expressions
- );
- }
- "counter_format" => {
- match value {
- None => {
- bug!(
- "`{}` option in environment variable {} requires one or more \
- plus-separated choices (a non-empty subset of \
- `id+block+operation`)",
- option,
- RUSTC_COVERAGE_DEBUG_OPTIONS
- );
- }
- Some(val) => {
- counter_format = counter_format_option_val(val);
- debug!(
- "{} env option `counter_format` is set to {:?}",
- RUSTC_COVERAGE_DEBUG_OPTIONS, counter_format
- );
- }
- };
- }
- _ => bug!(
- "Unsupported setting `{}` in environment variable {}",
- option,
- RUSTC_COVERAGE_DEBUG_OPTIONS
- ),
- };
- }
- }
-
- Self { allow_unused_expressions, counter_format }
- }
-}
-
-fn bool_option_val(option: &str, some_strval: Option<&str>) -> bool {
- if let Some(val) = some_strval {
- if ["yes", "y", "on", "true"].contains(&val) {
- true
- } else if ["no", "n", "off", "false"].contains(&val) {
- false
- } else {
- bug!(
- "Unsupported value `{}` for option `{}` in environment variable {}",
- option,
- val,
- RUSTC_COVERAGE_DEBUG_OPTIONS
- )
- }
- } else {
- true
- }
-}
-
-fn counter_format_option_val(strval: &str) -> ExpressionFormat {
- let mut counter_format = ExpressionFormat { id: false, block: false, operation: false };
- let components = strval.splitn(3, '+');
- for component in components {
- match component {
- "id" => counter_format.id = true,
- "block" => counter_format.block = true,
- "operation" => counter_format.operation = true,
- _ => bug!(
- "Unsupported counter_format choice `{}` in environment variable {}",
- component,
- RUSTC_COVERAGE_DEBUG_OPTIONS
- ),
- }
- }
- counter_format
-}
-
-#[derive(Debug, Clone)]
-struct ExpressionFormat {
- id: bool,
- block: bool,
- operation: bool,
-}
-
-impl Default for ExpressionFormat {
- fn default() -> Self {
- Self { id: false, block: true, operation: true }
- }
-}
-
-/// If enabled, this struct maintains a map from `BcbCounter` IDs (as `Operand`) to
-/// the `BcbCounter` data and optional label (normally, the counter's associated
-/// `BasicCoverageBlock` format string, if any).
-///
-/// Use `format_counter` to convert one of these `BcbCounter` counters to a debug output string,
-/// as directed by the `DebugOptions`. This allows the format of counter labels in logs and dump
-/// files (including the `CoverageGraph` graphviz file) to be changed at runtime, via environment
-/// variable.
-///
-/// `DebugCounters` supports a recursive rendering of `Expression` counters, so they can be
-/// presented as nested expressions such as `(bcb3 - (bcb0 + bcb1))`.
-pub(super) struct DebugCounters {
- some_counters: Option<FxHashMap<Operand, DebugCounter>>,
-}
-
-impl DebugCounters {
- pub fn new() -> Self {
- Self { some_counters: None }
- }
-
- pub fn enable(&mut self) {
- debug_assert!(!self.is_enabled());
- self.some_counters.replace(FxHashMap::default());
- }
-
- pub fn is_enabled(&self) -> bool {
- self.some_counters.is_some()
- }
-
- pub fn add_counter(&mut self, counter_kind: &BcbCounter, some_block_label: Option<String>) {
- if let Some(counters) = &mut self.some_counters {
- let id = counter_kind.as_operand();
- counters
- .try_insert(id, DebugCounter::new(counter_kind.clone(), some_block_label))
- .expect("attempt to add the same counter_kind to DebugCounters more than once");
- }
- }
-
- pub fn some_block_label(&self, operand: Operand) -> Option<&String> {
- self.some_counters.as_ref().and_then(|counters| {
- counters.get(&operand).and_then(|debug_counter| debug_counter.some_block_label.as_ref())
- })
- }
-
- pub fn format_counter(&self, counter_kind: &BcbCounter) -> String {
- match *counter_kind {
- BcbCounter::Counter { .. } => {
- format!("Counter({})", self.format_counter_kind(counter_kind))
- }
- BcbCounter::Expression { .. } => {
- format!("Expression({})", self.format_counter_kind(counter_kind))
- }
- }
- }
-
- fn format_counter_kind(&self, counter_kind: &BcbCounter) -> String {
- let counter_format = &debug_options().counter_format;
- if let BcbCounter::Expression { id, lhs, op, rhs } = *counter_kind {
- if counter_format.operation {
- return format!(
- "{}{} {} {}",
- if counter_format.id || self.some_counters.is_none() {
- format!("#{} = ", id.index())
- } else {
- String::new()
- },
- self.format_operand(lhs),
- match op {
- Op::Add => "+",
- Op::Subtract => "-",
- },
- self.format_operand(rhs),
- );
- }
- }
-
- let id = counter_kind.as_operand();
- if self.some_counters.is_some() && (counter_format.block || !counter_format.id) {
- let counters = self.some_counters.as_ref().unwrap();
- if let Some(DebugCounter { some_block_label: Some(block_label), .. }) =
- counters.get(&id)
- {
- return if counter_format.id {
- format!("{}#{:?}", block_label, id)
- } else {
- block_label.to_string()
- };
- }
- }
- format!("#{:?}", id)
- }
-
- fn format_operand(&self, operand: Operand) -> String {
- if matches!(operand, Operand::Zero) {
- return String::from("0");
- }
- if let Some(counters) = &self.some_counters {
- if let Some(DebugCounter { counter_kind, some_block_label }) = counters.get(&operand) {
- if let BcbCounter::Expression { .. } = counter_kind {
- if let Some(label) = some_block_label && debug_options().counter_format.block {
- return format!(
- "{}:({})",
- label,
- self.format_counter_kind(counter_kind)
- );
- }
- return format!("({})", self.format_counter_kind(counter_kind));
- }
- return self.format_counter_kind(counter_kind);
- }
- }
- format!("#{:?}", operand)
- }
-}
-
-/// A non-public support class to `DebugCounters`.
-#[derive(Debug)]
-struct DebugCounter {
- counter_kind: BcbCounter,
- some_block_label: Option<String>,
-}
-
-impl DebugCounter {
- fn new(counter_kind: BcbCounter, some_block_label: Option<String>) -> Self {
- Self { counter_kind, some_block_label }
- }
-}
-
-/// If enabled, this data structure captures additional debugging information used when generating
-/// a Graphviz (.dot file) representation of the `CoverageGraph`, for debugging purposes.
-pub(super) struct GraphvizData {
- some_bcb_to_coverage_spans_with_counters:
- Option<FxHashMap<BasicCoverageBlock, Vec<(CoverageSpan, BcbCounter)>>>,
- some_bcb_to_dependency_counters: Option<FxHashMap<BasicCoverageBlock, Vec<BcbCounter>>>,
- some_edge_to_counter: Option<FxHashMap<(BasicCoverageBlock, BasicBlock), BcbCounter>>,
-}
-
-impl GraphvizData {
- pub fn new() -> Self {
- Self {
- some_bcb_to_coverage_spans_with_counters: None,
- some_bcb_to_dependency_counters: None,
- some_edge_to_counter: None,
- }
- }
-
- pub fn enable(&mut self) {
- debug_assert!(!self.is_enabled());
- self.some_bcb_to_coverage_spans_with_counters = Some(FxHashMap::default());
- self.some_bcb_to_dependency_counters = Some(FxHashMap::default());
- self.some_edge_to_counter = Some(FxHashMap::default());
- }
-
- pub fn is_enabled(&self) -> bool {
- self.some_bcb_to_coverage_spans_with_counters.is_some()
- }
-
- pub fn add_bcb_coverage_span_with_counter(
- &mut self,
- bcb: BasicCoverageBlock,
- coverage_span: &CoverageSpan,
- counter_kind: &BcbCounter,
- ) {
- if let Some(bcb_to_coverage_spans_with_counters) =
- self.some_bcb_to_coverage_spans_with_counters.as_mut()
- {
- bcb_to_coverage_spans_with_counters
- .entry(bcb)
- .or_insert_with(Vec::new)
- .push((coverage_span.clone(), counter_kind.clone()));
- }
- }
-
- pub fn get_bcb_coverage_spans_with_counters(
- &self,
- bcb: BasicCoverageBlock,
- ) -> Option<&[(CoverageSpan, BcbCounter)]> {
- if let Some(bcb_to_coverage_spans_with_counters) =
- self.some_bcb_to_coverage_spans_with_counters.as_ref()
- {
- bcb_to_coverage_spans_with_counters.get(&bcb).map(Deref::deref)
- } else {
- None
- }
- }
-
- pub fn add_bcb_dependency_counter(
- &mut self,
- bcb: BasicCoverageBlock,
- counter_kind: &BcbCounter,
- ) {
- if let Some(bcb_to_dependency_counters) = self.some_bcb_to_dependency_counters.as_mut() {
- bcb_to_dependency_counters
- .entry(bcb)
- .or_insert_with(Vec::new)
- .push(counter_kind.clone());
- }
- }
-
- pub fn get_bcb_dependency_counters(&self, bcb: BasicCoverageBlock) -> Option<&[BcbCounter]> {
- if let Some(bcb_to_dependency_counters) = self.some_bcb_to_dependency_counters.as_ref() {
- bcb_to_dependency_counters.get(&bcb).map(Deref::deref)
- } else {
- None
- }
- }
-
- pub fn set_edge_counter(
- &mut self,
- from_bcb: BasicCoverageBlock,
- to_bb: BasicBlock,
- counter_kind: &BcbCounter,
- ) {
- if let Some(edge_to_counter) = self.some_edge_to_counter.as_mut() {
- edge_to_counter
- .try_insert((from_bcb, to_bb), counter_kind.clone())
- .expect("invalid attempt to insert more than one edge counter for the same edge");
- }
- }
-
- pub fn get_edge_counter(
- &self,
- from_bcb: BasicCoverageBlock,
- to_bb: BasicBlock,
- ) -> Option<&BcbCounter> {
- if let Some(edge_to_counter) = self.some_edge_to_counter.as_ref() {
- edge_to_counter.get(&(from_bcb, to_bb))
- } else {
- None
- }
- }
-}
-
-/// If enabled, this struct captures additional data used to track whether expressions were used,
-/// directly or indirectly, to compute the coverage counts for all `CoverageSpan`s, and any that are
-/// _not_ used are retained in the `unused_expressions` Vec, to be included in debug output (logs
-/// and/or a `CoverageGraph` graphviz output).
-pub(super) struct UsedExpressions {
- some_used_expression_operands: Option<FxHashMap<Operand, Vec<ExpressionId>>>,
- some_unused_expressions:
- Option<Vec<(BcbCounter, Option<BasicCoverageBlock>, BasicCoverageBlock)>>,
-}
-
-impl UsedExpressions {
- pub fn new() -> Self {
- Self { some_used_expression_operands: None, some_unused_expressions: None }
- }
-
- pub fn enable(&mut self) {
- debug_assert!(!self.is_enabled());
- self.some_used_expression_operands = Some(FxHashMap::default());
- self.some_unused_expressions = Some(Vec::new());
- }
-
- pub fn is_enabled(&self) -> bool {
- self.some_used_expression_operands.is_some()
- }
-
- pub fn add_expression_operands(&mut self, expression: &BcbCounter) {
- if let Some(used_expression_operands) = self.some_used_expression_operands.as_mut() {
- if let BcbCounter::Expression { id, lhs, rhs, .. } = *expression {
- used_expression_operands.entry(lhs).or_insert_with(Vec::new).push(id);
- used_expression_operands.entry(rhs).or_insert_with(Vec::new).push(id);
- }
- }
- }
-
- pub fn expression_is_used(&self, expression: &BcbCounter) -> bool {
- if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() {
- used_expression_operands.contains_key(&expression.as_operand())
- } else {
- false
- }
- }
-
- pub fn add_unused_expression_if_not_found(
- &mut self,
- expression: &BcbCounter,
- edge_from_bcb: Option<BasicCoverageBlock>,
- target_bcb: BasicCoverageBlock,
- ) {
- if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() {
- if !used_expression_operands.contains_key(&expression.as_operand()) {
- self.some_unused_expressions.as_mut().unwrap().push((
- expression.clone(),
- edge_from_bcb,
- target_bcb,
- ));
- }
- }
- }
-
- /// Return the list of unused counters (if any) as a tuple with the counter (`BcbCounter`),
- /// optional `from_bcb` (if it was an edge counter), and `target_bcb`.
- pub fn get_unused_expressions(
- &self,
- ) -> Vec<(BcbCounter, Option<BasicCoverageBlock>, BasicCoverageBlock)> {
- if let Some(unused_expressions) = self.some_unused_expressions.as_ref() {
- unused_expressions.clone()
- } else {
- Vec::new()
- }
- }
-
- /// If enabled, validate that every BCB or edge counter not directly associated with a coverage
- /// span is at least indirectly associated (it is a dependency of a BCB counter that _is_
- /// associated with a coverage span).
- pub fn validate(
- &mut self,
- bcb_counters_without_direct_coverage_spans: &[(
- Option<BasicCoverageBlock>,
- BasicCoverageBlock,
- BcbCounter,
- )],
- ) {
- if self.is_enabled() {
- let mut not_validated = bcb_counters_without_direct_coverage_spans
- .iter()
- .map(|(_, _, counter_kind)| counter_kind)
- .collect::<Vec<_>>();
- let mut validating_count = 0;
- while not_validated.len() != validating_count {
- let to_validate = not_validated.split_off(0);
- validating_count = to_validate.len();
- for counter_kind in to_validate {
- if self.expression_is_used(counter_kind) {
- self.add_expression_operands(counter_kind);
- } else {
- not_validated.push(counter_kind);
- }
- }
- }
- }
- }
-
- pub fn alert_on_unused_expressions(&self, debug_counters: &DebugCounters) {
- if let Some(unused_expressions) = self.some_unused_expressions.as_ref() {
- for (counter_kind, edge_from_bcb, target_bcb) in unused_expressions {
- let unused_counter_message = if let Some(from_bcb) = edge_from_bcb.as_ref() {
- format!(
- "non-coverage edge counter found without a dependent expression, in \
- {:?}->{:?}; counter={}",
- from_bcb,
- target_bcb,
- debug_counters.format_counter(&counter_kind),
- )
- } else {
- format!(
- "non-coverage counter found without a dependent expression, in {:?}; \
- counter={}",
- target_bcb,
- debug_counters.format_counter(&counter_kind),
- )
- };
-
- if debug_options().allow_unused_expressions {
- debug!("WARNING: {}", unused_counter_message);
- } else {
- bug!("{}", unused_counter_message);
- }
- }
- }
- }
-}
-
-/// Generates the MIR pass `CoverageSpan`-specific spanview dump file.
-pub(super) fn dump_coverage_spanview<'tcx>(
- tcx: TyCtxt<'tcx>,
- mir_body: &mir::Body<'tcx>,
- basic_coverage_blocks: &CoverageGraph,
- pass_name: &str,
- body_span: Span,
- coverage_spans: &[CoverageSpan],
-) {
- let mir_source = mir_body.source;
- let def_id = mir_source.def_id();
-
- let span_viewables = span_viewables(tcx, mir_body, basic_coverage_blocks, &coverage_spans);
- let mut file = create_dump_file(tcx, "html", false, pass_name, &0i32, mir_body)
- .expect("Unexpected error creating MIR spanview HTML file");
- let crate_name = tcx.crate_name(def_id.krate);
- let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate();
- let title = format!("{crate_name}.{item_name} - Coverage Spans");
- spanview::write_document(tcx, body_span, span_viewables, &title, &mut file)
- .expect("Unexpected IO error dumping coverage spans as HTML");
-}
-
-/// Converts the computed `BasicCoverageBlockData`s into `SpanViewable`s.
-fn span_viewables<'tcx>(
- tcx: TyCtxt<'tcx>,
- mir_body: &mir::Body<'tcx>,
- basic_coverage_blocks: &CoverageGraph,
- coverage_spans: &[CoverageSpan],
-) -> Vec<SpanViewable> {
- let mut span_viewables = Vec::new();
- for coverage_span in coverage_spans {
- let tooltip = coverage_span.format_coverage_statements(tcx, mir_body);
- let CoverageSpan { span, bcb, .. } = coverage_span;
- let bcb_data = &basic_coverage_blocks[*bcb];
- let id = bcb_data.id();
- let leader_bb = bcb_data.leader_bb();
- span_viewables.push(SpanViewable { bb: leader_bb, span: *span, id, tooltip });
- }
- span_viewables
-}
-
-/// Generates the MIR pass coverage-specific graphviz dump file.
-pub(super) fn dump_coverage_graphviz<'tcx>(
- tcx: TyCtxt<'tcx>,
- mir_body: &mir::Body<'tcx>,
- pass_name: &str,
- basic_coverage_blocks: &CoverageGraph,
- coverage_counters: &CoverageCounters,
- graphviz_data: &GraphvizData,
- intermediate_expressions: &[BcbCounter],
- debug_used_expressions: &UsedExpressions,
-) {
- let debug_counters = &coverage_counters.debug_counters;
-
- let mir_source = mir_body.source;
- let def_id = mir_source.def_id();
- let node_content = |bcb| {
- bcb_to_string_sections(
- tcx,
- mir_body,
- coverage_counters,
- bcb,
- &basic_coverage_blocks[bcb],
- graphviz_data.get_bcb_coverage_spans_with_counters(bcb),
- graphviz_data.get_bcb_dependency_counters(bcb),
- // intermediate_expressions are injected into the mir::START_BLOCK, so
- // include them in the first BCB.
- if bcb.index() == 0 { Some(&intermediate_expressions) } else { None },
- )
- };
- let edge_labels = |from_bcb| {
- let from_bcb_data = &basic_coverage_blocks[from_bcb];
- let from_terminator = from_bcb_data.terminator(mir_body);
- let mut edge_labels = from_terminator.kind.fmt_successor_labels();
- edge_labels.retain(|label| label != "unreachable");
- let edge_counters = from_terminator
- .successors()
- .map(|successor_bb| graphviz_data.get_edge_counter(from_bcb, successor_bb));
- iter::zip(&edge_labels, edge_counters)
- .map(|(label, some_counter)| {
- if let Some(counter) = some_counter {
- format!("{}\n{}", label, debug_counters.format_counter(counter))
- } else {
- label.to_string()
- }
- })
- .collect::<Vec<_>>()
- };
- let graphviz_name = format!("Cov_{}_{}", def_id.krate.index(), def_id.index.index());
- let mut graphviz_writer =
- GraphvizWriter::new(basic_coverage_blocks, &graphviz_name, node_content, edge_labels);
- let unused_expressions = debug_used_expressions.get_unused_expressions();
- if unused_expressions.len() > 0 {
- graphviz_writer.set_graph_label(&format!(
- "Unused expressions:\n {}",
- unused_expressions
- .as_slice()
- .iter()
- .map(|(counter_kind, edge_from_bcb, target_bcb)| {
- if let Some(from_bcb) = edge_from_bcb.as_ref() {
- format!(
- "{:?}->{:?}: {}",
- from_bcb,
- target_bcb,
- debug_counters.format_counter(&counter_kind),
- )
- } else {
- format!(
- "{:?}: {}",
- target_bcb,
- debug_counters.format_counter(&counter_kind),
- )
- }
- })
- .join("\n ")
- ));
- }
- let mut file = create_dump_file(tcx, "dot", false, pass_name, &0i32, mir_body)
- .expect("Unexpected error creating BasicCoverageBlock graphviz DOT file");
- graphviz_writer
- .write_graphviz(tcx, &mut file)
- .expect("Unexpected error writing BasicCoverageBlock graphviz DOT file");
-}
-
-fn bcb_to_string_sections<'tcx>(
- tcx: TyCtxt<'tcx>,
- mir_body: &mir::Body<'tcx>,
- coverage_counters: &CoverageCounters,
- bcb: BasicCoverageBlock,
- bcb_data: &BasicCoverageBlockData,
- some_coverage_spans_with_counters: Option<&[(CoverageSpan, BcbCounter)]>,
- some_dependency_counters: Option<&[BcbCounter]>,
- some_intermediate_expressions: Option<&[BcbCounter]>,
-) -> Vec<String> {
- let debug_counters = &coverage_counters.debug_counters;
-
- let len = bcb_data.basic_blocks.len();
- let mut sections = Vec::new();
- if let Some(collect_intermediate_expressions) = some_intermediate_expressions {
- sections.push(
- collect_intermediate_expressions
- .iter()
- .map(|expression| {
- format!("Intermediate {}", debug_counters.format_counter(expression))
- })
- .join("\n"),
- );
- }
- if let Some(coverage_spans_with_counters) = some_coverage_spans_with_counters {
- sections.push(
- coverage_spans_with_counters
- .iter()
- .map(|(covspan, counter)| {
- format!(
- "{} at {}",
- debug_counters.format_counter(counter),
- covspan.format(tcx, mir_body)
- )
- })
- .join("\n"),
- );
- }
- if let Some(dependency_counters) = some_dependency_counters {
- sections.push(format!(
- "Non-coverage counters:\n {}",
- dependency_counters
- .iter()
- .map(|counter| debug_counters.format_counter(counter))
- .join(" \n"),
- ));
- }
- if let Some(counter_kind) = coverage_counters.bcb_counter(bcb) {
- sections.push(format!("{counter_kind:?}"));
- }
- let non_term_blocks = bcb_data.basic_blocks[0..len - 1]
- .iter()
- .map(|&bb| format!("{:?}: {}", bb, mir_body[bb].terminator().kind.name()))
- .collect::<Vec<_>>();
- if non_term_blocks.len() > 0 {
- sections.push(non_term_blocks.join("\n"));
- }
- sections.push(format!(
- "{:?}: {}",
- bcb_data.basic_blocks.last().unwrap(),
- bcb_data.terminator(mir_body).kind.name(),
- ));
- sections
-}
diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs
index 59b01ffec..ff2254d69 100644
--- a/compiler/rustc_mir_transform/src/coverage/graph.rs
+++ b/compiler/rustc_mir_transform/src/coverage/graph.rs
@@ -1,4 +1,3 @@
-use itertools::Itertools;
use rustc_data_structures::graph::dominators::{self, Dominators};
use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode};
use rustc_index::bit_set::BitSet;
@@ -8,8 +7,6 @@ use rustc_middle::mir::{self, BasicBlock, BasicBlockData, Terminator, Terminator
use std::cmp::Ordering;
use std::ops::{Index, IndexMut};
-const ID_SEPARATOR: &str = ",";
-
/// A coverage-specific simplification of the MIR control flow graph (CFG). The `CoverageGraph`s
/// nodes are `BasicCoverageBlock`s, which encompass one or more MIR `BasicBlock`s.
#[derive(Debug)]
@@ -116,7 +113,7 @@ impl CoverageGraph {
match term.kind {
TerminatorKind::Return { .. }
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Yield { .. }
| TerminatorKind::SwitchInt { .. } => {
// The `bb` has more than one _outgoing_ edge, or exits the function. Save the
@@ -146,7 +143,7 @@ impl CoverageGraph {
// is as intended. (See Issue #78544 for a possible future option to support
// coverage in test programs that panic.)
TerminatorKind::Goto { .. }
- | TerminatorKind::Resume
+ | TerminatorKind::UnwindResume
| TerminatorKind::Unreachable
| TerminatorKind::Drop { .. }
| TerminatorKind::Call { .. }
@@ -199,12 +196,8 @@ impl CoverageGraph {
}
#[inline(always)]
- pub fn rank_partial_cmp(
- &self,
- a: BasicCoverageBlock,
- b: BasicCoverageBlock,
- ) -> Option<Ordering> {
- self.dominators.as_ref().unwrap().rank_partial_cmp(a, b)
+ pub fn cmp_in_dominator_order(&self, a: BasicCoverageBlock, b: BasicCoverageBlock) -> Ordering {
+ self.dominators.as_ref().unwrap().cmp_in_dominator_order(a, b)
}
}
@@ -328,10 +321,6 @@ impl BasicCoverageBlockData {
pub fn terminator<'a, 'tcx>(&self, mir_body: &'a mir::Body<'tcx>) -> &'a Terminator<'tcx> {
&mir_body[self.last_bb()].terminator()
}
-
- pub fn id(&self) -> String {
- format!("@{}", self.basic_blocks.iter().map(|bb| bb.index().to_string()).join(ID_SEPARATOR))
- }
}
/// Represents a successor from a branching BasicCoverageBlock (such as the arms of a `SwitchInt`)
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index 8c9eae508..c75d33eeb 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -1,7 +1,6 @@
pub mod query;
mod counters;
-mod debug;
mod graph;
mod spans;
@@ -20,7 +19,6 @@ use rustc_index::IndexVec;
use rustc_middle::hir;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::coverage::*;
-use rustc_middle::mir::dump_enabled;
use rustc_middle::mir::{
self, BasicBlock, BasicBlockData, Coverage, SourceInfo, Statement, StatementKind, Terminator,
TerminatorKind,
@@ -28,7 +26,7 @@ use rustc_middle::mir::{
use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::DefId;
use rustc_span::source_map::SourceMap;
-use rustc_span::{CharPos, ExpnKind, Pos, SourceFile, Span, Symbol};
+use rustc_span::{ExpnKind, SourceFile, Span, Symbol};
/// A simple error message wrapper for `coverage::Error`s.
#[derive(Debug)]
@@ -94,13 +92,12 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
}
trace!("InstrumentCoverage starting for {:?}", mir_source.def_id());
- Instrumentor::new(&self.name(), tcx, mir_body).inject_counters();
+ Instrumentor::new(tcx, mir_body).inject_counters();
trace!("InstrumentCoverage done for {:?}", mir_source.def_id());
}
}
struct Instrumentor<'a, 'tcx> {
- pass_name: &'a str,
tcx: TyCtxt<'tcx>,
mir_body: &'a mut mir::Body<'tcx>,
source_file: Lrc<SourceFile>,
@@ -112,7 +109,7 @@ struct Instrumentor<'a, 'tcx> {
}
impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
- fn new(pass_name: &'a str, tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self {
+ fn new(tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self {
let source_map = tcx.sess.source_map();
let def_id = mir_body.source.def_id();
let (some_fn_sig, hir_body) = fn_sig_and_body(tcx, def_id);
@@ -141,7 +138,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
let coverage_counters = CoverageCounters::new(&basic_coverage_blocks);
Self {
- pass_name,
tcx,
mir_body,
source_file,
@@ -154,28 +150,9 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
}
fn inject_counters(&'a mut self) {
- let tcx = self.tcx;
- let mir_source = self.mir_body.source;
- let def_id = mir_source.def_id();
let fn_sig_span = self.fn_sig_span;
let body_span = self.body_span;
- let mut graphviz_data = debug::GraphvizData::new();
- let mut debug_used_expressions = debug::UsedExpressions::new();
-
- let dump_mir = dump_enabled(tcx, self.pass_name, def_id);
- let dump_graphviz = dump_mir && tcx.sess.opts.unstable_opts.dump_mir_graphviz;
- let dump_spanview = dump_mir && tcx.sess.opts.unstable_opts.dump_mir_spanview.is_some();
-
- if dump_graphviz {
- graphviz_data.enable();
- self.coverage_counters.enable_debug();
- }
-
- if dump_graphviz || level_enabled!(tracing::Level::DEBUG) {
- debug_used_expressions.enable();
- }
-
////////////////////////////////////////////////////
// Compute `CoverageSpan`s from the `CoverageGraph`.
let coverage_spans = CoverageSpans::generate_coverage_spans(
@@ -185,17 +162,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
&self.basic_coverage_blocks,
);
- if dump_spanview {
- debug::dump_coverage_spanview(
- tcx,
- self.mir_body,
- &self.basic_coverage_blocks,
- self.pass_name,
- body_span,
- &coverage_spans,
- );
- }
-
////////////////////////////////////////////////////
// Create an optimized mix of `Counter`s and `Expression`s for the `CoverageGraph`. Ensure
// every `CoverageSpan` has a `Counter` or `Expression` assigned to its `BasicCoverageBlock`
@@ -209,14 +175,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
.make_bcb_counters(&mut self.basic_coverage_blocks, &coverage_spans);
if let Ok(()) = result {
- // If debugging, add any intermediate expressions (which are not associated with any
- // BCB) to the `debug_used_expressions` map.
- if debug_used_expressions.is_enabled() {
- for intermediate_expression in &self.coverage_counters.intermediate_expressions {
- debug_used_expressions.add_expression_operands(intermediate_expression);
- }
- }
-
////////////////////////////////////////////////////
// Remove the counter or edge counter from of each `CoverageSpan`s associated
// `BasicCoverageBlock`, and inject a `Coverage` statement into the MIR.
@@ -227,11 +185,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
// These `CoverageSpan`-associated counters are removed from their associated
// `BasicCoverageBlock`s so that the only remaining counters in the `CoverageGraph`
// are indirect counters (to be injected next, without associated code regions).
- self.inject_coverage_span_counters(
- coverage_spans,
- &mut graphviz_data,
- &mut debug_used_expressions,
- );
+ self.inject_coverage_span_counters(coverage_spans);
////////////////////////////////////////////////////
// For any remaining `BasicCoverageBlock` counters (that were not associated with
@@ -239,37 +193,17 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
// to ensure `BasicCoverageBlock` counters that other `Expression`s may depend on
// are in fact counted, even though they don't directly contribute to counting
// their own independent code region's coverage.
- self.inject_indirect_counters(&mut graphviz_data, &mut debug_used_expressions);
+ self.inject_indirect_counters();
// Intermediate expressions will be injected as the final step, after generating
// debug output, if any.
////////////////////////////////////////////////////
};
- if graphviz_data.is_enabled() {
- // Even if there was an error, a partial CoverageGraph can still generate a useful
- // graphviz output.
- debug::dump_coverage_graphviz(
- tcx,
- self.mir_body,
- self.pass_name,
- &self.basic_coverage_blocks,
- &self.coverage_counters,
- &graphviz_data,
- &self.coverage_counters.intermediate_expressions,
- &debug_used_expressions,
- );
- }
-
if let Err(e) = result {
bug!("Error processing: {:?}: {:?}", self.mir_body.source.def_id(), e.message)
};
- // Depending on current `debug_options()`, `alert_on_unused_expressions()` could panic, so
- // this check is performed as late as possible, to allow other debug output (logs and dump
- // files), which might be helpful in analyzing unused expressions, to still be generated.
- debug_used_expressions.alert_on_unused_expressions(&self.coverage_counters.debug_counters);
-
////////////////////////////////////////////////////
// Finally, inject the intermediate expressions collected along the way.
for intermediate_expression in &self.coverage_counters.intermediate_expressions {
@@ -285,15 +219,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
/// `bcb` to its `Counter`, when injected. Subsequent `CoverageSpan`s for a BCB that already has
/// a `Counter` will inject an `Expression` instead, and compute its value by adding `ZERO` to
/// the BCB `Counter` value.
- ///
- /// If debugging, add every BCB `Expression` associated with a `CoverageSpan`s to the
- /// `used_expression_operands` map.
- fn inject_coverage_span_counters(
- &mut self,
- coverage_spans: Vec<CoverageSpan>,
- graphviz_data: &mut debug::GraphvizData,
- debug_used_expressions: &mut debug::UsedExpressions,
- ) {
+ fn inject_coverage_span_counters(&mut self, coverage_spans: Vec<CoverageSpan>) {
let tcx = self.tcx;
let source_map = tcx.sess.source_map();
let body_span = self.body_span;
@@ -307,15 +233,12 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
self.coverage_counters.make_identity_counter(counter_operand)
} else if let Some(counter_kind) = self.coverage_counters.take_bcb_counter(bcb) {
bcb_counters[bcb] = Some(counter_kind.as_operand());
- debug_used_expressions.add_expression_operands(&counter_kind);
counter_kind
} else {
bug!("Every BasicCoverageBlock should have a Counter or Expression");
};
- graphviz_data.add_bcb_coverage_span_with_counter(bcb, &covspan, &counter_kind);
- let code_region =
- make_code_region(source_map, file_name, &self.source_file, span, body_span);
+ let code_region = make_code_region(source_map, file_name, span, body_span);
inject_statement(
self.mir_body,
@@ -334,11 +257,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
/// associated with a `CoverageSpan`, should only exist if the counter is an `Expression`
/// dependency (one of the expression operands). Collect them, and inject the additional
/// counters into the MIR, without a reportable coverage span.
- fn inject_indirect_counters(
- &mut self,
- graphviz_data: &mut debug::GraphvizData,
- debug_used_expressions: &mut debug::UsedExpressions,
- ) {
+ fn inject_indirect_counters(&mut self) {
let mut bcb_counters_without_direct_coverage_spans = Vec::new();
for (target_bcb, counter_kind) in self.coverage_counters.drain_bcb_counters() {
bcb_counters_without_direct_coverage_spans.push((None, target_bcb, counter_kind));
@@ -353,19 +272,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
));
}
- // If debug is enabled, validate that every BCB or edge counter not directly associated
- // with a coverage span is at least indirectly associated (it is a dependency of a BCB
- // counter that _is_ associated with a coverage span).
- debug_used_expressions.validate(&bcb_counters_without_direct_coverage_spans);
-
for (edge_from_bcb, target_bcb, counter_kind) in bcb_counters_without_direct_coverage_spans
{
- debug_used_expressions.add_unused_expression_if_not_found(
- &counter_kind,
- edge_from_bcb,
- target_bcb,
- );
-
match counter_kind {
BcbCounter::Counter { .. } => {
let inject_to_bb = if let Some(from_bcb) = edge_from_bcb {
@@ -376,26 +284,17 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
let to_bb = self.bcb_leader_bb(target_bcb);
let new_bb = inject_edge_counter_basic_block(self.mir_body, from_bb, to_bb);
- graphviz_data.set_edge_counter(from_bcb, new_bb, &counter_kind);
debug!(
"Edge {:?} (last {:?}) -> {:?} (leader {:?}) requires a new MIR \
- BasicBlock {:?}, for unclaimed edge counter {}",
- edge_from_bcb,
- from_bb,
- target_bcb,
- to_bb,
- new_bb,
- self.format_counter(&counter_kind),
+ BasicBlock {:?}, for unclaimed edge counter {:?}",
+ edge_from_bcb, from_bb, target_bcb, to_bb, new_bb, counter_kind,
);
new_bb
} else {
let target_bb = self.bcb_last_bb(target_bcb);
- graphviz_data.add_bcb_dependency_counter(target_bcb, &counter_kind);
debug!(
- "{:?} ({:?}) gets a new Coverage statement for unclaimed counter {}",
- target_bcb,
- target_bb,
- self.format_counter(&counter_kind),
+ "{:?} ({:?}) gets a new Coverage statement for unclaimed counter {:?}",
+ target_bcb, target_bb, counter_kind,
);
target_bb
};
@@ -430,11 +329,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
&self.basic_coverage_blocks[bcb]
}
- #[inline]
- fn format_counter(&self, counter_kind: &BcbCounter) -> String {
- self.coverage_counters.debug_counters.format_counter(counter_kind)
- }
-
fn make_mir_coverage_kind(&self, counter_kind: &BcbCounter) -> CoverageKind {
match *counter_kind {
BcbCounter::Counter { id } => {
@@ -510,40 +404,36 @@ fn inject_intermediate_expression(mir_body: &mut mir::Body<'_>, expression: Cove
fn make_code_region(
source_map: &SourceMap,
file_name: Symbol,
- source_file: &Lrc<SourceFile>,
span: Span,
body_span: Span,
) -> CodeRegion {
debug!(
- "Called make_code_region(file_name={}, source_file={:?}, span={}, body_span={})",
+ "Called make_code_region(file_name={}, span={}, body_span={})",
file_name,
- source_file,
source_map.span_to_diagnostic_string(span),
source_map.span_to_diagnostic_string(body_span)
);
- let (start_line, mut start_col) = source_file.lookup_file_pos(span.lo());
- let (end_line, end_col) = if span.hi() == span.lo() {
- let (end_line, mut end_col) = (start_line, start_col);
+ let (file, mut start_line, mut start_col, mut end_line, mut end_col) =
+ source_map.span_to_location_info(span);
+ if span.hi() == span.lo() {
// Extend an empty span by one character so the region will be counted.
- let CharPos(char_pos) = start_col;
if span.hi() == body_span.hi() {
- start_col = CharPos(char_pos.saturating_sub(1));
+ start_col = start_col.saturating_sub(1);
} else {
- end_col = CharPos(char_pos + 1);
+ end_col = start_col + 1;
}
- (end_line, end_col)
- } else {
- source_file.lookup_file_pos(span.hi())
};
- let start_line = source_map.doctest_offset_line(&source_file.name, start_line);
- let end_line = source_map.doctest_offset_line(&source_file.name, end_line);
+ if let Some(file) = file {
+ start_line = source_map.doctest_offset_line(&file.name, start_line);
+ end_line = source_map.doctest_offset_line(&file.name, end_line);
+ }
CodeRegion {
file_name,
start_line: start_line as u32,
- start_col: start_col.to_u32() + 1,
+ start_col: start_col as u32,
end_line: end_line as u32,
- end_col: end_col.to_u32() + 1,
+ end_col: end_col as u32,
}
}
diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs
index aa205655f..56365c5d4 100644
--- a/compiler/rustc_mir_transform/src/coverage/query.rs
+++ b/compiler/rustc_mir_transform/src/coverage/query.rs
@@ -1,5 +1,6 @@
use super::*;
+use rustc_data_structures::captures::Captures;
use rustc_middle::mir::coverage::*;
use rustc_middle::mir::{self, Body, Coverage, CoverageInfo};
use rustc_middle::query::Providers;
@@ -12,15 +13,10 @@ pub(crate) fn provide(providers: &mut Providers) {
providers.covered_code_regions = |tcx, def_id| covered_code_regions(tcx, def_id);
}
-/// The `num_counters` argument to `llvm.instrprof.increment` is the max counter_id + 1, or in
-/// other words, the number of counter value references injected into the MIR (plus 1 for the
-/// reserved `ZERO` counter, which uses counter ID `0` when included in an expression). Injected
-/// counters have a counter ID from `1..num_counters-1`.
-///
-/// `num_expressions` is the number of counter expressions added to the MIR body.
-///
-/// Both `num_counters` and `num_expressions` are used to initialize new vectors, during backend
-/// code generate, to lookup counters and expressions by simple u32 indexes.
+/// Coverage codegen needs to know the total number of counter IDs and expression IDs that have
+/// been used by a function's coverage mappings. These totals are used to create vectors to hold
+/// the relevant counter and expression data, and the maximum counter ID (+ 1) is also needed by
+/// the `llvm.instrprof.increment` intrinsic.
///
/// MIR optimization may split and duplicate some BasicBlock sequences, or optimize out some code
/// including injected counters. (It is OK if some counters are optimized out, but those counters
@@ -28,71 +24,51 @@ pub(crate) fn provide(providers: &mut Providers) {
/// calls may not work; but computing the number of counters or expressions by adding `1` to the
/// highest ID (for a given instrumented function) is valid.
///
-/// This visitor runs twice, first with `add_missing_operands` set to `false`, to find the maximum
-/// counter ID and maximum expression ID based on their enum variant `id` fields; then, as a
-/// safeguard, with `add_missing_operands` set to `true`, to find any other counter or expression
-/// IDs referenced by expression operands, if not already seen.
-///
-/// Ideally, each operand ID in a MIR `CoverageKind::Expression` will have a separate MIR `Coverage`
-/// statement for the `Counter` or `Expression` with the referenced ID. but since current or future
-/// MIR optimizations can theoretically optimize out segments of a MIR, it may not be possible to
-/// guarantee this, so the second pass ensures the `CoverageInfo` counts include all referenced IDs.
+/// It's possible for a coverage expression to remain in MIR while one or both of its operands
+/// have been optimized away. To avoid problems in codegen, we include those operands' IDs when
+/// determining the maximum counter/expression ID, even if the underlying counter/expression is
+/// no longer present.
struct CoverageVisitor {
- info: CoverageInfo,
- add_missing_operands: bool,
+ max_counter_id: CounterId,
+ max_expression_id: ExpressionId,
}
impl CoverageVisitor {
- /// Updates `num_counters` to the maximum encountered counter ID plus 1.
+ /// Updates `max_counter_id` to the maximum encountered counter ID.
#[inline(always)]
- fn update_num_counters(&mut self, counter_id: CounterId) {
- let counter_id = counter_id.as_u32();
- self.info.num_counters = std::cmp::max(self.info.num_counters, counter_id + 1);
+ fn update_max_counter_id(&mut self, counter_id: CounterId) {
+ self.max_counter_id = self.max_counter_id.max(counter_id);
}
- /// Updates `num_expressions` to the maximum encountered expression ID plus 1.
+ /// Updates `max_expression_id` to the maximum encountered expression ID.
#[inline(always)]
- fn update_num_expressions(&mut self, expression_id: ExpressionId) {
- let expression_id = expression_id.as_u32();
- self.info.num_expressions = std::cmp::max(self.info.num_expressions, expression_id + 1);
+ fn update_max_expression_id(&mut self, expression_id: ExpressionId) {
+ self.max_expression_id = self.max_expression_id.max(expression_id);
}
fn update_from_expression_operand(&mut self, operand: Operand) {
match operand {
- Operand::Counter(id) => self.update_num_counters(id),
- Operand::Expression(id) => self.update_num_expressions(id),
+ Operand::Counter(id) => self.update_max_counter_id(id),
+ Operand::Expression(id) => self.update_max_expression_id(id),
Operand::Zero => {}
}
}
fn visit_body(&mut self, body: &Body<'_>) {
- for bb_data in body.basic_blocks.iter() {
- for statement in bb_data.statements.iter() {
- if let StatementKind::Coverage(box ref coverage) = statement.kind {
- if is_inlined(body, statement) {
- continue;
- }
- self.visit_coverage(coverage);
- }
- }
+ for coverage in all_coverage_in_mir_body(body) {
+ self.visit_coverage(coverage);
}
}
fn visit_coverage(&mut self, coverage: &Coverage) {
- if self.add_missing_operands {
- match coverage.kind {
- CoverageKind::Expression { lhs, rhs, .. } => {
- self.update_from_expression_operand(lhs);
- self.update_from_expression_operand(rhs);
- }
- _ => {}
- }
- } else {
- match coverage.kind {
- CoverageKind::Counter { id, .. } => self.update_num_counters(id),
- CoverageKind::Expression { id, .. } => self.update_num_expressions(id),
- _ => {}
+ match coverage.kind {
+ CoverageKind::Counter { id, .. } => self.update_max_counter_id(id),
+ CoverageKind::Expression { id, lhs, rhs, .. } => {
+ self.update_max_expression_id(id);
+ self.update_from_expression_operand(lhs);
+ self.update_from_expression_operand(rhs);
}
+ CoverageKind::Unreachable => {}
}
}
}
@@ -101,37 +77,40 @@ fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, instance_def: ty::InstanceDef<'tcx>) ->
let mir_body = tcx.instance_mir(instance_def);
let mut coverage_visitor = CoverageVisitor {
- info: CoverageInfo { num_counters: 0, num_expressions: 0 },
- add_missing_operands: false,
+ max_counter_id: CounterId::START,
+ max_expression_id: ExpressionId::START,
};
coverage_visitor.visit_body(mir_body);
- coverage_visitor.add_missing_operands = true;
- coverage_visitor.visit_body(mir_body);
-
- coverage_visitor.info
+ // Add 1 to the highest IDs to get the total number of IDs.
+ CoverageInfo {
+ num_counters: (coverage_visitor.max_counter_id + 1).as_u32(),
+ num_expressions: (coverage_visitor.max_expression_id + 1).as_u32(),
+ }
}
fn covered_code_regions(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<&CodeRegion> {
let body = mir_body(tcx, def_id);
- body.basic_blocks
- .iter()
- .flat_map(|data| {
- data.statements.iter().filter_map(|statement| match statement.kind {
- StatementKind::Coverage(box ref coverage) => {
- if is_inlined(body, statement) {
- None
- } else {
- coverage.code_region.as_ref() // may be None
- }
- }
- _ => None,
- })
- })
+ all_coverage_in_mir_body(body)
+ // Not all coverage statements have an attached code region.
+ .filter_map(|coverage| coverage.code_region.as_ref())
.collect()
}
+fn all_coverage_in_mir_body<'a, 'tcx>(
+ body: &'a Body<'tcx>,
+) -> impl Iterator<Item = &'a Coverage> + Captures<'tcx> {
+ body.basic_blocks.iter().flat_map(|bb_data| &bb_data.statements).filter_map(|statement| {
+ match statement.kind {
+ StatementKind::Coverage(box ref coverage) if !is_inlined(body, statement) => {
+ Some(coverage)
+ }
+ _ => None,
+ }
+ })
+}
+
fn is_inlined(body: &Body<'_>, statement: &Statement<'_>) -> bool {
let scope_data = &body.source_scopes[statement.source_info.scope];
scope_data.inlined.is_some() || scope_data.inlined_parent_scope.is_some()
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs
index deebf5345..ed0e104d6 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -1,18 +1,14 @@
use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB};
-use itertools::Itertools;
use rustc_data_structures::graph::WithNumNodes;
-use rustc_middle::mir::spanview::source_range_no_file;
use rustc_middle::mir::{
self, AggregateKind, BasicBlock, FakeReadCause, Rvalue, Statement, StatementKind, Terminator,
TerminatorKind,
};
-use rustc_middle::ty::TyCtxt;
use rustc_span::source_map::original_sp;
use rustc_span::{BytePos, ExpnKind, MacroKind, Span, Symbol};
use std::cell::OnceCell;
-use std::cmp::Ordering;
#[derive(Debug, Copy, Clone)]
pub(super) enum CoverageStatement {
@@ -21,31 +17,6 @@ pub(super) enum CoverageStatement {
}
impl CoverageStatement {
- pub fn format<'tcx>(&self, tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>) -> String {
- match *self {
- Self::Statement(bb, span, stmt_index) => {
- let stmt = &mir_body[bb].statements[stmt_index];
- format!(
- "{}: @{}[{}]: {:?}",
- source_range_no_file(tcx, span),
- bb.index(),
- stmt_index,
- stmt
- )
- }
- Self::Terminator(bb, span) => {
- let term = mir_body[bb].terminator();
- format!(
- "{}: @{}.{}: {:?}",
- source_range_no_file(tcx, span),
- bb.index(),
- term.kind.name(),
- term.kind
- )
- }
- }
- }
-
pub fn span(&self) -> Span {
match self {
Self::Statement(_, span, _) | Self::Terminator(_, span) => *span,
@@ -151,27 +122,6 @@ impl CoverageSpan {
self.bcb == other.bcb
}
- pub fn format<'tcx>(&self, tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>) -> String {
- format!(
- "{}\n {}",
- source_range_no_file(tcx, self.span),
- self.format_coverage_statements(tcx, mir_body).replace('\n', "\n "),
- )
- }
-
- pub fn format_coverage_statements<'tcx>(
- &self,
- tcx: TyCtxt<'tcx>,
- mir_body: &mir::Body<'tcx>,
- ) -> String {
- let mut sorted_coverage_statements = self.coverage_statements.clone();
- sorted_coverage_statements.sort_unstable_by_key(|covstmt| match *covstmt {
- CoverageStatement::Statement(bb, _, index) => (bb, index),
- CoverageStatement::Terminator(bb, _) => (bb, usize::MAX),
- });
- sorted_coverage_statements.iter().map(|covstmt| covstmt.format(tcx, mir_body)).join("\n")
- }
-
/// If the span is part of a macro, returns the macro name symbol.
pub fn current_macro(&self) -> Option<Symbol> {
self.current_macro_or_none
@@ -333,30 +283,21 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
initial_spans.push(CoverageSpan::for_fn_sig(self.fn_sig_span));
- initial_spans.sort_unstable_by(|a, b| {
- if a.span.lo() == b.span.lo() {
- if a.span.hi() == b.span.hi() {
- if a.is_in_same_bcb(b) {
- Some(Ordering::Equal)
- } else {
- // Sort equal spans by dominator relationship (so dominators always come
- // before the dominated equal spans). When later comparing two spans in
- // order, the first will either dominate the second, or they will have no
- // dominator relationship.
- self.basic_coverage_blocks.rank_partial_cmp(a.bcb, b.bcb)
- }
- } else {
- // Sort hi() in reverse order so shorter spans are attempted after longer spans.
- // This guarantees that, if a `prev` span overlaps, and is not equal to, a
- // `curr` span, the prev span either extends further left of the curr span, or
- // they start at the same position and the prev span extends further right of
- // the end of the curr span.
- b.span.hi().partial_cmp(&a.span.hi())
- }
- } else {
- a.span.lo().partial_cmp(&b.span.lo())
- }
- .unwrap()
+ initial_spans.sort_by(|a, b| {
+ // First sort by span start.
+ Ord::cmp(&a.span.lo(), &b.span.lo())
+ // If span starts are the same, sort by span end in reverse order.
+ // This ensures that if spans A and B are adjacent in the list,
+ // and they overlap but are not equal, then either:
+ // - Span A extends further left, or
+ // - Both have the same start and span A extends further right
+ .then_with(|| Ord::cmp(&a.span.hi(), &b.span.hi()).reverse())
+ // If both spans are equal, sort the BCBs in dominator order,
+ // so that dominating BCBs come before other BCBs they dominate.
+ .then_with(|| self.basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb))
+ // If two spans are otherwise identical, put closure spans first,
+ // as this seems to be what the refinement step expects.
+ .then_with(|| Ord::cmp(&a.is_closure, &b.is_closure).reverse())
});
initial_spans
@@ -486,6 +427,12 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
let merged_prefix_len = self.curr_original_span.lo() - self.curr().span.lo();
let after_macro_bang =
merged_prefix_len + BytePos(visible_macro.as_str().len() as u32 + 1);
+ if self.curr().span.lo() + after_macro_bang > self.curr().span.hi() {
+ // Something is wrong with the macro name span;
+ // return now to avoid emitting malformed mappings.
+ // FIXME(#117788): Track down why this happens.
+ return;
+ }
let mut macro_name_cov = self.curr().clone();
self.curr_mut().span =
self.curr().span.with_lo(self.curr().span.lo() + after_macro_bang);
@@ -822,7 +769,7 @@ pub(super) fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span>
// and `_1` is the `Place` for `somenum`.
//
// If and when the Issue is resolved, remove this special case match pattern:
- StatementKind::FakeRead(box (cause, _)) if cause == FakeReadCause::ForGuardBinding => None,
+ StatementKind::FakeRead(box (FakeReadCause::ForGuardBinding, _)) => None,
// Retain spans from all other statements
StatementKind::FakeRead(box (_, _)) // Not including `ForGuardBinding`
@@ -867,8 +814,8 @@ pub(super) fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Sp
}
// Retain spans from all other terminators
- TerminatorKind::Resume
- | TerminatorKind::Terminate
+ TerminatorKind::UnwindResume
+ | TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Yield { .. }
| TerminatorKind::GeneratorDrop