Skip to content

Commit 9edf5b8

Browse files
committed
coverage. Record branch blocks for match
1 parent c5de414 commit 9edf5b8

File tree

2 files changed

+61
-2
lines changed

2 files changed

+61
-2
lines changed

compiler/rustc_mir_build/src/build/coverageinfo.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
use std::assert_matches::assert_matches;
22
use std::collections::hash_map::Entry;
3+
use std::collections::BTreeMap;
34

45
use rustc_data_structures::fx::FxHashMap;
56
use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind};
67
use rustc_middle::mir::{self, BasicBlock, UnOp};
78
use rustc_middle::thir::{ExprId, ExprKind, Thir};
89
use rustc_middle::ty::TyCtxt;
910
use rustc_span::def_id::LocalDefId;
11+
use rustc_span::Span;
1012

1113
use crate::build::Builder;
1214

@@ -16,6 +18,7 @@ pub(crate) struct BranchInfoBuilder {
1618

1719
num_block_markers: usize,
1820
branch_spans: Vec<BranchSpan>,
21+
pattern_match_branches: BTreeMap<Span, (Vec<BasicBlock>, Vec<BasicBlock>)>,
1922
}
2023

2124
#[derive(Clone, Copy)]
@@ -33,7 +36,12 @@ impl BranchInfoBuilder {
3336
/// is enabled and `def_id` represents a function that is eligible for coverage.
3437
pub(crate) fn new_if_enabled(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<Self> {
3538
if tcx.sess.instrument_coverage_branch() && tcx.is_eligible_for_coverage(def_id) {
36-
Some(Self { nots: FxHashMap::default(), num_block_markers: 0, branch_spans: vec![] })
39+
Some(Self {
40+
nots: FxHashMap::default(),
41+
num_block_markers: 0,
42+
branch_spans: vec![],
43+
pattern_match_branches: BTreeMap::new(),
44+
})
3745
} else {
3846
None
3947
}
@@ -86,7 +94,7 @@ impl BranchInfoBuilder {
8694
}
8795

8896
pub(crate) fn into_done(self) -> Option<Box<mir::coverage::BranchInfo>> {
89-
let Self { nots: _, num_block_markers, branch_spans } = self;
97+
let Self { nots: _, num_block_markers, branch_spans, .. } = self;
9098

9199
if num_block_markers == 0 {
92100
assert!(branch_spans.is_empty());
@@ -143,4 +151,29 @@ impl Builder<'_, '_> {
143151
false_marker,
144152
});
145153
}
154+
155+
#[allow(unused)]
156+
pub(crate) fn visit_pattern_match_branches(
157+
&mut self,
158+
targets: impl Iterator<Item = (Span, BasicBlock)>,
159+
otherwise_block: BasicBlock,
160+
) {
161+
// TODO! Add get_block_marker_id here to transform BasicBlock to BlockMarkerId then `pattern_match_branches` could store BlockMarkerId.
162+
let targets = targets.collect::<Vec<_>>();
163+
164+
let Some(branch_info) = self.coverage_branch_info.as_mut() else { return };
165+
for (span, true_blk) in &targets {
166+
let (true_blks, false_blks) =
167+
branch_info.pattern_match_branches.entry(*span).or_insert_with(|| (vec![], vec![]));
168+
// SomeEnum::A | SomeEnum::B would be lowered to something like switchInt(_1) -> [ 0: bb1, 1: bb3, otherwise: otherwise_block ]
169+
// Thus bb3 and otherwise_block both are false blocks for SomeEnum::A.
170+
true_blks.push(*true_blk);
171+
false_blks.extend(
172+
targets
173+
.iter()
174+
.filter_map(|(_, blk)| (blk != true_blk).then_some(*blk))
175+
.chain(std::iter::once(otherwise_block)),
176+
);
177+
}
178+
}
146179
}

compiler/rustc_mir_build/src/build/matches/mod.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ mod test;
2828
mod util;
2929

3030
use std::borrow::Borrow;
31+
use std::collections::BTreeMap;
3132
use std::mem;
3233

3334
/// Arguments to [`Builder::then_else_break_inner`] that are usually forwarded
@@ -1855,6 +1856,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
18551856
// Extract the match-pair from the highest priority candidate and build a test from it.
18561857
let (match_place, test) = self.pick_test(candidates);
18571858

1859+
// Record spans of targets. `test.span` only represents `Enum::A` out of `Enum::A | Enum::B`
1860+
// Span in extra data to identify associated candidate later.
1861+
let mut coverage_targets: BTreeMap<_, _> = candidates
1862+
.iter()
1863+
.filter_map(|candidate| {
1864+
candidate
1865+
.match_pairs
1866+
.first()
1867+
.map(|match_pair| (candidate.extra_data.span, (match_pair.pattern.span, None)))
1868+
})
1869+
.collect();
1870+
18581871
// For each of the N possible test outcomes, build the vector of candidates that applies if
18591872
// the test has that particular outcome.
18601873
let (remaining_candidates, target_candidates) =
@@ -1881,6 +1894,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
18811894
.into_iter()
18821895
.map(|(branch, mut candidates)| {
18831896
let candidate_start = self.cfg.start_new_block();
1897+
// After `match_candidates` above the candidate replace its `matched_pairs` with sub patterns.
1898+
// But with luck the extra_data.span is unchanged. So we can use it to find the associated target span and update its target blocks.
1899+
coverage_targets
1900+
.get_mut(&candidates.first().expect("must be non-empty").extra_data.span)
1901+
.expect("must exist")
1902+
.1 = Some(candidate_start);
18841903
self.match_candidates(
18851904
span,
18861905
scrutinee_span,
@@ -1892,6 +1911,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
18921911
})
18931912
.collect();
18941913

1914+
self.visit_pattern_match_branches(
1915+
coverage_targets
1916+
.into_values()
1917+
.filter_map(|(span, target)| target.map(|blk| (span, blk))),
1918+
remainder_start,
1919+
);
1920+
18951921
// Perform the test, branching to one of N blocks.
18961922
self.perform_test(
18971923
span,

0 commit comments

Comments
 (0)