Skip to content

Commit 6da319f

Browse files
committed
coverage: Store all of a function's mappings in function coverage info
Previously, mappings were attached to individual coverage statements in MIR. That necessitated special handling in MIR optimizations to avoid deleting those statements, since otherwise codegen would be unable to reassemble the original list of mappings. With this change, a function's list of mappings is now attached to its MIR body, and survives intact even if individual statements are deleted by optimizations.
1 parent 4099ab1 commit 6da319f

File tree

14 files changed

+107
-270
lines changed

14 files changed

+107
-270
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs

Lines changed: 28 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,10 @@ pub struct FunctionCoverage<'tcx> {
2323
function_coverage_info: &'tcx FunctionCoverageInfo,
2424
is_used: bool,
2525

26-
/// Tracks which counters have been seen, to avoid duplicate mappings
27-
/// that might be introduced by MIR inlining.
26+
/// Tracks which counters have been seen, so that we can identify mappings
27+
/// to counters that were optimized out, and set them to zero.
2828
counters_seen: BitSet<CounterId>,
2929
expressions: IndexVec<ExpressionId, Option<Expression>>,
30-
mappings: Vec<Mapping>,
3130
}
3231

3332
impl<'tcx> FunctionCoverage<'tcx> {
@@ -63,7 +62,6 @@ impl<'tcx> FunctionCoverage<'tcx> {
6362
is_used,
6463
counters_seen: BitSet::new_empty(num_counters),
6564
expressions: IndexVec::from_elem_n(None, num_expressions),
66-
mappings: Vec::new(),
6765
}
6866
}
6967

@@ -72,28 +70,20 @@ impl<'tcx> FunctionCoverage<'tcx> {
7270
self.is_used
7371
}
7472

75-
/// Adds code regions to be counted by an injected counter intrinsic.
73+
/// Marks a counter ID as having been seen in a counter-increment statement.
7674
#[instrument(level = "debug", skip(self))]
77-
pub(crate) fn add_counter(&mut self, id: CounterId, code_regions: &[CodeRegion]) {
78-
if self.counters_seen.insert(id) {
79-
self.add_mappings(CovTerm::Counter(id), code_regions);
80-
}
75+
pub(crate) fn mark_counter_id_seen(&mut self, id: CounterId) {
76+
self.counters_seen.insert(id);
8177
}
8278

83-
/// Adds information about a coverage expression, along with zero or more
84-
/// code regions mapped to that expression.
85-
///
86-
/// Both counters and "counter expressions" (or simply, "expressions") can be operands in other
87-
/// expressions. These are tracked as separate variants of `CovTerm`, so there is no ambiguity
88-
/// between operands that are counter IDs and operands that are expression IDs.
79+
/// Adds information about a coverage expression.
8980
#[instrument(level = "debug", skip(self))]
9081
pub(crate) fn add_counter_expression(
9182
&mut self,
9283
expression_id: ExpressionId,
9384
lhs: CovTerm,
9485
op: Op,
9586
rhs: CovTerm,
96-
code_regions: &[CodeRegion],
9787
) {
9888
debug_assert!(
9989
expression_id.as_usize() < self.expressions.len(),
@@ -107,10 +97,7 @@ impl<'tcx> FunctionCoverage<'tcx> {
10797
let expression = Expression { lhs, op, rhs };
10898
let slot = &mut self.expressions[expression_id];
10999
match slot {
110-
None => {
111-
*slot = Some(expression);
112-
self.add_mappings(CovTerm::Expression(expression_id), code_regions);
113-
}
100+
None => *slot = Some(expression),
114101
// If this expression ID slot has already been filled, it should
115102
// contain identical information.
116103
Some(ref previous_expression) => assert_eq!(
@@ -120,29 +107,6 @@ impl<'tcx> FunctionCoverage<'tcx> {
120107
}
121108
}
122109

123-
/// Adds regions that will be marked as "unreachable", with a constant "zero counter".
124-
#[instrument(level = "debug", skip(self))]
125-
pub(crate) fn add_unreachable_regions(&mut self, code_regions: &[CodeRegion]) {
126-
assert!(!code_regions.is_empty(), "unreachable regions always have code regions");
127-
self.add_mappings(CovTerm::Zero, code_regions);
128-
}
129-
130-
#[instrument(level = "debug", skip(self))]
131-
fn add_mappings(&mut self, term: CovTerm, code_regions: &[CodeRegion]) {
132-
self.mappings
133-
.extend(code_regions.iter().cloned().map(|code_region| Mapping { term, code_region }));
134-
}
135-
136-
pub(crate) fn finalize(&mut self) {
137-
// Reorder the collected mappings so that counter mappings are first and
138-
// zero mappings are last, matching the historical order.
139-
self.mappings.sort_by_key(|mapping| match mapping.term {
140-
CovTerm::Counter(_) => 0,
141-
CovTerm::Expression(_) => 1,
142-
CovTerm::Zero => u8::MAX,
143-
});
144-
}
145-
146110
/// Identify expressions that will always have a value of zero, and note
147111
/// their IDs in [`ZeroExpressions`]. Mappings that refer to a zero expression
148112
/// can instead become mappings to a constant zero value.
@@ -235,7 +199,7 @@ impl<'tcx> FunctionCoverage<'tcx> {
235199
// the two vectors should correspond 1:1.
236200
assert_eq!(self.expressions.len(), counter_expressions.len());
237201

238-
let counter_regions = self.counter_regions();
202+
let counter_regions = self.counter_regions(zero_expressions);
239203

240204
(counter_expressions, counter_regions)
241205
}
@@ -280,9 +244,26 @@ impl<'tcx> FunctionCoverage<'tcx> {
280244

281245
/// Converts this function's coverage mappings into an intermediate form
282246
/// that will be used by `mapgen` when preparing for FFI.
283-
fn counter_regions(&self) -> impl Iterator<Item = (Counter, &CodeRegion)> {
284-
self.mappings.iter().map(|&Mapping { term, ref code_region }| {
285-
let counter = Counter::from_term(term);
247+
fn counter_regions(
248+
&self,
249+
zero_expressions: ZeroExpressions,
250+
) -> impl Iterator<Item = (Counter, &CodeRegion)> {
251+
// Historically, mappings were stored directly in counter/expression
252+
// statements in MIR, and MIR optimizations would sometimes remove them.
253+
// That's mostly no longer true, so now we detect cases where that would
254+
// have happened, and zero out the corresponding mappings here instead.
255+
let counter_for_term = move |term: CovTerm| {
256+
let force_to_zero = match term {
257+
CovTerm::Counter(id) => !self.counters_seen.contains(id),
258+
CovTerm::Expression(id) => zero_expressions.contains(id),
259+
CovTerm::Zero => false,
260+
};
261+
if force_to_zero { Counter::ZERO } else { Counter::from_term(term) }
262+
};
263+
264+
self.function_coverage_info.mappings.iter().map(move |mapping| {
265+
let &Mapping { term, ref code_region } = mapping;
266+
let counter = counter_for_term(term);
286267
(counter, code_region)
287268
})
288269
}

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,8 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
5959

6060
// Encode coverage mappings and generate function records
6161
let mut function_data = Vec::new();
62-
for (instance, mut function_coverage) in function_coverage_map {
62+
for (instance, function_coverage) in function_coverage_map {
6363
debug!("Generate function coverage for {}, {:?}", cx.codegen_unit.name(), instance);
64-
function_coverage.finalize();
65-
let function_coverage = function_coverage;
6664

6765
let mangled_function_name = tcx.symbol_name(instance).name;
6866
let source_hash = function_coverage.source_hash();

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,12 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
8888
/// For used/called functions, the coverageinfo was already added to the
8989
/// `function_coverage_map` (keyed by function `Instance`) during codegen.
9090
/// But in this case, since the unused function was _not_ previously
91-
/// codegenned, collect the coverage `CodeRegion`s from the MIR and add
92-
/// them. Since the function is never called, all of its `CodeRegion`s can be
93-
/// added as `unreachable_region`s.
91+
/// codegenned, collect the function coverage info from MIR and add an
92+
/// "unused" entry to the function coverage map.
9493
fn define_unused_fn(&self, def_id: DefId, function_coverage_info: &'tcx FunctionCoverageInfo) {
9594
let instance = declare_unused_fn(self, def_id);
9695
codegen_unused_fn_and_counter(self, instance);
97-
add_unused_function_coverage(self, instance, def_id, function_coverage_info);
96+
add_unused_function_coverage(self, instance, function_coverage_info);
9897
}
9998
}
10099

@@ -116,10 +115,10 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
116115
.entry(instance)
117116
.or_insert_with(|| FunctionCoverage::new(instance, function_coverage_info));
118117

119-
let Coverage { kind, code_regions } = coverage;
118+
let Coverage { kind } = coverage;
120119
match *kind {
121-
CoverageKind::Counter { id } => {
122-
func_coverage.add_counter(id, code_regions);
120+
CoverageKind::CounterIncrement { id } => {
121+
func_coverage.mark_counter_id_seen(id);
123122
// We need to explicitly drop the `RefMut` before calling into `instrprof_increment`,
124123
// as that needs an exclusive borrow.
125124
drop(coverage_map);
@@ -147,10 +146,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
147146
bx.instrprof_increment(fn_name, hash, num_counters, index);
148147
}
149148
CoverageKind::Expression { id, lhs, op, rhs } => {
150-
func_coverage.add_counter_expression(id, lhs, op, rhs, code_regions);
151-
}
152-
CoverageKind::Unreachable => {
153-
func_coverage.add_unreachable_regions(code_regions);
149+
func_coverage.add_counter_expression(id, lhs, op, rhs);
154150
}
155151
}
156152
}
@@ -213,16 +209,11 @@ fn codegen_unused_fn_and_counter<'tcx>(cx: &CodegenCx<'_, 'tcx>, instance: Insta
213209
fn add_unused_function_coverage<'tcx>(
214210
cx: &CodegenCx<'_, 'tcx>,
215211
instance: Instance<'tcx>,
216-
def_id: DefId,
217212
function_coverage_info: &'tcx FunctionCoverageInfo,
218213
) {
219-
let tcx = cx.tcx;
220-
221-
let mut function_coverage = FunctionCoverage::unused(instance, function_coverage_info);
222-
for &code_region in tcx.covered_code_regions(def_id) {
223-
let code_region = std::slice::from_ref(code_region);
224-
function_coverage.add_unreachable_regions(code_region);
225-
}
214+
// An unused function's mappings will automatically be rewritten to map to
215+
// zero, because none of its counters/expressions are marked as seen.
216+
let function_coverage = FunctionCoverage::unused(instance, function_coverage_info);
226217

227218
if let Some(coverage_context) = cx.coverage_context() {
228219
coverage_context.function_coverage_map.borrow_mut().insert(instance, function_coverage);

compiler/rustc_middle/src/mir/coverage.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,13 @@ impl Debug for CovTerm {
6161

6262
#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
6363
pub enum CoverageKind {
64-
Counter {
65-
/// ID of this counter within its enclosing function.
66-
/// Expressions in the same function can refer to it as an operand.
67-
id: CounterId,
68-
},
64+
/// Marks the point in MIR control flow represented by a coverage counter.
65+
///
66+
/// This is eventually lowered to `llvm.instrprof.increment` in LLVM IR.
67+
///
68+
/// If this statement does not survive MIR optimizations, any mappings that
69+
/// refer to this counter can have those references simplified to zero.
70+
CounterIncrement { id: CounterId },
6971
Expression {
7072
/// ID of this coverage-counter expression within its enclosing function.
7173
/// Other expressions in the same function can refer to it as an operand.
@@ -74,14 +76,13 @@ pub enum CoverageKind {
7476
op: Op,
7577
rhs: CovTerm,
7678
},
77-
Unreachable,
7879
}
7980

8081
impl Debug for CoverageKind {
8182
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
8283
use CoverageKind::*;
8384
match self {
84-
Counter { id } => write!(fmt, "Counter({:?})", id.index()),
85+
CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()),
8586
Expression { id, lhs, op, rhs } => write!(
8687
fmt,
8788
"Expression({:?}) = {:?} {} {:?}",
@@ -93,7 +94,6 @@ impl Debug for CoverageKind {
9394
},
9495
rhs,
9596
),
96-
Unreachable => write!(fmt, "Unreachable"),
9797
}
9898
}
9999
}
@@ -158,4 +158,6 @@ pub struct FunctionCoverageInfo {
158158
pub function_source_hash: u64,
159159
pub num_counters: usize,
160160
pub num_expressions: usize,
161+
162+
pub mappings: Vec<Mapping>,
161163
}

compiler/rustc_middle/src/mir/pretty.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,24 @@ pub fn write_mir_intro<'tcx>(
493493
// Add an empty line before the first block is printed.
494494
writeln!(w)?;
495495

496+
if let Some(function_coverage_info) = &body.function_coverage_info {
497+
write_function_coverage_info(function_coverage_info, w)?;
498+
}
499+
500+
Ok(())
501+
}
502+
503+
fn write_function_coverage_info(
504+
function_coverage_info: &coverage::FunctionCoverageInfo,
505+
w: &mut dyn io::Write,
506+
) -> io::Result<()> {
507+
let coverage::FunctionCoverageInfo { mappings, .. } = function_coverage_info;
508+
509+
for coverage::Mapping { term, code_region } in mappings {
510+
writeln!(w, "{INDENT}coverage {term:?} => {code_region:?};")?;
511+
}
512+
writeln!(w)?;
513+
496514
Ok(())
497515
}
498516

@@ -685,13 +703,7 @@ impl Debug for Statement<'_> {
685703
AscribeUserType(box (ref place, ref c_ty), ref variance) => {
686704
write!(fmt, "AscribeUserType({place:?}, {variance:?}, {c_ty:?})")
687705
}
688-
Coverage(box mir::Coverage { ref kind, ref code_regions }) => {
689-
if code_regions.is_empty() {
690-
write!(fmt, "Coverage::{kind:?}")
691-
} else {
692-
write!(fmt, "Coverage::{kind:?} for {code_regions:?}")
693-
}
694-
}
706+
Coverage(box mir::Coverage { ref kind }) => write!(fmt, "Coverage::{kind:?}"),
695707
Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"),
696708
ConstEvalCounter => write!(fmt, "ConstEvalCounter"),
697709
Nop => write!(fmt, "nop"),

compiler/rustc_middle/src/mir/syntax.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
66
use super::{BasicBlock, Const, Local, UserTypeProjection};
77

8-
use crate::mir::coverage::{CodeRegion, CoverageKind};
8+
use crate::mir::coverage::CoverageKind;
99
use crate::traits::Reveal;
1010
use crate::ty::adjustment::PointerCoercion;
1111
use crate::ty::GenericArgsRef;
@@ -514,7 +514,6 @@ pub enum FakeReadCause {
514514
#[derive(TypeFoldable, TypeVisitable)]
515515
pub struct Coverage {
516516
pub kind: CoverageKind,
517-
pub code_regions: Vec<CodeRegion>,
518517
}
519518

520519
#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]

compiler/rustc_middle/src/query/mod.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -581,17 +581,6 @@ rustc_queries! {
581581
arena_cache
582582
}
583583

584-
/// Returns the `CodeRegions` for a function that has instrumented coverage, in case the
585-
/// function was optimized out before codegen, and before being added to the Coverage Map.
586-
query covered_code_regions(key: DefId) -> &'tcx Vec<&'tcx mir::coverage::CodeRegion> {
587-
desc {
588-
|tcx| "retrieving the covered `CodeRegion`s, if instrumented, for `{}`",
589-
tcx.def_path_str(key)
590-
}
591-
arena_cache
592-
cache_on_disk_if { key.is_local() }
593-
}
594-
595584
/// The `DefId` is the `DefId` of the containing MIR body. Promoteds do not have their own
596585
/// `DefId`. This function returns all promoteds in the specified body. The body references
597586
/// promoteds by the `DefId` and the `mir::Promoted` index. This is necessary, because

0 commit comments

Comments
 (0)