rustc_middle/mir/coverage.rs
1//! Metadata from source code coverage analysis and instrumentation.
2
3use std::fmt::{self, Debug, Formatter};
4
5use rustc_data_structures::fx::FxIndexMap;
6use rustc_index::{Idx, IndexVec};
7use rustc_macros::{HashStable, TyDecodable, TyEncodable};
8use rustc_span::Span;
9
10rustc_index::newtype_index! {
11 /// Used by [`CoverageKind::BlockMarker`] to mark blocks during THIR-to-MIR
12 /// lowering, so that those blocks can be identified later.
13 #[derive(HashStable)]
14 #[encodable]
15 #[debug_format = "BlockMarkerId({})"]
16 pub struct BlockMarkerId {}
17}
18
19rustc_index::newtype_index! {
20 /// ID of a coverage counter. Values ascend from 0.
21 ///
22 /// Before MIR inlining, counter IDs are local to their enclosing function.
23 /// After MIR inlining, coverage statements may have been inlined into
24 /// another function, so use the statement's source-scope to find which
25 /// function/instance its IDs are meaningful for.
26 ///
27 /// Note that LLVM handles counter IDs as `uint32_t`, so there is no need
28 /// to use a larger representation on the Rust side.
29 #[derive(HashStable)]
30 #[encodable]
31 #[orderable]
32 #[debug_format = "CounterId({})"]
33 pub struct CounterId {}
34}
35
36rustc_index::newtype_index! {
37 /// ID of a coverage-counter expression. Values ascend from 0.
38 ///
39 /// Before MIR inlining, expression IDs are local to their enclosing function.
40 /// After MIR inlining, coverage statements may have been inlined into
41 /// another function, so use the statement's source-scope to find which
42 /// function/instance its IDs are meaningful for.
43 ///
44 /// Note that LLVM handles expression IDs as `uint32_t`, so there is no need
45 /// to use a larger representation on the Rust side.
46 #[derive(HashStable)]
47 #[encodable]
48 #[orderable]
49 #[debug_format = "ExpressionId({})"]
50 pub struct ExpressionId {}
51}
52
53/// Enum that can hold a constant zero value, the ID of an physical coverage
54/// counter, or the ID of a coverage-counter expression.
55#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
56#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
57pub enum CovTerm {
58 Zero,
59 Counter(CounterId),
60 Expression(ExpressionId),
61}
62
63impl Debug for CovTerm {
64 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
65 match self {
66 Self::Zero => write!(f, "Zero"),
67 Self::Counter(id) => f.debug_tuple("Counter").field(&id.as_u32()).finish(),
68 Self::Expression(id) => f.debug_tuple("Expression").field(&id.as_u32()).finish(),
69 }
70 }
71}
72
73#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]
74pub enum CoverageKind {
75 /// Marks a span that might otherwise not be represented in MIR, so that
76 /// coverage instrumentation can associate it with its enclosing block/BCB.
77 ///
78 /// Should be erased before codegen (at some point after `InstrumentCoverage`).
79 SpanMarker,
80
81 /// Marks its enclosing basic block with an ID that can be referred to by
82 /// side data in [`CoverageInfoHi`].
83 ///
84 /// Should be erased before codegen (at some point after `InstrumentCoverage`).
85 BlockMarker { id: BlockMarkerId },
86
87 /// Marks its enclosing basic block with the ID of the coverage graph node
88 /// that it was part of during the `InstrumentCoverage` MIR pass.
89 ///
90 /// During codegen, this might be lowered to `llvm.instrprof.increment` or
91 /// to a no-op, depending on the outcome of counter-creation.
92 VirtualCounter { bcb: BasicCoverageBlock },
93}
94
95impl Debug for CoverageKind {
96 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
97 use CoverageKind::*;
98 match self {
99 SpanMarker => write!(fmt, "SpanMarker"),
100 BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()),
101 VirtualCounter { bcb } => write!(fmt, "VirtualCounter({bcb:?})"),
102 }
103 }
104}
105
106#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)]
107#[derive(TyEncodable, TyDecodable)]
108pub enum Op {
109 Subtract,
110 Add,
111}
112
113impl Op {
114 pub fn is_add(&self) -> bool {
115 matches!(self, Self::Add)
116 }
117
118 pub fn is_subtract(&self) -> bool {
119 matches!(self, Self::Subtract)
120 }
121}
122
123#[derive(Clone, Debug, PartialEq, Eq)]
124#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
125pub struct Expression {
126 pub lhs: CovTerm,
127 pub op: Op,
128 pub rhs: CovTerm,
129}
130
131#[derive(Clone, Debug)]
132#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
133pub enum MappingKind {
134 /// Associates a normal region of code with a counter/expression/zero.
135 Code { bcb: BasicCoverageBlock },
136 /// Associates a branch region with separate counters for true and false.
137 Branch { true_bcb: BasicCoverageBlock, false_bcb: BasicCoverageBlock },
138}
139
140#[derive(Clone, Debug)]
141#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
142pub struct Mapping {
143 pub kind: MappingKind,
144 pub span: Span,
145}
146
147/// Stores per-function coverage information attached to a `mir::Body`,
148/// to be used in conjunction with the individual coverage statements injected
149/// into the function's basic blocks.
150#[derive(Clone, Debug)]
151#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
152pub struct FunctionCoverageInfo {
153 pub function_source_hash: u64,
154
155 /// Used in conjunction with `priority_list` to create physical counters
156 /// and counter expressions, after MIR optimizations.
157 pub node_flow_data: NodeFlowData<BasicCoverageBlock>,
158 pub priority_list: Vec<BasicCoverageBlock>,
159
160 pub mappings: Vec<Mapping>,
161}
162
163/// Coverage information for a function, recorded during MIR building and
164/// attached to the corresponding `mir::Body`. Used by the `InstrumentCoverage`
165/// MIR pass.
166///
167/// ("Hi" indicates that this is "high-level" information collected at the
168/// THIR/MIR boundary, before the MIR-based coverage instrumentation pass.)
169#[derive(Clone, Debug)]
170#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
171pub struct CoverageInfoHi {
172 /// 1 more than the highest-numbered [`CoverageKind::BlockMarker`] that was
173 /// injected into the MIR body. This makes it possible to allocate per-ID
174 /// data structures without having to scan the entire body first.
175 pub num_block_markers: usize,
176 pub branch_spans: Vec<BranchSpan>,
177}
178
179#[derive(Clone, Debug)]
180#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
181pub struct BranchSpan {
182 pub span: Span,
183 pub true_marker: BlockMarkerId,
184 pub false_marker: BlockMarkerId,
185}
186
187/// Contains information needed during codegen, obtained by inspecting the
188/// function's MIR after MIR optimizations.
189///
190/// Returned by the `coverage_ids_info` query.
191#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable)]
192pub struct CoverageIdsInfo {
193 pub num_counters: u32,
194 pub phys_counter_for_node: FxIndexMap<BasicCoverageBlock, CounterId>,
195 pub term_for_bcb: IndexVec<BasicCoverageBlock, Option<CovTerm>>,
196 pub expressions: IndexVec<ExpressionId, Expression>,
197}
198
199rustc_index::newtype_index! {
200 /// During the `InstrumentCoverage` MIR pass, a BCB is a node in the
201 /// "coverage graph", which is a refinement of the MIR control-flow graph
202 /// that merges or omits some blocks that aren't relevant to coverage.
203 ///
204 /// After that pass is complete, the coverage graph no longer exists, so a
205 /// BCB is effectively an opaque ID.
206 #[derive(HashStable)]
207 #[encodable]
208 #[orderable]
209 #[debug_format = "bcb{}"]
210 pub struct BasicCoverageBlock {
211 const START_BCB = 0;
212 }
213}
214
215/// Data representing a view of some underlying graph, in which each node's
216/// successors have been merged into a single "supernode".
217///
218/// The resulting supernodes have no obvious meaning on their own.
219/// However, merging successor nodes means that a node's out-edges can all
220/// be combined into a single out-edge, whose flow is the same as the flow
221/// (execution count) of its corresponding node in the original graph.
222///
223/// With all node flows now in the original graph now represented as edge flows
224/// in the merged graph, it becomes possible to analyze the original node flows
225/// using techniques for analyzing edge flows.
226#[derive(Clone, Debug)]
227#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
228pub struct NodeFlowData<Node: Idx> {
229 /// Maps each node to the supernode that contains it, indicated by some
230 /// arbitrary "root" node that is part of that supernode.
231 pub supernodes: IndexVec<Node, Node>,
232 /// For each node, stores the single supernode that all of its successors
233 /// have been merged into.
234 ///
235 /// (Note that each node in a supernode can potentially have a _different_
236 /// successor supernode from its peers.)
237 pub succ_supernodes: IndexVec<Node, Node>,
238}