Skip to content
Open
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
};

match *kind {
CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!(
CoverageKind::Point { .. } | CoverageKind::BlockMarker { .. } => unreachable!(
"marker statement {kind:?} should have been removed by CleanupPostBorrowck"
),
CoverageKind::VirtualCounter { bcb }
Expand Down
43 changes: 34 additions & 9 deletions compiler/rustc_middle/src/mir/coverage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use std::fmt::{self, Debug, Formatter};

use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::HirId;
use rustc_index::{Idx, IndexVec};
use rustc_macros::{StableHash, TyDecodable, TyEncodable};
use rustc_span::Span;
Expand Down Expand Up @@ -70,13 +71,24 @@ impl Debug for CovTerm {
}
}

/// The specific relationship between [`CoverageKind::Point`] and its [`HirId`].
#[derive(Clone, Copy, Debug, PartialEq, TyEncodable, TyDecodable, StableHash)]
pub enum PointKind {
/// Inserted just before evaluating an expression.
Expr,
/// Inserted when a one-sided `if` expression generates its synthetic `else {}`.
/// The absent `else` has no node, so [`HirId`] is the `if` expression.
ImplicitElse,
/// Inserted at the end of a function's body. [`HirId`] is the function itself.
FunctionEnd,
}

#[derive(Clone, PartialEq, TyEncodable, TyDecodable, StableHash)]
pub enum CoverageKind {
/// Marks a span that might otherwise not be represented in MIR, so that
/// coverage instrumentation can associate it with its enclosing block/BCB.
///
/// Should be erased before codegen (at some point after `InstrumentCoverage`).
SpanMarker,
/// Associates a HIR node (such as an expression) with a particular point in
/// MIR control-flow. The relationship between the node and the point is
/// indicated by [`PointKind`]. Injected during MIR building.
Point { point_kind: PointKind, hir_id: HirId },

/// Marks its enclosing basic block with an ID that can be referred to by
/// side data in [`CoverageEarlyInfo`].
Expand All @@ -94,11 +106,24 @@ pub enum CoverageKind {

impl Debug for CoverageKind {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
use CoverageKind::*;
match self {
SpanMarker => write!(fmt, "SpanMarker"),
BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()),
VirtualCounter { bcb } => write!(fmt, "VirtualCounter({bcb:?})"),
CoverageKind::Point { point_kind, hir_id } => {
write!(fmt, "Point({point_kind:?}, {hir_id:?}")
}
CoverageKind::BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()),
CoverageKind::VirtualCounter { bcb } => write!(fmt, "VirtualCounter({bcb:?})"),
}
}
}

impl CoverageKind {
/// Returns true if this kind of coverage statement is a marker inserted during
/// MIR building, for use by analysis in the `InstrumentCoverage` pass, and is
/// no longer needed after that pass.
pub fn is_removed_after_analysis(&self) -> bool {
match self {
CoverageKind::Point { .. } | CoverageKind::BlockMarker { .. } => true,
CoverageKind::VirtualCounter { .. } => false,
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_middle/src/mir/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,14 @@ pub enum AnalysisPhase {
/// * [`TerminatorKind::FalseEdge`]
/// * [`StatementKind::FakeRead`]
/// * [`StatementKind::AscribeUserType`]
/// * [`StatementKind::Coverage`] with [`CoverageKind::BlockMarker`] or
/// [`CoverageKind::SpanMarker`]
/// * [`StatementKind::Coverage`] with [`CoverageKind::is_removed_after_analysis`]
/// * [`Rvalue::Ref`] with `BorrowKind::Fake`
/// * [`CastKind::PointerCoercion`] with any of the following:
/// * [`PointerCoercion::ArrayToPointer`]
/// * [`PointerCoercion::MutToConstPointer`]
///
/// [`CoverageKind::is_removed_after_analysis`]: crate::mir::coverage::CoverageKind::is_removed_after_analysis
///
/// Furthermore, `Deref` projections must be the first projection within any place (if they
/// appear at all)
PostCleanup = 1,
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_middle/src/thir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,9 @@ pub struct Expr<'tcx> {

/// The id of the HIR expression whose [temporary scope] should be used for this expression.
///
/// Also used by coverage instrumentation to recover the HIR node that corresponds to a THIR
/// expression node.
///
/// [temporary scope]: https://doc.rust-lang.org/reference/destructors.html#temporary-scopes
pub temp_scope_id: hir::ItemLocalId,

Expand Down
11 changes: 0 additions & 11 deletions compiler/rustc_mir_build/src/builder/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,17 +107,6 @@ impl<'tcx> CFG<'tcx> {
self.push(block, stmt);
}

/// Adds a dummy statement whose only role is to associate a span with its
/// enclosing block for the purposes of coverage instrumentation.
///
/// This results in more accurate coverage reports for certain kinds of
/// syntax (e.g. `continue` or `if !`) that would otherwise not appear in MIR.
pub(crate) fn push_coverage_span_marker(&mut self, block: BasicBlock, source_info: SourceInfo) {
let kind = StatementKind::Coverage(coverage::CoverageKind::SpanMarker);
let stmt = Statement::new(source_info, kind);
self.push(block, stmt);
}

pub(crate) fn terminate(
&mut self,
block: BasicBlock,
Expand Down
79 changes: 76 additions & 3 deletions compiler/rustc_mir_build/src/builder/coverageinfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@ use std::assert_matches;
use std::collections::hash_map::Entry;

use rustc_data_structures::fx::FxHashMap;
use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageEarlyInfo, CoverageKind};
use rustc_middle::mir::{self, BasicBlock, SourceInfo, UnOp};
use rustc_middle::thir::{ExprId, ExprKind, Pat, Thir};
use rustc_hir::HirId;
use rustc_middle::mir::coverage::{
BlockMarkerId, BranchSpan, CoverageEarlyInfo, CoverageKind, PointKind,
};
use rustc_middle::mir::{self, BasicBlock, SourceInfo, Statement, UnOp};
use rustc_middle::thir::{self, ExprId, ExprKind, Pat, Thir};
use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::LocalDefId;

use crate::builder::{Builder, CFG};

/// Collects coverage-related information during MIR building, to eventually be
/// turned into a function's [`CoverageEarlyInfo`] when MIR building is complete.
///
/// FIXME(Zalathar): Now that we have [`CoverageKind::Point`], we should be able
/// to remove this and perform HIR-aware analysis during instrumentation instead.
pub(crate) struct CoverageInfoBuilder {
/// Maps condition expressions to their enclosing `!`, for better instrumentation.
nots: FxHashMap<ExprId, NotInfo>,
Expand Down Expand Up @@ -175,6 +181,73 @@ impl CoverageInfoBuilder {
}

impl<'tcx> Builder<'_, 'tcx> {
/// Does nothing if `-Cinstrument-coverage` is not enabled.
///
/// Otherwise, pushes a marker statement to `block` indicating that this is where
/// the HIR expression `hir_id` is being evaluated.
pub(crate) fn push_coverage_point_for_expr(
&mut self,
block: BasicBlock,
source_info: SourceInfo,
hir_id: HirId,
) {
if !self.tcx.sess.instrument_coverage() {
return;
}
self.push_coverage_point_inner(block, source_info, PointKind::Expr, hir_id);
}

/// Does nothing if `-Cinstrument-coverage` is not enabled.
///
/// Otherwise, pushes a marker statement to `block` indicating that this is where
/// the one-sided if-expression `if_expr` will generate its synthetic `else {}`
/// path, since it lacks an explicit `else` block.
pub(crate) fn push_coverage_point_for_implicit_else(
&mut self,
block: BasicBlock,
source_info: SourceInfo,
if_expr: &thir::Expr<'tcx>,
) {
if !self.tcx.sess.instrument_coverage() {
return;
}
// Recover the full HirId by combining a local ID with the function's owner ID.
let hir_id = HirId { owner: self.hir_id.owner, local_id: if_expr.temp_scope_id };
self.push_coverage_point_inner(block, source_info, PointKind::ImplicitElse, hir_id);
}

/// Does nothing if `-Cinstrument-coverage` is not enabled.
///
/// Otherwise, pushes a marker statement to `block` indicating that this is where
/// the function `fn_hir_id` would implicitly return at the end of its body.
pub(crate) fn push_coverage_point_for_fn_end(
&mut self,
block: BasicBlock,
source_info: SourceInfo,
fn_hir_id: HirId,
) {
if !self.tcx.sess.instrument_coverage() {
return;
}
self.push_coverage_point_inner(block, source_info, PointKind::FunctionEnd, fn_hir_id);
}

fn push_coverage_point_inner(
&mut self,
block: BasicBlock,
source_info: SourceInfo,
point_kind: PointKind,
hir_id: HirId,
) {
assert!(self.tcx.sess.instrument_coverage());

let stmt = Statement::new(
source_info,
mir::StatementKind::Coverage(CoverageKind::Point { point_kind, hir_id }),
);
self.cfg.push(block, stmt);
}

/// If condition coverage is enabled, inject extra blocks and marker statements
/// that will let us track the value of the condition in `place`.
pub(crate) fn visit_coverage_standalone_condition(
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_mir_build/src/builder/expr/as_operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let source_info = this.source_info(expr.span);
let region_scope = (region_scope, source_info);
return this.in_scope(region_scope, LintLevel::Explicit(hir_id), |this| {
this.push_coverage_point_for_expr(block, source_info, hir_id);
this.as_operand(block, scope, value, local_info, needs_temporary)
});
}
Expand Down Expand Up @@ -170,6 +171,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let source_info = this.source_info(expr.span);
let region_scope = (region_scope, source_info);
return this.in_scope(region_scope, LintLevel::Explicit(hir_id), |this| {
this.push_coverage_point_for_expr(block, source_info, hir_id);
this.as_call_operand(block, scope, value)
});
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir_build/src/builder/expr/as_place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
match expr.kind {
ExprKind::Scope { region_scope, hir_id, value } => {
this.in_scope((region_scope, source_info), LintLevel::Explicit(hir_id), |this| {
this.push_coverage_point_for_expr(block, source_info, hir_id);
this.expr_as_place(block, value, mutability, fake_borrow_temps)
})
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ExprKind::Scope { region_scope, hir_id, value } => {
let region_scope = (region_scope, source_info);
this.in_scope(region_scope, LintLevel::Explicit(hir_id), |this| {
this.push_coverage_point_for_expr(block, source_info, hir_id);
this.as_rvalue(block, scope, value)
})
}
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_mir_build/src/builder/expr/as_temp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
return this.in_scope(
(region_scope, source_info),
LintLevel::Explicit(hir_id),
|this| this.as_temp(block, temp_lifetime, value, mutability),
|this| {
this.push_coverage_point_for_expr(block, source_info, hir_id);
this.as_temp(block, temp_lifetime, value, mutability)
},
);
}

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_mir_build/src/builder/expr/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ExprKind::Scope { region_scope, hir_id, value } => {
let region_scope = (region_scope, source_info);
this.in_scope(region_scope, LintLevel::Explicit(hir_id), |this| {
this.push_coverage_point_for_expr(block, source_info, hir_id);
this.expr_into_dest(destination, block, value)
})
}
Expand Down Expand Up @@ -114,6 +115,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// There is no `else` arm, so we know both arms have type `()`.
// Generate the implicit `else {}` by assigning unit.
let correct_si = this.source_info(expr_span.shrink_to_hi());
this.push_coverage_point_for_implicit_else(else_blk, correct_si, expr);
this.cfg.push_assign_unit(else_blk, correct_si, destination, this.tcx);
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir_build/src/builder/expr/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
match expr.kind {
ExprKind::Scope { region_scope, hir_id, value } => {
this.in_scope((region_scope, source_info), LintLevel::Explicit(hir_id), |this| {
this.push_coverage_point_for_expr(block, source_info, hir_id);
this.stmt_expr(block, value, statement_scope)
})
}
Expand Down
10 changes: 3 additions & 7 deletions compiler/rustc_mir_build/src/builder/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let local_scope = this.local_scope();
let (success_block, failure_block) =
this.in_if_then_scope(local_scope, expr_span, |this| {
// Help out coverage instrumentation by injecting a dummy statement with
// the original condition's span (including `!`). This fixes #115468.
if this.tcx.sess.instrument_coverage() {
this.cfg.push_coverage_span_marker(block, this.source_info(expr_span));
}
this.then_else_break_inner(
block,
arg,
Expand All @@ -182,8 +177,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
failure_block.unit()
}
ExprKind::Scope { region_scope, hir_id, value } => {
let region_scope = (region_scope, this.source_info(expr_span));
this.in_scope(region_scope, LintLevel::Explicit(hir_id), |this| {
let source_info = this.source_info(expr_span);
this.in_scope((region_scope, source_info), LintLevel::Explicit(hir_id), |this| {
this.push_coverage_point_for_expr(block, source_info, hir_id);
this.then_else_break_inner(block, value, args)
})
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir_build/src/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,7 @@ fn construct_fn<'tcx>(
})
.into_block();
let source_info = builder.source_info(fn_end);
builder.push_coverage_point_for_fn_end(return_block, source_info, fn_id);
builder.cfg.terminate(return_block, source_info, TerminatorKind::Return);
builder.build_drop_trees();
return_block.unit()
Expand Down
10 changes: 1 addition & 9 deletions compiler/rustc_mir_build/src/builder/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -791,15 +791,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
(None, Some(_)) => {
panic!("`return`, `become` and `break` with value and must have a destination")
}
(None, None) => {
if self.tcx.sess.instrument_coverage() {
// Normally we wouldn't build any MIR in this case, but that makes it
// harder for coverage instrumentation to extract a relevant span for
// `continue` expressions. So here we inject a dummy statement with the
// desired span.
self.cfg.push_coverage_span_marker(block, source_info);
}
}
(None, None) => {}
}

let region_scope = self.scopes.breakable_scopes[break_index].region_scope;
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_mir_build/src/thir/cx/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ impl<'tcx> ThirBuildCx<'tcx> {
}

// Finally, wrap this up in the expr's scope.
//
// (In addition to marking scope, coverage instrumentation also uses this node
// to help mark the point in MIR where an expression is about to be evaluated.)
expr = Expr {
temp_scope_id: expr_scope.local_id,
ty,
Expand Down
14 changes: 5 additions & 9 deletions compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,16 @@
//! - [`AscribeUserType`]
//! - [`FakeRead`]
//! - [`Assign`] statements with a [`Fake`] borrow
//! - [`Coverage`] statements of kind [`BlockMarker`] or [`SpanMarker`]
//! - [`Coverage`] statements that are not needed after the [`InstrumentCoverage`] pass
//!
//! [`AscribeUserType`]: rustc_middle::mir::StatementKind::AscribeUserType
//! [`Assign`]: rustc_middle::mir::StatementKind::Assign
//! [`FakeRead`]: rustc_middle::mir::StatementKind::FakeRead
//! [`Nop`]: rustc_middle::mir::StatementKind::Nop
//! [`Fake`]: rustc_middle::mir::BorrowKind::Fake
//! [`Coverage`]: rustc_middle::mir::StatementKind::Coverage
//! [`BlockMarker`]: rustc_middle::mir::coverage::CoverageKind::BlockMarker
//! [`SpanMarker`]: rustc_middle::mir::coverage::CoverageKind::SpanMarker
//! [`InstrumentCoverage`]: crate::coverage::InstrumentCoverage

use rustc_middle::mir::coverage::CoverageKind;
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::adjustment::PointerCoercion;
Expand All @@ -34,15 +32,13 @@ impl<'tcx> crate::MirPass<'tcx> for CleanupPostBorrowck {
match statement.kind {
StatementKind::AscribeUserType(..)
| StatementKind::Assign((_, Rvalue::Ref(_, BorrowKind::Fake(_), _)))
| StatementKind::Coverage(
// These kinds of coverage statements are markers inserted during
// MIR building, and are not needed after InstrumentCoverage.
CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. },
)
| StatementKind::FakeRead(..)
| StatementKind::BackwardIncompatibleDropHint { .. } => {
statement.make_nop(true)
}
StatementKind::Coverage(ref kind) if kind.is_removed_after_analysis() => {
statement.make_nop(true)
}
StatementKind::Assign((
_,
Rvalue::Cast(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/coverage/expansion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ pub(crate) fn build_expn_tree(
hir_info: &ExtractedHirInfo,
graph: &CoverageGraph,
) -> Result<ExpnTree, MappingsError> {
let raw_spans = from_mir::extract_raw_spans_from_mir(mir_body, graph);
let raw_spans = from_mir::extract_raw_spans_from_mir(mir_body, hir_info, graph);

let mut nodes = FxIndexMap::default();
let new_node = |&context: &SyntaxContext| ExpnNode::for_context(context);
Expand Down
Loading
Loading