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
53rustc_index::newtype_index! {
54    /// ID of a mcdc condition. Used by llvm to check mcdc coverage.
55    ///
56    /// Note for future: the max limit of 0xFFFF is probably too loose. Actually llvm does not
57    /// support decisions with too many conditions (7 and more at LLVM 18 while may be hundreds at 19)
58    /// and represents it with `int16_t`. This max value may be changed once we could
59    /// figure out an accurate limit.
60    #[derive(HashStable)]
61    #[encodable]
62    #[orderable]
63    #[max = 0xFFFF]
64    #[debug_format = "ConditionId({})"]
65    pub struct ConditionId {}
66}
67
68impl ConditionId {
69    pub const START: Self = Self::from_usize(0);
70}
71
72/// Enum that can hold a constant zero value, the ID of an physical coverage
73/// counter, or the ID of a coverage-counter expression.
74#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
75#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
76pub enum CovTerm {
77    Zero,
78    Counter(CounterId),
79    Expression(ExpressionId),
80}
81
82impl Debug for CovTerm {
83    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
84        match self {
85            Self::Zero => write!(f, "Zero"),
86            Self::Counter(id) => f.debug_tuple("Counter").field(&id.as_u32()).finish(),
87            Self::Expression(id) => f.debug_tuple("Expression").field(&id.as_u32()).finish(),
88        }
89    }
90}
91
92#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]
93pub enum CoverageKind {
94    /// Marks a span that might otherwise not be represented in MIR, so that
95    /// coverage instrumentation can associate it with its enclosing block/BCB.
96    ///
97    /// Should be erased before codegen (at some point after `InstrumentCoverage`).
98    SpanMarker,
99
100    /// Marks its enclosing basic block with an ID that can be referred to by
101    /// side data in [`CoverageInfoHi`].
102    ///
103    /// Should be erased before codegen (at some point after `InstrumentCoverage`).
104    BlockMarker { id: BlockMarkerId },
105
106    /// Marks its enclosing basic block with the ID of the coverage graph node
107    /// that it was part of during the `InstrumentCoverage` MIR pass.
108    ///
109    /// During codegen, this might be lowered to `llvm.instrprof.increment` or
110    /// to a no-op, depending on the outcome of counter-creation.
111    VirtualCounter { bcb: BasicCoverageBlock },
112
113    /// Marks the point in MIR control flow represented by a evaluated condition.
114    ///
115    /// This is eventually lowered to instruments updating mcdc temp variables.
116    CondBitmapUpdate { index: u32, decision_depth: u16 },
117
118    /// Marks the point in MIR control flow represented by a evaluated decision.
119    ///
120    /// This is eventually lowered to `llvm.instrprof.mcdc.tvbitmap.update` in LLVM IR.
121    TestVectorBitmapUpdate { bitmap_idx: u32, decision_depth: u16 },
122}
123
124impl Debug for CoverageKind {
125    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
126        use CoverageKind::*;
127        match self {
128            SpanMarker => write!(fmt, "SpanMarker"),
129            BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()),
130            VirtualCounter { bcb } => write!(fmt, "VirtualCounter({bcb:?})"),
131            CondBitmapUpdate { index, decision_depth } => {
132                write!(fmt, "CondBitmapUpdate(index={:?}, depth={:?})", index, decision_depth)
133            }
134            TestVectorBitmapUpdate { bitmap_idx, decision_depth } => {
135                write!(fmt, "TestVectorUpdate({:?}, depth={:?})", bitmap_idx, decision_depth)
136            }
137        }
138    }
139}
140
141#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)]
142#[derive(TyEncodable, TyDecodable)]
143pub enum Op {
144    Subtract,
145    Add,
146}
147
148impl Op {
149    pub fn is_add(&self) -> bool {
150        matches!(self, Self::Add)
151    }
152
153    pub fn is_subtract(&self) -> bool {
154        matches!(self, Self::Subtract)
155    }
156}
157
158#[derive(Clone, Debug, PartialEq, Eq)]
159#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
160pub struct Expression {
161    pub lhs: CovTerm,
162    pub op: Op,
163    pub rhs: CovTerm,
164}
165
166#[derive(Clone, Debug)]
167#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
168pub enum MappingKind {
169    /// Associates a normal region of code with a counter/expression/zero.
170    Code { bcb: BasicCoverageBlock },
171    /// Associates a branch region with separate counters for true and false.
172    Branch { true_bcb: BasicCoverageBlock, false_bcb: BasicCoverageBlock },
173    /// Associates a branch region with separate counters for true and false.
174    MCDCBranch {
175        true_bcb: BasicCoverageBlock,
176        false_bcb: BasicCoverageBlock,
177        mcdc_params: ConditionInfo,
178    },
179    /// Associates a decision region with a bitmap and number of conditions.
180    MCDCDecision(DecisionInfo),
181}
182
183#[derive(Clone, Debug)]
184#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
185pub struct Mapping {
186    pub kind: MappingKind,
187    pub span: Span,
188}
189
190/// Stores per-function coverage information attached to a `mir::Body`,
191/// to be used in conjunction with the individual coverage statements injected
192/// into the function's basic blocks.
193#[derive(Clone, Debug)]
194#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
195pub struct FunctionCoverageInfo {
196    pub function_source_hash: u64,
197
198    /// Used in conjunction with `priority_list` to create physical counters
199    /// and counter expressions, after MIR optimizations.
200    pub node_flow_data: NodeFlowData<BasicCoverageBlock>,
201    pub priority_list: Vec<BasicCoverageBlock>,
202
203    pub mappings: Vec<Mapping>,
204
205    pub mcdc_bitmap_bits: usize,
206    /// The depth of the deepest decision is used to know how many
207    /// temp condbitmaps should be allocated for the function.
208    pub mcdc_num_condition_bitmaps: usize,
209}
210
211/// Coverage information for a function, recorded during MIR building and
212/// attached to the corresponding `mir::Body`. Used by the `InstrumentCoverage`
213/// MIR pass.
214///
215/// ("Hi" indicates that this is "high-level" information collected at the
216/// THIR/MIR boundary, before the MIR-based coverage instrumentation pass.)
217#[derive(Clone, Debug)]
218#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
219pub struct CoverageInfoHi {
220    /// 1 more than the highest-numbered [`CoverageKind::BlockMarker`] that was
221    /// injected into the MIR body. This makes it possible to allocate per-ID
222    /// data structures without having to scan the entire body first.
223    pub num_block_markers: usize,
224    pub branch_spans: Vec<BranchSpan>,
225    /// Branch spans generated by mcdc. Because of some limits mcdc builder give up generating
226    /// decisions including them so that they are handled as normal branch spans.
227    pub mcdc_degraded_branch_spans: Vec<MCDCBranchSpan>,
228    pub mcdc_spans: Vec<(MCDCDecisionSpan, Vec<MCDCBranchSpan>)>,
229}
230
231#[derive(Clone, Debug)]
232#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
233pub struct BranchSpan {
234    pub span: Span,
235    pub true_marker: BlockMarkerId,
236    pub false_marker: BlockMarkerId,
237}
238
239#[derive(Copy, Clone, Debug)]
240#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
241pub struct ConditionInfo {
242    pub condition_id: ConditionId,
243    pub true_next_id: Option<ConditionId>,
244    pub false_next_id: Option<ConditionId>,
245}
246
247#[derive(Clone, Debug)]
248#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
249pub struct MCDCBranchSpan {
250    pub span: Span,
251    pub condition_info: ConditionInfo,
252    pub true_marker: BlockMarkerId,
253    pub false_marker: BlockMarkerId,
254}
255
256#[derive(Copy, Clone, Debug)]
257#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
258pub struct DecisionInfo {
259    pub bitmap_idx: u32,
260    pub num_conditions: u16,
261}
262
263#[derive(Clone, Debug)]
264#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
265pub struct MCDCDecisionSpan {
266    pub span: Span,
267    pub end_markers: Vec<BlockMarkerId>,
268    pub decision_depth: u16,
269    pub num_conditions: usize,
270}
271
272/// Contains information needed during codegen, obtained by inspecting the
273/// function's MIR after MIR optimizations.
274///
275/// Returned by the `coverage_ids_info` query.
276#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable)]
277pub struct CoverageIdsInfo {
278    pub num_counters: u32,
279    pub phys_counter_for_node: FxIndexMap<BasicCoverageBlock, CounterId>,
280    pub term_for_bcb: IndexVec<BasicCoverageBlock, Option<CovTerm>>,
281    pub expressions: IndexVec<ExpressionId, Expression>,
282}
283
284rustc_index::newtype_index! {
285    /// During the `InstrumentCoverage` MIR pass, a BCB is a node in the
286    /// "coverage graph", which is a refinement of the MIR control-flow graph
287    /// that merges or omits some blocks that aren't relevant to coverage.
288    ///
289    /// After that pass is complete, the coverage graph no longer exists, so a
290    /// BCB is effectively an opaque ID.
291    #[derive(HashStable)]
292    #[encodable]
293    #[orderable]
294    #[debug_format = "bcb{}"]
295    pub struct BasicCoverageBlock {
296        const START_BCB = 0;
297    }
298}
299
300/// Data representing a view of some underlying graph, in which each node's
301/// successors have been merged into a single "supernode".
302///
303/// The resulting supernodes have no obvious meaning on their own.
304/// However, merging successor nodes means that a node's out-edges can all
305/// be combined into a single out-edge, whose flow is the same as the flow
306/// (execution count) of its corresponding node in the original graph.
307///
308/// With all node flows now in the original graph now represented as edge flows
309/// in the merged graph, it becomes possible to analyze the original node flows
310/// using techniques for analyzing edge flows.
311#[derive(Clone, Debug)]
312#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
313pub struct NodeFlowData<Node: Idx> {
314    /// Maps each node to the supernode that contains it, indicated by some
315    /// arbitrary "root" node that is part of that supernode.
316    pub supernodes: IndexVec<Node, Node>,
317    /// For each node, stores the single supernode that all of its successors
318    /// have been merged into.
319    ///
320    /// (Note that each node in a supernode can potentially have a _different_
321    /// successor supernode from its peers.)
322    pub succ_supernodes: IndexVec<Node, Node>,
323}