Skip to content

Commit 6b5c247

Browse files
committed
coverage: Add CoverageKind::SpanMarker for including extra spans in MIR
There are cases where coverage instrumentation wants to show a span for some syntax element, but there is no MIR node that naturally carries that span, so the instrumentor can't see it. MIR building can now use this new kind of coverage statement to deliberately include those spans in MIR, attached to a dummy statement that has no other effect.
1 parent e318b92 commit 6b5c247

File tree

4 files changed

+36
-3
lines changed

4 files changed

+36
-3
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
100100

101101
let Coverage { kind } = coverage;
102102
match *kind {
103+
// Span markers are only meaningful during MIR instrumentation,
104+
// and have no effect during codegen.
105+
CoverageKind::SpanMarker => {}
103106
CoverageKind::CounterIncrement { id } => {
104107
func_coverage.mark_counter_id_seen(id);
105108
// We need to explicitly drop the `RefMut` before calling into `instrprof_increment`,

compiler/rustc_middle/src/mir/coverage.rs

+8
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@ impl Debug for CovTerm {
7676

7777
#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
7878
pub enum CoverageKind {
79+
/// Marks a span that might otherwise not be represented in MIR, so that
80+
/// coverage instrumentation can associate it with its enclosing block/BCB.
81+
///
82+
/// Only used by the `InstrumentCoverage` pass, and has no effect during
83+
/// codegen.
84+
SpanMarker,
85+
7986
/// Marks the point in MIR control flow represented by a coverage counter.
8087
///
8188
/// This is eventually lowered to `llvm.instrprof.increment` in LLVM IR.
@@ -99,6 +106,7 @@ impl Debug for CoverageKind {
99106
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
100107
use CoverageKind::*;
101108
match self {
109+
SpanMarker => write!(fmt, "SpanMarker"),
102110
CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()),
103111
ExpressionUsed { id } => write!(fmt, "ExpressionUsed({:?})", id.index()),
104112
}

compiler/rustc_mir_build/src/build/cfg.rs

+13
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,19 @@ impl<'tcx> CFG<'tcx> {
101101
self.push(block, stmt);
102102
}
103103

104+
/// Adds a dummy statement whose only role is to associate a span with its
105+
/// enclosing block for the purposes of coverage instrumentation.
106+
///
107+
/// This results in more accurate coverage reports for certain kinds of
108+
/// syntax (e.g. `continue` or `if !`) that would otherwise not appear in MIR.
109+
pub(crate) fn push_coverage_span_marker(&mut self, block: BasicBlock, source_info: SourceInfo) {
110+
let kind = StatementKind::Coverage(Box::new(Coverage {
111+
kind: coverage::CoverageKind::SpanMarker,
112+
}));
113+
let stmt = Statement { source_info, kind };
114+
self.push(block, stmt);
115+
}
116+
104117
pub(crate) fn terminate(
105118
&mut self,
106119
block: BasicBlock,

compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,13 @@ fn is_closure(statement: &Statement<'_>) -> bool {
9292
/// If the MIR `Statement` has a span contributive to computing coverage spans,
9393
/// return it; otherwise return `None`.
9494
fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> {
95+
use mir::coverage::CoverageKind;
96+
9597
match statement.kind {
9698
// These statements have spans that are often outside the scope of the executed source code
9799
// for their parent `BasicBlock`.
98100
StatementKind::StorageLive(_)
99101
| StatementKind::StorageDead(_)
100-
// Coverage should not be encountered, but don't inject coverage coverage
101-
| StatementKind::Coverage(_)
102102
// Ignore `ConstEvalCounter`s
103103
| StatementKind::ConstEvalCounter
104104
// Ignore `Nop`s
@@ -122,9 +122,13 @@ fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> {
122122
// If and when the Issue is resolved, remove this special case match pattern:
123123
StatementKind::FakeRead(box (FakeReadCause::ForGuardBinding, _)) => None,
124124

125-
// Retain spans from all other statements
125+
// Retain spans from most other statements.
126126
StatementKind::FakeRead(box (_, _)) // Not including `ForGuardBinding`
127127
| StatementKind::Intrinsic(..)
128+
| StatementKind::Coverage(box mir::Coverage {
129+
// The purpose of `SpanMarker` is to be matched and accepted here.
130+
kind: CoverageKind::SpanMarker
131+
})
128132
| StatementKind::Assign(_)
129133
| StatementKind::SetDiscriminant { .. }
130134
| StatementKind::Deinit(..)
@@ -133,6 +137,11 @@ fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> {
133137
| StatementKind::AscribeUserType(_, _) => {
134138
Some(statement.source_info.span)
135139
}
140+
141+
StatementKind::Coverage(box mir::Coverage {
142+
// These coverage statements should not exist at this point, so ignore them.
143+
kind: CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. }
144+
}) => None,
136145
}
137146
}
138147

0 commit comments

Comments
 (0)