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}