Skip to content

Commit 24c2fff

Browse files
committed
coverage: Defer part of counter-creation until codegen
1 parent 00fb7c4 commit 24c2fff

18 files changed

+175
-285
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs

+4-9
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pub(crate) fn prepare_covfun_record<'tcx>(
5454
let fn_cov_info = tcx.instance_mir(instance.def).function_coverage_info.as_deref()?;
5555
let ids_info = tcx.coverage_ids_info(instance.def)?;
5656

57-
let expressions = prepare_expressions(fn_cov_info, ids_info, is_used);
57+
let expressions = prepare_expressions(ids_info, is_used);
5858

5959
let mut covfun = CovfunRecord {
6060
mangled_function_name: tcx.symbol_name(instance).name,
@@ -76,11 +76,7 @@ pub(crate) fn prepare_covfun_record<'tcx>(
7676
}
7777

7878
/// Convert the function's coverage-counter expressions into a form suitable for FFI.
79-
fn prepare_expressions(
80-
fn_cov_info: &FunctionCoverageInfo,
81-
ids_info: &CoverageIdsInfo,
82-
is_used: bool,
83-
) -> Vec<ffi::CounterExpression> {
79+
fn prepare_expressions(ids_info: &CoverageIdsInfo, is_used: bool) -> Vec<ffi::CounterExpression> {
8480
// If any counters or expressions were removed by MIR opts, replace their
8581
// terms with zero.
8682
let counter_for_term = |term| {
@@ -95,7 +91,7 @@ fn prepare_expressions(
9591
// producing the final coverage map, so there's no need to do the same
9692
// thing on the Rust side unless we're confident we can do much better.
9793
// (See `CounterExpressionsMinimizer` in `CoverageMappingWriter.cpp`.)
98-
fn_cov_info
94+
ids_info
9995
.expressions
10096
.iter()
10197
.map(move |&Expression { lhs, op, rhs }| ffi::CounterExpression {
@@ -142,8 +138,7 @@ fn fill_region_tables<'tcx>(
142138
// If the mapping refers to counters/expressions that were removed by
143139
// MIR opts, replace those occurrences with zero.
144140
let counter_for_bcb = |bcb: BasicCoverageBlock| -> ffi::Counter {
145-
let term =
146-
fn_cov_info.term_for_bcb[bcb].expect("every BCB in a mapping was given a term");
141+
let term = ids_info.term_for_bcb[bcb].expect("every BCB in a mapping was given a term");
147142
let term = if is_zero_term(term) { CovTerm::Zero } else { term };
148143
ffi::Counter::from_term(term)
149144
};

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

+5-14
Original file line numberDiff line numberDiff line change
@@ -160,17 +160,10 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
160160
CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!(
161161
"marker statement {kind:?} should have been removed by CleanupPostBorrowck"
162162
),
163-
CoverageKind::CounterIncrement { id } => {
164-
// The number of counters passed to `llvm.instrprof.increment` might
165-
// be smaller than the number originally inserted by the instrumentor,
166-
// if some high-numbered counters were removed by MIR optimizations.
167-
// If so, LLVM's profiler runtime will use fewer physical counters.
163+
CoverageKind::VirtualCounter { bcb }
164+
if let Some(&id) = ids_info.phys_counter_for_node.get(&bcb) =>
165+
{
168166
let num_counters = ids_info.num_counters_after_mir_opts();
169-
assert!(
170-
num_counters as usize <= function_coverage_info.num_counters,
171-
"num_counters disagreement: query says {num_counters} but function info only has {}",
172-
function_coverage_info.num_counters
173-
);
174167

175168
let fn_name = bx.get_pgo_func_name_var(instance);
176169
let hash = bx.const_u64(function_coverage_info.function_source_hash);
@@ -182,10 +175,8 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
182175
);
183176
bx.instrprof_increment(fn_name, hash, num_counters, index);
184177
}
185-
CoverageKind::ExpressionUsed { id: _ } => {
186-
// Expression-used statements are markers that are handled by
187-
// `coverage_ids_info`, so there's nothing to codegen here.
188-
}
178+
// If a BCB doesn't have an associated physical counter, there's nothing to codegen.
179+
CoverageKind::VirtualCounter { .. } => {}
189180
CoverageKind::CondBitmapUpdate { index, decision_depth } => {
190181
let cond_bitmap = coverage_cx
191182
.try_get_mcdc_condition_bitmap(&instance, decision_depth)

compiler/rustc_codegen_llvm/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#![feature(extern_types)]
1414
#![feature(file_buffered)]
1515
#![feature(hash_raw_entry)]
16+
#![feature(if_let_guard)]
1617
#![feature(impl_trait_in_assoc_type)]
1718
#![feature(iter_intersperse)]
1819
#![feature(let_chains)]

compiler/rustc_middle/src/mir/coverage.rs

+41-22
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
33
use std::fmt::{self, Debug, Formatter};
44

5-
use rustc_index::IndexVec;
5+
use rustc_data_structures::fx::FxIndexMap;
66
use rustc_index::bit_set::DenseBitSet;
7+
use rustc_index::{Idx, IndexVec};
78
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
89
use rustc_span::Span;
910

@@ -103,23 +104,12 @@ pub enum CoverageKind {
103104
/// Should be erased before codegen (at some point after `InstrumentCoverage`).
104105
BlockMarker { id: BlockMarkerId },
105106

106-
/// Marks the point in MIR control flow represented by a coverage counter.
107+
/// Marks its enclosing basic block with the ID of the coverage graph node
108+
/// that it was part of during the `InstrumentCoverage` MIR pass.
107109
///
108-
/// This is eventually lowered to `llvm.instrprof.increment` in LLVM IR.
109-
///
110-
/// If this statement does not survive MIR optimizations, any mappings that
111-
/// refer to this counter can have those references simplified to zero.
112-
CounterIncrement { id: CounterId },
113-
114-
/// Marks the point in MIR control-flow represented by a coverage expression.
115-
///
116-
/// If this statement does not survive MIR optimizations, any mappings that
117-
/// refer to this expression can have those references simplified to zero.
118-
///
119-
/// (This is only inserted for expression IDs that are directly used by
120-
/// mappings. Intermediate expressions with no direct mappings are
121-
/// retained/zeroed based on whether they are transitively used.)
122-
ExpressionUsed { id: ExpressionId },
110+
/// During codegen, this might be lowered to `llvm.instrprof.increment` or
111+
/// to a no-op, depending on the outcome of counter-creation.
112+
VirtualCounter { bcb: BasicCoverageBlock },
123113

124114
/// Marks the point in MIR control flow represented by a evaluated condition.
125115
///
@@ -138,8 +128,7 @@ impl Debug for CoverageKind {
138128
match self {
139129
SpanMarker => write!(fmt, "SpanMarker"),
140130
BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()),
141-
CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()),
142-
ExpressionUsed { id } => write!(fmt, "ExpressionUsed({:?})", id.index()),
131+
VirtualCounter { bcb } => write!(fmt, "VirtualCounter({bcb:?})"),
143132
CondBitmapUpdate { index, decision_depth } => {
144133
write!(fmt, "CondBitmapUpdate(index={:?}, depth={:?})", index, decision_depth)
145134
}
@@ -208,11 +197,12 @@ pub struct FunctionCoverageInfo {
208197
pub function_source_hash: u64,
209198
pub body_span: Span,
210199

211-
pub num_counters: usize,
212-
pub expressions: IndexVec<ExpressionId, Expression>,
200+
/// Used in conjunction with `priority_list` to create physical counters
201+
/// and counter expressions, after MIR optimizations.
202+
pub node_flow_data: NodeFlowData<BasicCoverageBlock>,
203+
pub priority_list: Vec<BasicCoverageBlock>,
213204

214205
pub mappings: Vec<Mapping>,
215-
pub term_for_bcb: IndexVec<BasicCoverageBlock, Option<CovTerm>>,
216206

217207
pub mcdc_bitmap_bits: usize,
218208
/// The depth of the deepest decision is used to know how many
@@ -290,6 +280,10 @@ pub struct MCDCDecisionSpan {
290280
pub struct CoverageIdsInfo {
291281
pub counters_seen: DenseBitSet<CounterId>,
292282
pub zero_expressions: DenseBitSet<ExpressionId>,
283+
284+
pub phys_counter_for_node: FxIndexMap<BasicCoverageBlock, CounterId>,
285+
pub term_for_bcb: IndexVec<BasicCoverageBlock, Option<CovTerm>>,
286+
pub expressions: IndexVec<ExpressionId, Expression>,
293287
}
294288

295289
impl CoverageIdsInfo {
@@ -334,3 +328,28 @@ rustc_index::newtype_index! {
334328
const START_BCB = 0;
335329
}
336330
}
331+
332+
/// Data representing a view of some underlying graph, in which each node's
333+
/// successors have been merged into a single "supernode".
334+
///
335+
/// The resulting supernodes have no obvious meaning on their own.
336+
/// However, merging successor nodes means that a node's out-edges can all
337+
/// be combined into a single out-edge, whose flow is the same as the flow
338+
/// (execution count) of its corresponding node in the original graph.
339+
///
340+
/// With all node flows now in the original graph now represented as edge flows
341+
/// in the merged graph, it becomes possible to analyze the original node flows
342+
/// using techniques for analyzing edge flows.
343+
#[derive(Clone, Debug)]
344+
#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
345+
pub struct NodeFlowData<Node: Idx> {
346+
/// Maps each node to the supernode that contains it, indicated by some
347+
/// arbitrary "root" node that is part of that supernode.
348+
pub supernodes: IndexVec<Node, Node>,
349+
/// For each node, stores the single supernode that all of its successors
350+
/// have been merged into.
351+
///
352+
/// (Note that each node in a supernode can potentially have a _different_
353+
/// successor supernode from its peers.)
354+
pub succ_supernodes: IndexVec<Node, Node>,
355+
}

compiler/rustc_middle/src/mir/pretty.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -597,13 +597,9 @@ fn write_function_coverage_info(
597597
function_coverage_info: &coverage::FunctionCoverageInfo,
598598
w: &mut dyn io::Write,
599599
) -> io::Result<()> {
600-
let coverage::FunctionCoverageInfo { body_span, expressions, mappings, .. } =
601-
function_coverage_info;
600+
let coverage::FunctionCoverageInfo { body_span, mappings, .. } = function_coverage_info;
602601

603602
writeln!(w, "{INDENT}coverage body span: {body_span:?}")?;
604-
for (id, expression) in expressions.iter_enumerated() {
605-
writeln!(w, "{INDENT}coverage {id:?} => {expression:?};")?;
606-
}
607603
for coverage::Mapping { kind, span } in mappings {
608604
writeln!(w, "{INDENT}coverage {kind:?} => {span:?};")?;
609605
}

compiler/rustc_mir_transform/src/coverage/counters.rs

+13-45
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use std::cmp::Ordering;
22

33
use either::Either;
44
use itertools::Itertools;
5-
use rustc_data_structures::captures::Captures;
65
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
76
use rustc_data_structures::graph::DirectedGraph;
87
use rustc_index::IndexVec;
@@ -12,32 +11,33 @@ use rustc_middle::mir::coverage::{CounterId, CovTerm, Expression, ExpressionId,
1211
use crate::coverage::counters::balanced_flow::BalancedFlowGraph;
1312
use crate::coverage::counters::iter_nodes::IterNodes;
1413
use crate::coverage::counters::node_flow::{
15-
CounterTerm, NodeCounters, make_node_counters, node_flow_data_for_balanced_graph,
14+
CounterTerm, NodeCounters, NodeFlowData, node_flow_data_for_balanced_graph,
1615
};
1716
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
1817

1918
mod balanced_flow;
2019
mod iter_nodes;
21-
mod node_flow;
20+
pub(crate) mod node_flow;
2221
mod union_find;
2322

23+
/// Struct containing the results of [`make_bcb_counters`].
24+
pub(crate) struct BcbCountersData {
25+
pub(crate) node_flow_data: NodeFlowData<BasicCoverageBlock>,
26+
pub(crate) priority_list: Vec<BasicCoverageBlock>,
27+
}
28+
2429
/// Ensures that each BCB node needing a counter has one, by creating physical
2530
/// counters or counter expressions for nodes as required.
26-
pub(super) fn make_bcb_counters(
27-
graph: &CoverageGraph,
28-
bcb_needs_counter: &DenseBitSet<BasicCoverageBlock>,
29-
) -> CoverageCounters {
31+
pub(super) fn make_bcb_counters(graph: &CoverageGraph) -> BcbCountersData {
3032
// Create the derived graphs that are necessary for subsequent steps.
3133
let balanced_graph = BalancedFlowGraph::for_graph(graph, |n| !graph[n].is_out_summable);
3234
let node_flow_data = node_flow_data_for_balanced_graph(&balanced_graph);
3335

3436
// Use those graphs to determine which nodes get physical counters, and how
3537
// to compute the execution counts of other nodes from those counters.
3638
let priority_list = make_node_flow_priority_list(graph, balanced_graph);
37-
let node_counters = make_node_counters(&node_flow_data, &priority_list);
3839

39-
// Convert the counters into a form suitable for embedding into MIR.
40-
transcribe_counters(&node_counters, bcb_needs_counter)
40+
BcbCountersData { node_flow_data, priority_list }
4141
}
4242

4343
/// Arranges the nodes in `balanced_graph` into a list, such that earlier nodes
@@ -76,7 +76,7 @@ fn make_node_flow_priority_list(
7676
}
7777

7878
// Converts node counters into a form suitable for embedding into MIR.
79-
fn transcribe_counters(
79+
pub(crate) fn transcribe_counters(
8080
old: &NodeCounters<BasicCoverageBlock>,
8181
bcb_needs_counter: &DenseBitSet<BasicCoverageBlock>,
8282
) -> CoverageCounters {
@@ -131,15 +131,15 @@ fn transcribe_counters(
131131
pub(super) struct CoverageCounters {
132132
/// List of places where a counter-increment statement should be injected
133133
/// into MIR, each with its corresponding counter ID.
134-
phys_counter_for_node: FxIndexMap<BasicCoverageBlock, CounterId>,
134+
pub(crate) phys_counter_for_node: FxIndexMap<BasicCoverageBlock, CounterId>,
135135
next_counter_id: CounterId,
136136

137137
/// Coverage counters/expressions that are associated with individual BCBs.
138138
pub(crate) node_counters: IndexVec<BasicCoverageBlock, Option<CovTerm>>,
139139

140140
/// Table of expression data, associating each expression ID with its
141141
/// corresponding operator (+ or -) and its LHS/RHS operands.
142-
expressions: IndexVec<ExpressionId, Expression>,
142+
pub(crate) expressions: IndexVec<ExpressionId, Expression>,
143143
/// Remember expressions that have already been created (or simplified),
144144
/// so that we don't create unnecessary duplicates.
145145
expressions_memo: FxHashMap<Expression, CovTerm>,
@@ -190,12 +190,6 @@ impl CoverageCounters {
190190
self.make_expression(lhs, Op::Subtract, rhs_sum)
191191
}
192192

193-
pub(super) fn num_counters(&self) -> usize {
194-
let num_counters = self.phys_counter_for_node.len();
195-
assert_eq!(num_counters, self.next_counter_id.as_usize());
196-
num_counters
197-
}
198-
199193
fn set_node_counter(&mut self, bcb: BasicCoverageBlock, counter: CovTerm) -> CovTerm {
200194
let existing = self.node_counters[bcb].replace(counter);
201195
assert!(
@@ -204,30 +198,4 @@ impl CoverageCounters {
204198
);
205199
counter
206200
}
207-
208-
/// Returns an iterator over all the nodes in the coverage graph that
209-
/// should have a counter-increment statement injected into MIR, along with
210-
/// each site's corresponding counter ID.
211-
pub(super) fn counter_increment_sites(
212-
&self,
213-
) -> impl Iterator<Item = (CounterId, BasicCoverageBlock)> + Captures<'_> {
214-
self.phys_counter_for_node.iter().map(|(&site, &id)| (id, site))
215-
}
216-
217-
/// Returns an iterator over the subset of BCB nodes that have been associated
218-
/// with a counter *expression*, along with the ID of that expression.
219-
pub(super) fn bcb_nodes_with_coverage_expressions(
220-
&self,
221-
) -> impl Iterator<Item = (BasicCoverageBlock, ExpressionId)> + Captures<'_> {
222-
self.node_counters.iter_enumerated().filter_map(|(bcb, &counter)| match counter {
223-
// Yield the BCB along with its associated expression ID.
224-
Some(CovTerm::Expression(id)) => Some((bcb, id)),
225-
// This BCB is associated with a counter or nothing, so skip it.
226-
Some(CovTerm::Counter { .. } | CovTerm::Zero) | None => None,
227-
})
228-
}
229-
230-
pub(super) fn into_expressions(self) -> IndexVec<ExpressionId, Expression> {
231-
self.expressions
232-
}
233201
}

compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs

+1-24
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use rustc_data_structures::graph;
1010
use rustc_index::bit_set::DenseBitSet;
1111
use rustc_index::{Idx, IndexSlice, IndexVec};
12+
pub(crate) use rustc_middle::mir::coverage::NodeFlowData;
1213
use rustc_middle::mir::coverage::Op;
1314

1415
use crate::coverage::counters::iter_nodes::IterNodes;
@@ -17,30 +18,6 @@ use crate::coverage::counters::union_find::UnionFind;
1718
#[cfg(test)]
1819
mod tests;
1920

20-
/// Data representing a view of some underlying graph, in which each node's
21-
/// successors have been merged into a single "supernode".
22-
///
23-
/// The resulting supernodes have no obvious meaning on their own.
24-
/// However, merging successor nodes means that a node's out-edges can all
25-
/// be combined into a single out-edge, whose flow is the same as the flow
26-
/// (execution count) of its corresponding node in the original graph.
27-
///
28-
/// With all node flows now in the original graph now represented as edge flows
29-
/// in the merged graph, it becomes possible to analyze the original node flows
30-
/// using techniques for analyzing edge flows.
31-
#[derive(Debug)]
32-
pub(crate) struct NodeFlowData<Node: Idx> {
33-
/// Maps each node to the supernode that contains it, indicated by some
34-
/// arbitrary "root" node that is part of that supernode.
35-
supernodes: IndexVec<Node, Node>,
36-
/// For each node, stores the single supernode that all of its successors
37-
/// have been merged into.
38-
///
39-
/// (Note that each node in a supernode can potentially have a _different_
40-
/// successor supernode from its peers.)
41-
succ_supernodes: IndexVec<Node, Node>,
42-
}
43-
4421
/// Creates a "merged" view of an underlying graph.
4522
///
4623
/// The given graph is assumed to have [“balanced flow”](balanced-flow),

0 commit comments

Comments
 (0)