rustc_mir_transform/coverage/
mod.rs

1pub(super) mod query;
2
3mod counters;
4mod graph;
5mod mappings;
6mod spans;
7#[cfg(test)]
8mod tests;
9mod unexpand;
10
11use rustc_hir as hir;
12use rustc_hir::intravisit::{Visitor, walk_expr};
13use rustc_middle::hir::map::Map;
14use rustc_middle::hir::nested_filter;
15use rustc_middle::mir::coverage::{
16    CoverageKind, DecisionInfo, FunctionCoverageInfo, Mapping, MappingKind,
17};
18use rustc_middle::mir::{self, BasicBlock, Statement, StatementKind, TerminatorKind};
19use rustc_middle::ty::TyCtxt;
20use rustc_span::Span;
21use rustc_span::def_id::LocalDefId;
22use tracing::{debug, debug_span, trace};
23
24use crate::coverage::counters::BcbCountersData;
25use crate::coverage::graph::CoverageGraph;
26use crate::coverage::mappings::ExtractedMappings;
27
28/// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected
29/// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen
30/// to construct the coverage map.
31pub(super) struct InstrumentCoverage;
32
33impl<'tcx> crate::MirPass<'tcx> for InstrumentCoverage {
34    fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
35        sess.instrument_coverage()
36    }
37
38    fn run_pass(&self, tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) {
39        let mir_source = mir_body.source;
40
41        // This pass runs after MIR promotion, but before promoted MIR starts to
42        // be transformed, so it should never see promoted MIR.
43        assert!(mir_source.promoted.is_none());
44
45        let def_id = mir_source.def_id().expect_local();
46
47        if !tcx.is_eligible_for_coverage(def_id) {
48            trace!("InstrumentCoverage skipped for {def_id:?} (not eligible)");
49            return;
50        }
51
52        // An otherwise-eligible function is still skipped if its start block
53        // is known to be unreachable.
54        match mir_body.basic_blocks[mir::START_BLOCK].terminator().kind {
55            TerminatorKind::Unreachable => {
56                trace!("InstrumentCoverage skipped for unreachable `START_BLOCK`");
57                return;
58            }
59            _ => {}
60        }
61
62        instrument_function_for_coverage(tcx, mir_body);
63    }
64
65    fn is_required(&self) -> bool {
66        false
67    }
68}
69
70fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) {
71    let def_id = mir_body.source.def_id();
72    let _span = debug_span!("instrument_function_for_coverage", ?def_id).entered();
73
74    let hir_info = extract_hir_info(tcx, def_id.expect_local());
75
76    // Build the coverage graph, which is a simplified view of the MIR control-flow
77    // graph that ignores some details not relevant to coverage instrumentation.
78    let graph = CoverageGraph::from_mir(mir_body);
79
80    ////////////////////////////////////////////////////
81    // Extract coverage spans and other mapping info from MIR.
82    let extracted_mappings =
83        mappings::extract_all_mapping_info_from_mir(tcx, mir_body, &hir_info, &graph);
84
85    let mappings = create_mappings(&extracted_mappings);
86    if mappings.is_empty() {
87        // No spans could be converted into valid mappings, so skip this function.
88        debug!("no spans could be converted into valid mappings; skipping");
89        return;
90    }
91
92    // Use the coverage graph to prepare intermediate data that will eventually
93    // be used to assign physical counters and counter expressions to points in
94    // the control-flow graph
95    let BcbCountersData { node_flow_data, priority_list } =
96        counters::prepare_bcb_counters_data(&graph);
97
98    // Inject coverage statements into MIR.
99    inject_coverage_statements(mir_body, &graph);
100    inject_mcdc_statements(mir_body, &graph, &extracted_mappings);
101
102    let mcdc_num_condition_bitmaps = extracted_mappings
103        .mcdc_mappings
104        .iter()
105        .map(|&(mappings::MCDCDecision { decision_depth, .. }, _)| decision_depth)
106        .max()
107        .map_or(0, |max| usize::from(max) + 1);
108
109    mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
110        function_source_hash: hir_info.function_source_hash,
111        body_span: hir_info.body_span,
112
113        node_flow_data,
114        priority_list,
115
116        mappings,
117
118        mcdc_bitmap_bits: extracted_mappings.mcdc_bitmap_bits,
119        mcdc_num_condition_bitmaps,
120    }));
121}
122
123/// For each coverage span extracted from MIR, create a corresponding mapping.
124///
125/// FIXME(Zalathar): This used to be where BCBs in the extracted mappings were
126/// resolved to a `CovTerm`. But that is now handled elsewhere, so this
127/// function can potentially be simplified even further.
128fn create_mappings(extracted_mappings: &ExtractedMappings) -> Vec<Mapping> {
129    // Fully destructure the mappings struct to make sure we don't miss any kinds.
130    let ExtractedMappings {
131        code_mappings,
132        branch_pairs,
133        mcdc_bitmap_bits: _,
134        mcdc_degraded_branches,
135        mcdc_mappings,
136    } = extracted_mappings;
137    let mut mappings = Vec::new();
138
139    mappings.extend(code_mappings.iter().map(
140        // Ordinary code mappings are the simplest kind.
141        |&mappings::CodeMapping { span, bcb }| {
142            let kind = MappingKind::Code { bcb };
143            Mapping { kind, span }
144        },
145    ));
146
147    mappings.extend(branch_pairs.iter().map(
148        |&mappings::BranchPair { span, true_bcb, false_bcb }| {
149            let kind = MappingKind::Branch { true_bcb, false_bcb };
150            Mapping { kind, span }
151        },
152    ));
153
154    // MCDC branch mappings are appended with their decisions in case decisions were ignored.
155    mappings.extend(mcdc_degraded_branches.iter().map(
156        |&mappings::MCDCBranch {
157             span,
158             true_bcb,
159             false_bcb,
160             condition_info: _,
161             true_index: _,
162             false_index: _,
163         }| { Mapping { kind: MappingKind::Branch { true_bcb, false_bcb }, span } },
164    ));
165
166    for (decision, branches) in mcdc_mappings {
167        // FIXME(#134497): Previously it was possible for some of these branch
168        // conversions to fail, in which case the remaining branches in the
169        // decision would be degraded to plain `MappingKind::Branch`.
170        // The changes in #134497 made that failure impossible, because the
171        // fallible step was deferred to codegen. But the corresponding code
172        // in codegen wasn't updated to detect the need for a degrade step.
173        let conditions = branches
174            .into_iter()
175            .map(
176                |&mappings::MCDCBranch {
177                     span,
178                     true_bcb,
179                     false_bcb,
180                     condition_info,
181                     true_index: _,
182                     false_index: _,
183                 }| {
184                    Mapping {
185                        kind: MappingKind::MCDCBranch {
186                            true_bcb,
187                            false_bcb,
188                            mcdc_params: condition_info,
189                        },
190                        span,
191                    }
192                },
193            )
194            .collect::<Vec<_>>();
195
196        // LLVM requires end index for counter mapping regions.
197        let kind = MappingKind::MCDCDecision(DecisionInfo {
198            bitmap_idx: (decision.bitmap_idx + decision.num_test_vectors) as u32,
199            num_conditions: u16::try_from(conditions.len()).unwrap(),
200        });
201        let span = decision.span;
202        mappings.extend(std::iter::once(Mapping { kind, span }).chain(conditions.into_iter()));
203    }
204
205    mappings
206}
207
208/// Inject any necessary coverage statements into MIR, so that they influence codegen.
209fn inject_coverage_statements<'tcx>(mir_body: &mut mir::Body<'tcx>, graph: &CoverageGraph) {
210    for (bcb, data) in graph.iter_enumerated() {
211        let target_bb = data.leader_bb();
212        inject_statement(mir_body, CoverageKind::VirtualCounter { bcb }, target_bb);
213    }
214}
215
216/// For each conditions inject statements to update condition bitmap after it has been evaluated.
217/// For each decision inject statements to update test vector bitmap after it has been evaluated.
218fn inject_mcdc_statements<'tcx>(
219    mir_body: &mut mir::Body<'tcx>,
220    graph: &CoverageGraph,
221    extracted_mappings: &ExtractedMappings,
222) {
223    for (decision, conditions) in &extracted_mappings.mcdc_mappings {
224        // Inject test vector update first because `inject_statement` always insert new statement at head.
225        for &end in &decision.end_bcbs {
226            let end_bb = graph[end].leader_bb();
227            inject_statement(
228                mir_body,
229                CoverageKind::TestVectorBitmapUpdate {
230                    bitmap_idx: decision.bitmap_idx as u32,
231                    decision_depth: decision.decision_depth,
232                },
233                end_bb,
234            );
235        }
236
237        for &mappings::MCDCBranch {
238            span: _,
239            true_bcb,
240            false_bcb,
241            condition_info: _,
242            true_index,
243            false_index,
244        } in conditions
245        {
246            for (index, bcb) in [(false_index, false_bcb), (true_index, true_bcb)] {
247                let bb = graph[bcb].leader_bb();
248                inject_statement(
249                    mir_body,
250                    CoverageKind::CondBitmapUpdate {
251                        index: index as u32,
252                        decision_depth: decision.decision_depth,
253                    },
254                    bb,
255                );
256            }
257        }
258    }
259}
260
261fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb: BasicBlock) {
262    debug!("  injecting statement {counter_kind:?} for {bb:?}");
263    let data = &mut mir_body[bb];
264    let source_info = data.terminator().source_info;
265    let statement = Statement { source_info, kind: StatementKind::Coverage(counter_kind) };
266    data.statements.insert(0, statement);
267}
268
269/// Function information extracted from HIR by the coverage instrumentor.
270#[derive(Debug)]
271struct ExtractedHirInfo {
272    function_source_hash: u64,
273    is_async_fn: bool,
274    /// The span of the function's signature, extended to the start of `body_span`.
275    /// Must have the same context and filename as the body span.
276    fn_sig_span_extended: Option<Span>,
277    body_span: Span,
278    /// "Holes" are regions within the body span that should not be included in
279    /// coverage spans for this function (e.g. closures and nested items).
280    hole_spans: Vec<Span>,
281}
282
283fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHirInfo {
284    // FIXME(#79625): Consider improving MIR to provide the information needed, to avoid going back
285    // to HIR for it.
286
287    // HACK: For synthetic MIR bodies (async closures), use the def id of the HIR body.
288    if tcx.is_synthetic_mir(def_id) {
289        return extract_hir_info(tcx, tcx.local_parent(def_id));
290    }
291
292    let hir_node = tcx.hir_node_by_def_id(def_id);
293    let fn_body_id = hir_node.body_id().expect("HIR node is a function with body");
294    let hir_body = tcx.hir().body(fn_body_id);
295
296    let maybe_fn_sig = hir_node.fn_sig();
297    let is_async_fn = maybe_fn_sig.is_some_and(|fn_sig| fn_sig.header.is_async());
298
299    let mut body_span = hir_body.value.span;
300
301    use hir::{Closure, Expr, ExprKind, Node};
302    // Unexpand a closure's body span back to the context of its declaration.
303    // This helps with closure bodies that consist of just a single bang-macro,
304    // and also with closure bodies produced by async desugaring.
305    if let Node::Expr(&Expr { kind: ExprKind::Closure(&Closure { fn_decl_span, .. }), .. }) =
306        hir_node
307    {
308        body_span = body_span.find_ancestor_in_same_ctxt(fn_decl_span).unwrap_or(body_span);
309    }
310
311    // The actual signature span is only used if it has the same context and
312    // filename as the body, and precedes the body.
313    let fn_sig_span_extended = maybe_fn_sig
314        .map(|fn_sig| fn_sig.span)
315        .filter(|&fn_sig_span| {
316            let source_map = tcx.sess.source_map();
317            let file_idx = |span: Span| source_map.lookup_source_file_idx(span.lo());
318
319            fn_sig_span.eq_ctxt(body_span)
320                && fn_sig_span.hi() <= body_span.lo()
321                && file_idx(fn_sig_span) == file_idx(body_span)
322        })
323        // If so, extend it to the start of the body span.
324        .map(|fn_sig_span| fn_sig_span.with_hi(body_span.lo()));
325
326    let function_source_hash = hash_mir_source(tcx, hir_body);
327
328    let hole_spans = extract_hole_spans_from_hir(tcx, body_span, hir_body);
329
330    ExtractedHirInfo {
331        function_source_hash,
332        is_async_fn,
333        fn_sig_span_extended,
334        body_span,
335        hole_spans,
336    }
337}
338
339fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx hir::Body<'tcx>) -> u64 {
340    // FIXME(cjgillot) Stop hashing HIR manually here.
341    let owner = hir_body.id().hir_id.owner;
342    tcx.hir_owner_nodes(owner).opt_hash_including_bodies.unwrap().to_smaller_hash().as_u64()
343}
344
345fn extract_hole_spans_from_hir<'tcx>(
346    tcx: TyCtxt<'tcx>,
347    body_span: Span, // Usually `hir_body.value.span`, but not always
348    hir_body: &hir::Body<'tcx>,
349) -> Vec<Span> {
350    struct HolesVisitor<'hir, F> {
351        hir: Map<'hir>,
352        visit_hole_span: F,
353    }
354
355    impl<'hir, F: FnMut(Span)> Visitor<'hir> for HolesVisitor<'hir, F> {
356        /// - We need `NestedFilter::INTRA = true` so that `visit_item` will be called.
357        /// - Bodies of nested items don't actually get visited, because of the
358        ///   `visit_item` override.
359        /// - For nested bodies that are not part of an item, we do want to visit any
360        ///   items contained within them.
361        type NestedFilter = nested_filter::All;
362
363        fn nested_visit_map(&mut self) -> Self::Map {
364            self.hir
365        }
366
367        fn visit_item(&mut self, item: &'hir hir::Item<'hir>) {
368            (self.visit_hole_span)(item.span);
369            // Having visited this item, we don't care about its children,
370            // so don't call `walk_item`.
371        }
372
373        // We override `visit_expr` instead of the more specific expression
374        // visitors, so that we have direct access to the expression span.
375        fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
376            match expr.kind {
377                hir::ExprKind::Closure(_) | hir::ExprKind::ConstBlock(_) => {
378                    (self.visit_hole_span)(expr.span);
379                    // Having visited this expression, we don't care about its
380                    // children, so don't call `walk_expr`.
381                }
382
383                // For other expressions, recursively visit as normal.
384                _ => walk_expr(self, expr),
385            }
386        }
387    }
388
389    let mut hole_spans = vec![];
390    let mut visitor = HolesVisitor {
391        hir: tcx.hir(),
392        visit_hole_span: |hole_span| {
393            // Discard any holes that aren't directly visible within the body span.
394            if body_span.contains(hole_span) && body_span.eq_ctxt(hole_span) {
395                hole_spans.push(hole_span);
396            }
397        },
398    };
399
400    visitor.visit_body(hir_body);
401    hole_spans
402}