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}