Skip to content

Commit adce72b

Browse files
committed
coverage: Store expression data in function coverage info
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 8e5f75c commit adce72b

File tree

7 files changed

+58
-145
lines changed

7 files changed

+58
-145
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs

+31-51
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
}
3125

3226
impl<'tcx> FunctionCoverage<'tcx> {
@@ -52,16 +46,27 @@ impl<'tcx> FunctionCoverage<'tcx> {
5246
is_used: bool,
5347
) -> Self {
5448
let num_counters = function_coverage_info.num_counters;
55-
let num_expressions = function_coverage_info.num_expressions;
49+
let num_expressions = function_coverage_info.expressions.len();
5650
debug!(
5751
"FunctionCoverage::create(instance={instance:?}) has \
5852
num_counters={num_counters}, num_expressions={num_expressions}, is_used={is_used}"
5953
);
54+
55+
// Every expression that has an associated mapping should have a
56+
// corresponding statement in MIR. If we don't see that statement, we
57+
// know that it was removed by MIR optimizations.
58+
let mut expressions_not_seen = BitSet::new_empty(num_expressions);
59+
for Mapping { term, .. } in &function_coverage_info.mappings {
60+
if let &CovTerm::Expression(id) = term {
61+
expressions_not_seen.insert(id);
62+
}
63+
}
64+
6065
Self {
6166
function_coverage_info,
6267
is_used,
6368
counters_seen: BitSet::new_empty(num_counters),
64-
expressions: IndexVec::from_elem_n(None, num_expressions),
69+
expressions_not_seen,
6570
}
6671
}
6772

@@ -76,35 +81,10 @@ impl<'tcx> FunctionCoverage<'tcx> {
7681
self.counters_seen.insert(id);
7782
}
7883

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

11090
/// Identify expressions that will always have a value of zero, and note
@@ -125,13 +105,13 @@ impl<'tcx> FunctionCoverage<'tcx> {
125105
// and then update the set of always-zero expressions if necessary.
126106
// (By construction, expressions can only refer to other expressions
127107
// that have lower IDs, so one pass is sufficient.)
128-
for (id, maybe_expression) in self.expressions.iter_enumerated() {
129-
let Some(expression) = maybe_expression else {
130-
// If an expression is missing, it must have been optimized away,
108+
for (id, expression) in self.function_coverage_info.expressions.iter_enumerated() {
109+
if self.expressions_not_seen.contains(id) {
110+
// If an expression was not seen, it must have been optimized away,
131111
// so any operand that refers to it can be replaced with zero.
132112
zero_expressions.insert(id);
133113
continue;
134-
};
114+
}
135115

136116
// We don't need to simplify the actual expression data in the
137117
// expressions list; we can just simplify a temporary copy and then
@@ -184,7 +164,7 @@ impl<'tcx> FunctionCoverage<'tcx> {
184164
// Expression IDs are indices into `self.expressions`, and on the LLVM
185165
// side they will be treated as indices into `counter_expressions`, so
186166
// the two vectors should correspond 1:1.
187-
assert_eq!(self.expressions.len(), counter_expressions.len());
167+
assert_eq!(self.function_coverage_info.expressions.len(), counter_expressions.len());
188168

189169
let counter_regions = self.counter_regions(zero_expressions);
190170

@@ -204,17 +184,17 @@ impl<'tcx> FunctionCoverage<'tcx> {
204184
_ => Counter::from_term(operand),
205185
};
206186

207-
self.expressions
208-
.iter()
209-
.map(|expression| match expression {
210-
None => {
187+
self.function_coverage_info
188+
.expressions
189+
.iter_enumerated()
190+
.map(|(id, &Expression { lhs, op, rhs })| {
191+
if self.expressions_not_seen.contains(id) {
211192
// This expression ID was allocated, but we never saw the
212193
// actual expression, so it must have been optimized out.
213194
// Replace it with a dummy expression, and let LLVM take
214195
// care of omitting it from the expression list.
215196
CounterExpression::DUMMY
216-
}
217-
&Some(Expression { lhs, op, rhs, .. }) => {
197+
} else {
218198
// Convert the operands and operator as normal.
219199
CounterExpression::new(
220200
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

+11-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
}
@@ -133,6 +121,14 @@ impl Op {
133121
}
134122
}
135123

124+
#[derive(Clone, Debug)]
125+
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
126+
pub struct Expression {
127+
pub lhs: CovTerm,
128+
pub op: Op,
129+
pub rhs: CovTerm,
130+
}
131+
136132
#[derive(Clone, Debug)]
137133
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
138134
pub struct Mapping {
@@ -155,7 +151,7 @@ pub struct Mapping {
155151
pub struct FunctionCoverageInfo {
156152
pub function_source_hash: u64,
157153
pub num_counters: usize,
158-
pub num_expressions: usize,
159154

155+
pub expressions: IndexVec<ExpressionId, Expression>,
160156
pub mappings: Vec<Mapping>,
161157
}

compiler/rustc_mir_transform/src/coverage/counters.rs

+8-36
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const NESTED_INDENT: &str = " ";
1919
#[derive(Clone)]
2020
pub(super) enum BcbCounter {
2121
Counter { id: CounterId },
22-
Expression { id: ExpressionId, lhs: CovTerm, op: Op, rhs: CovTerm },
22+
Expression { id: ExpressionId },
2323
}
2424

2525
impl BcbCounter {
@@ -39,17 +39,7 @@ impl Debug for BcbCounter {
3939
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
4040
match self {
4141
Self::Counter { id, .. } => write!(fmt, "Counter({:?})", id.index()),
42-
Self::Expression { id, lhs, op, rhs } => write!(
43-
fmt,
44-
"Expression({:?}) = {:?} {} {:?}",
45-
id.index(),
46-
lhs,
47-
match op {
48-
Op::Add => "+",
49-
Op::Subtract => "-",
50-
},
51-
rhs,
52-
),
42+
Self::Expression { id } => write!(fmt, "Expression({:?})", id.index()),
5343
}
5444
}
5545
}
@@ -58,7 +48,6 @@ impl Debug for BcbCounter {
5848
/// associated with nodes/edges in the BCB graph.
5949
pub(super) struct CoverageCounters {
6050
next_counter_id: CounterId,
61-
next_expression_id: ExpressionId,
6251

6352
/// Coverage counters/expressions that are associated with individual BCBs.
6453
bcb_counters: IndexVec<BasicCoverageBlock, Option<BcbCounter>>,
@@ -69,10 +58,9 @@ pub(super) struct CoverageCounters {
6958
/// Only used by debug assertions, to verify that BCBs with incoming edge
7059
/// counters do not have their own physical counters (expressions are allowed).
7160
bcb_has_incoming_edge_counters: BitSet<BasicCoverageBlock>,
72-
/// Expression nodes that are not directly associated with any particular
73-
/// BCB/edge, but are needed as operands to more complex expressions.
74-
/// These are always [`BcbCounter::Expression`].
75-
pub(super) intermediate_expressions: Vec<BcbCounter>,
61+
/// Table of expression data, associating each expression ID with its
62+
/// corresponding operator (+ or -) and its LHS/RHS operands.
63+
pub(super) expressions: IndexVec<ExpressionId, Expression>,
7664
}
7765

7866
impl CoverageCounters {
@@ -81,12 +69,10 @@ impl CoverageCounters {
8169

8270
Self {
8371
next_counter_id: CounterId::START,
84-
next_expression_id: ExpressionId::START,
85-
8672
bcb_counters: IndexVec::from_elem_n(None, num_bcbs),
8773
bcb_edge_counters: FxHashMap::default(),
8874
bcb_has_incoming_edge_counters: BitSet::new_empty(num_bcbs),
89-
intermediate_expressions: Vec::new(),
75+
expressions: IndexVec::new(),
9076
}
9177
}
9278

@@ -107,8 +93,8 @@ impl CoverageCounters {
10793
}
10894

10995
fn make_expression(&mut self, lhs: CovTerm, op: Op, rhs: CovTerm) -> BcbCounter {
110-
let id = self.next_expression();
111-
BcbCounter::Expression { id, lhs, op, rhs }
96+
let id = self.expressions.push(Expression { lhs, op, rhs });
97+
BcbCounter::Expression { id }
11298
}
11399

114100
/// Counter IDs start from one and go up.
@@ -118,22 +104,10 @@ impl CoverageCounters {
118104
next
119105
}
120106

121-
/// Expression IDs start from 0 and go up.
122-
/// (Counter IDs and Expression IDs are distinguished by the `Operand` enum.)
123-
fn next_expression(&mut self) -> ExpressionId {
124-
let next = self.next_expression_id;
125-
self.next_expression_id = self.next_expression_id + 1;
126-
next
127-
}
128-
129107
pub(super) fn num_counters(&self) -> usize {
130108
self.next_counter_id.as_usize()
131109
}
132110

133-
pub(super) fn num_expressions(&self) -> usize {
134-
self.next_expression_id.as_usize()
135-
}
136-
137111
fn set_bcb_counter(
138112
&mut self,
139113
bcb: BasicCoverageBlock,
@@ -333,7 +307,6 @@ impl<'a> MakeBcbCounters<'a> {
333307
);
334308
debug!(" [new intermediate expression: {:?}]", intermediate_expression);
335309
let intermediate_expression_operand = intermediate_expression.as_term();
336-
self.coverage_counters.intermediate_expressions.push(intermediate_expression);
337310
some_sumup_counter_operand.replace(intermediate_expression_operand);
338311
}
339312
}
@@ -446,7 +419,6 @@ impl<'a> MakeBcbCounters<'a> {
446419
intermediate_expression
447420
);
448421
let intermediate_expression_operand = intermediate_expression.as_term();
449-
self.coverage_counters.intermediate_expressions.push(intermediate_expression);
450422
some_sumup_edge_counter_operand.replace(intermediate_expression_operand);
451423
}
452424
}

0 commit comments

Comments
 (0)