Skip to content

Commit 741330a

Browse files
committed
coverage: Store expression data in FunctionCoverageData
Even though expression details are now stored in the info structure, we still need to inject `Expression` statements into MIR, because if one is missing during codegen then we know that it was optimized out and we can remap all of its associated code regions to zero.
1 parent d29a59d commit 741330a

File tree

7 files changed

+41
-106
lines changed

7 files changed

+41
-106
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs

+30-50
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,11 @@ use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind};
22

33
use rustc_data_structures::fx::FxIndexSet;
44
use rustc_index::bit_set::BitSet;
5-
use rustc_index::IndexVec;
65
use rustc_middle::mir::coverage::{
7-
CodeRegion, CounterId, CovTerm, ExpressionId, FunctionCoverageInfo, Mapping, Op,
6+
CodeRegion, CounterId, CovTerm, Expression, ExpressionId, FunctionCoverageInfo, Mapping, Op,
87
};
98
use rustc_middle::ty::Instance;
109

11-
#[derive(Clone, Debug, PartialEq)]
12-
pub struct Expression {
13-
lhs: CovTerm,
14-
op: Op,
15-
rhs: CovTerm,
16-
}
17-
1810
/// Holds all of the coverage mapping data associated with a function instance,
1911
/// collected during traversal of `Coverage` statements in the function's MIR.
2012
#[derive(Debug)]
@@ -26,7 +18,9 @@ pub struct FunctionCoverage<'tcx> {
2618
/// Tracks which counters have been seen, so that we can identify mappings
2719
/// to counters that were optimized out, and set them to zero.
2820
counters_seen: BitSet<CounterId>,
29-
expressions: IndexVec<ExpressionId, Option<Expression>>,
21+
/// Tracks which expressions have one or more associated mappings, but have
22+
/// not yet been seen. This lets us detect that they were optimized out.
23+
expressions_not_seen: BitSet<ExpressionId>,
3024
/// Tracks which expressions are known to always have a value of zero.
3125
/// Only updated during the finalize step.
3226
zero_expressions: FxIndexSet<ExpressionId>,
@@ -55,16 +49,27 @@ impl<'tcx> FunctionCoverage<'tcx> {
5549
is_used: bool,
5650
) -> Self {
5751
let num_counters = function_coverage_info.num_counters;
58-
let num_expressions = function_coverage_info.num_expressions;
52+
let num_expressions = function_coverage_info.expressions.len();
5953
debug!(
6054
"FunctionCoverage::create(instance={instance:?}) has \
6155
num_counters={num_counters}, num_expressions={num_expressions}, is_used={is_used}"
6256
);
57+
58+
// Every expression that has an associated mapping should have a
59+
// corresponding statement in MIR. If we don't see that statement, we
60+
// know that it was removed by MIR optimizations.
61+
let mut expressions_not_seen = BitSet::new_empty(num_expressions);
62+
for Mapping { term, .. } in &function_coverage_info.mappings {
63+
if let &CovTerm::Expression(id) = term {
64+
expressions_not_seen.insert(id);
65+
}
66+
}
67+
6368
Self {
6469
function_coverage_info,
6570
is_used,
6671
counters_seen: BitSet::new_empty(num_counters),
67-
expressions: IndexVec::from_elem_n(None, num_expressions),
72+
expressions_not_seen,
6873
zero_expressions: FxIndexSet::default(),
6974
}
7075
}
@@ -80,35 +85,10 @@ impl<'tcx> FunctionCoverage<'tcx> {
8085
self.counters_seen.insert(id);
8186
}
8287

83-
/// Adds information about a coverage expression.
88+
/// Marks an expression ID as having been seen.
8489
#[instrument(level = "debug", skip(self))]
85-
pub(crate) fn add_counter_expression(
86-
&mut self,
87-
expression_id: ExpressionId,
88-
lhs: CovTerm,
89-
op: Op,
90-
rhs: CovTerm,
91-
) {
92-
debug_assert!(
93-
expression_id.as_usize() < self.expressions.len(),
94-
"expression_id {} is out of range for expressions.len() = {}
95-
for {:?}",
96-
expression_id.as_usize(),
97-
self.expressions.len(),
98-
self,
99-
);
100-
101-
let expression = Expression { lhs, op, rhs };
102-
let slot = &mut self.expressions[expression_id];
103-
match slot {
104-
None => *slot = Some(expression),
105-
// If this expression ID slot has already been filled, it should
106-
// contain identical information.
107-
Some(ref previous_expression) => assert_eq!(
108-
previous_expression, &expression,
109-
"add_counter_expression: expression for id changed"
110-
),
111-
}
90+
pub(crate) fn add_counter_expression(&mut self, expression_id: ExpressionId) {
91+
self.expressions_not_seen.remove(expression_id);
11292
}
11393

11494
pub(crate) fn finalize(&mut self) {
@@ -133,13 +113,13 @@ impl<'tcx> FunctionCoverage<'tcx> {
133113
// and then update the set of always-zero expressions if necessary.
134114
// (By construction, expressions can only refer to other expressions
135115
// that have lower IDs, so one pass is sufficient.)
136-
for (id, maybe_expression) in self.expressions.iter_enumerated() {
137-
let Some(expression) = maybe_expression else {
138-
// If an expression is missing, it must have been optimized away,
116+
for (id, expression) in self.function_coverage_info.expressions.iter_enumerated() {
117+
if self.expressions_not_seen.contains(id) {
118+
// If an expression was not seen, it must have been optimized away,
139119
// so any operand that refers to it can be replaced with zero.
140120
zero_expressions.insert(id);
141121
continue;
142-
};
122+
}
143123

144124
// We don't need to simplify the actual expression data in the
145125
// expressions list; we can just simplify a temporary copy and then
@@ -191,17 +171,17 @@ impl<'tcx> FunctionCoverage<'tcx> {
191171
_ => Counter::from_term(operand),
192172
};
193173

194-
self.expressions
195-
.iter()
196-
.map(|expression| match expression {
197-
None => {
174+
self.function_coverage_info
175+
.expressions
176+
.iter_enumerated()
177+
.map(|(id, &Expression { lhs, op, rhs })| {
178+
if self.expressions_not_seen.contains(id) {
198179
// This expression ID was allocated, but we never saw the
199180
// actual expression, so it must have been optimized out.
200181
// Replace it with a dummy expression, and let LLVM take
201182
// care of omitting it from the expression list.
202183
CounterExpression::DUMMY
203-
}
204-
&Some(Expression { lhs, op, rhs, .. }) => {
184+
} else {
205185
// Convert the operands and operator as normal.
206186
CounterExpression::new(
207187
counter_from_operand(lhs),

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
147147
);
148148
bx.instrprof_increment(fn_name, hash, num_counters, index);
149149
}
150-
CoverageKind::Expression { id, lhs, op, rhs } => {
151-
func_coverage.add_counter_expression(id, lhs, op, rhs);
150+
CoverageKind::Expression { id } => {
151+
func_coverage.add_counter_expression(id);
152152
}
153153
}
154154
}

compiler/rustc_middle/src/mir/coverage.rs

+3-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Metadata from source code coverage analysis and instrumentation.
22
3+
use rustc_index::IndexVec;
34
use rustc_macros::HashStable;
45
use rustc_span::Symbol;
56

@@ -70,9 +71,6 @@ pub enum CoverageKind {
7071
/// ID of this coverage-counter expression within its enclosing function.
7172
/// Other expressions in the same function can refer to it as an operand.
7273
id: ExpressionId,
73-
lhs: CovTerm,
74-
op: Op,
75-
rhs: CovTerm,
7674
},
7775
}
7876

@@ -81,17 +79,7 @@ impl Debug for CoverageKind {
8179
use CoverageKind::*;
8280
match self {
8381
Counter { id } => write!(fmt, "Counter({:?})", id.index()),
84-
Expression { id, lhs, op, rhs } => write!(
85-
fmt,
86-
"Expression({:?}) = {:?} {} {:?}",
87-
id.index(),
88-
lhs,
89-
match op {
90-
Op::Add => "+",
91-
Op::Subtract => "-",
92-
},
93-
rhs,
94-
),
82+
Expression { id } => write!(fmt, "Expression({:?})", id.index()),
9583
}
9684
}
9785
}
@@ -166,7 +154,7 @@ pub struct Mapping {
166154
pub struct FunctionCoverageInfo {
167155
pub function_source_hash: u64,
168156
pub num_counters: usize,
169-
pub num_expressions: usize,
170157

158+
pub expressions: IndexVec<ExpressionId, Expression>,
171159
pub mappings: Vec<Mapping>,
172160
}

compiler/rustc_mir_transform/src/coverage/counters.rs

-4
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,6 @@ impl CoverageCounters {
142142
self.next_counter_id.as_usize()
143143
}
144144

145-
pub(crate) fn num_expressions(&self) -> usize {
146-
self.expressions.len()
147-
}
148-
149145
fn set_bcb_counter(
150146
&mut self,
151147
bcb: BasicCoverageBlock,

compiler/rustc_mir_transform/src/coverage/mod.rs

+3-32
Original file line numberDiff line numberDiff line change
@@ -271,19 +271,10 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
271271
// files), which might be helpful in analyzing unused expressions, to still be generated.
272272
debug_used_expressions.alert_on_unused_expressions(&self.coverage_counters.debug_counters);
273273

274-
////////////////////////////////////////////////////
275-
// Finally, inject the intermediate expressions collected along the way.
276-
for intermediate_expression in &self.coverage_counters.intermediate_expressions {
277-
inject_intermediate_expression(
278-
self.mir_body,
279-
self.make_mir_coverage_kind(intermediate_expression),
280-
);
281-
}
282-
283274
self.mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
284275
function_source_hash: self.function_source_hash,
285276
num_counters: self.coverage_counters.num_counters(),
286-
num_expressions: self.coverage_counters.num_expressions(),
277+
expressions: std::mem::take(&mut self.coverage_counters.expressions),
287278
mappings: std::mem::take(&mut self.mappings),
288279
}));
289280
}
@@ -411,10 +402,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
411402
inject_to_bb,
412403
);
413404
}
414-
BcbCounter::Expression { .. } => inject_intermediate_expression(
415-
self.mir_body,
416-
self.make_mir_coverage_kind(&counter_kind),
417-
),
405+
BcbCounter::Expression { .. } => {}
418406
}
419407
}
420408
}
@@ -442,10 +430,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
442430
fn make_mir_coverage_kind(&self, counter_kind: &BcbCounter) -> CoverageKind {
443431
match *counter_kind {
444432
BcbCounter::Counter { id } => CoverageKind::Counter { id },
445-
BcbCounter::Expression { id } => {
446-
let Expression { lhs, op, rhs } = self.coverage_counters.expressions[id];
447-
CoverageKind::Expression { id, lhs, op, rhs }
448-
}
433+
BcbCounter::Expression { id } => CoverageKind::Expression { id },
449434
}
450435
}
451436
}
@@ -484,20 +469,6 @@ fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb
484469
data.statements.insert(0, statement);
485470
}
486471

487-
// Non-code expressions are injected into the coverage map, without generating executable code.
488-
fn inject_intermediate_expression(mir_body: &mut mir::Body<'_>, expression: CoverageKind) {
489-
debug_assert!(matches!(expression, CoverageKind::Expression { .. }));
490-
debug!(" injecting non-code expression {:?}", expression);
491-
let inject_in_bb = mir::START_BLOCK;
492-
let data = &mut mir_body[inject_in_bb];
493-
let source_info = data.terminator().source_info;
494-
let statement = Statement {
495-
source_info,
496-
kind: StatementKind::Coverage(Box::new(Coverage { kind: expression })),
497-
};
498-
data.statements.push(statement);
499-
}
500-
501472
/// Convert the Span into its file name, start line and column, and end line and column
502473
fn make_code_region(
503474
source_map: &SourceMap,

tests/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ digraph Cov_0_3 {
33
node [fontname="Courier, monospace"];
44
edge [fontname="Courier, monospace"];
55
bcb3__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb3</td></tr><tr><td align="left" balign="left">Counter(bcb3) at 13:10-13:10<br align="left"/> 13:10-13:10: @5[0]: Coverage::Counter(1)</td></tr><tr><td align="left" balign="left">bb5: Goto</td></tr></table>>];
6-
bcb2__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb2</td></tr><tr><td align="left" balign="left">Expression(bcb1:(bcb0 + bcb3) - bcb3) at 12:13-12:18<br align="left"/> 12:13-12:18: @4[0]: Coverage::Expression(1) = Expression(0) - Counter(1)<br align="left"/>Expression(bcb1:(bcb0 + bcb3) - bcb3) at 15:2-15:2<br align="left"/> 15:2-15:2: @4.Return: return</td></tr><tr><td align="left" balign="left">bb4: Return</td></tr></table>>];
6+
bcb2__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb2</td></tr><tr><td align="left" balign="left">Expression(bcb1:(bcb0 + bcb3) - bcb3) at 12:13-12:18<br align="left"/> 12:13-12:18: @4[0]: Coverage::Expression(1)<br align="left"/>Expression(bcb1:(bcb0 + bcb3) - bcb3) at 15:2-15:2<br align="left"/> 15:2-15:2: @4.Return: return</td></tr><tr><td align="left" balign="left">bb4: Return</td></tr></table>>];
77
bcb1__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb1</td></tr><tr><td align="left" balign="left">Expression(bcb0 + bcb3) at 10:5-11:17<br align="left"/> 11:12-11:17: @2.Call: _2 = bar() -&gt; [return: bb3, unwind: bb6]</td></tr><tr><td align="left" balign="left">bb1: FalseUnwind<br align="left"/>bb2: Call</td></tr><tr><td align="left" balign="left">bb3: SwitchInt</td></tr></table>>];
88
bcb0__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb0</td></tr><tr><td align="left" balign="left"></td></tr><tr><td align="left" balign="left">Counter(bcb0) at 9:1-9:11<br align="left"/> </td></tr><tr><td align="left" balign="left">bb0: Goto</td></tr></table>>];
99
bcb3__Cov_0_3 -> bcb1__Cov_0_3 [label=<>];

tests/mir-opt/instrument_coverage.main.InstrumentCoverage.diff

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
}
1414

1515
bb1: {
16-
+ Coverage::Expression(0) = Counter(0) + Counter(1);
16+
+ Coverage::Expression(0);
1717
falseUnwind -> [real: bb2, unwind: bb6];
1818
}
1919

@@ -27,7 +27,7 @@
2727
}
2828

2929
bb4: {
30-
+ Coverage::Expression(1) = Expression(0) - Counter(1);
30+
+ Coverage::Expression(1);
3131
_0 = const ();
3232
StorageDead(_2);
3333
return;

0 commit comments

Comments
 (0)