Skip to content

Commit ba87e5b

Browse files
committed
coverage: Split off mappings.rs from spans.rs and from_mir.rs
1 parent 7d1c6af commit ba87e5b

File tree

4 files changed

+283
-275
lines changed

4 files changed

+283
-275
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
use std::collections::BTreeSet;
2+
3+
use rustc_data_structures::graph::DirectedGraph;
4+
use rustc_index::bit_set::BitSet;
5+
use rustc_middle::mir::coverage::{
6+
BlockMarkerId, BranchSpan, ConditionInfo, CoverageKind, MCDCBranchSpan, MCDCDecisionSpan,
7+
};
8+
use rustc_middle::mir::{self, BasicBlock, StatementKind};
9+
use rustc_span::Span;
10+
11+
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
12+
use crate::coverage::spans::{
13+
extract_refined_covspans, unexpand_into_body_span_with_visible_macro,
14+
};
15+
use crate::coverage::ExtractedHirInfo;
16+
use rustc_index::IndexVec;
17+
18+
#[derive(Clone, Debug)]
19+
pub(super) enum BcbMappingKind {
20+
/// Associates an ordinary executable code span with its corresponding BCB.
21+
Code(BasicCoverageBlock),
22+
23+
// Ordinary branch mappings are stored separately, so they don't have a
24+
// variant in this enum.
25+
//
26+
/// Associates a mcdc branch span with condition info besides fields for normal branch.
27+
MCDCBranch {
28+
true_bcb: BasicCoverageBlock,
29+
false_bcb: BasicCoverageBlock,
30+
/// If `None`, this actually represents a normal branch mapping inserted
31+
/// for code that was too complex for MC/DC.
32+
condition_info: Option<ConditionInfo>,
33+
decision_depth: u16,
34+
},
35+
/// Associates a mcdc decision with its join BCB.
36+
MCDCDecision {
37+
end_bcbs: BTreeSet<BasicCoverageBlock>,
38+
bitmap_idx: u32,
39+
conditions_num: u16,
40+
decision_depth: u16,
41+
},
42+
}
43+
44+
#[derive(Debug)]
45+
pub(super) struct BcbMapping {
46+
pub(super) kind: BcbMappingKind,
47+
pub(super) span: Span,
48+
}
49+
50+
/// This is separate from [`BcbMappingKind`] to help prepare for larger changes
51+
/// that will be needed for improved branch coverage in the future.
52+
/// (See <https://github.com/rust-lang/rust/pull/124217>.)
53+
#[derive(Debug)]
54+
pub(super) struct BcbBranchPair {
55+
pub(super) span: Span,
56+
pub(super) true_bcb: BasicCoverageBlock,
57+
pub(super) false_bcb: BasicCoverageBlock,
58+
}
59+
60+
pub(super) struct CoverageSpans {
61+
bcb_has_mappings: BitSet<BasicCoverageBlock>,
62+
pub(super) mappings: Vec<BcbMapping>,
63+
pub(super) branch_pairs: Vec<BcbBranchPair>,
64+
test_vector_bitmap_bytes: u32,
65+
}
66+
67+
impl CoverageSpans {
68+
pub(super) fn bcb_has_coverage_spans(&self, bcb: BasicCoverageBlock) -> bool {
69+
self.bcb_has_mappings.contains(bcb)
70+
}
71+
72+
pub(super) fn test_vector_bitmap_bytes(&self) -> u32 {
73+
self.test_vector_bitmap_bytes
74+
}
75+
}
76+
77+
/// Extracts coverage-relevant spans from MIR, and associates them with
78+
/// their corresponding BCBs.
79+
///
80+
/// Returns `None` if no coverage-relevant spans could be extracted.
81+
pub(super) fn generate_coverage_spans(
82+
mir_body: &mir::Body<'_>,
83+
hir_info: &ExtractedHirInfo,
84+
basic_coverage_blocks: &CoverageGraph,
85+
) -> Option<CoverageSpans> {
86+
let mut mappings = vec![];
87+
let mut branch_pairs = vec![];
88+
89+
if hir_info.is_async_fn {
90+
// An async function desugars into a function that returns a future,
91+
// with the user code wrapped in a closure. Any spans in the desugared
92+
// outer function will be unhelpful, so just keep the signature span
93+
// and ignore all of the spans in the MIR body.
94+
if let Some(span) = hir_info.fn_sig_span_extended {
95+
mappings.push(BcbMapping { kind: BcbMappingKind::Code(START_BCB), span });
96+
}
97+
} else {
98+
extract_refined_covspans(mir_body, hir_info, basic_coverage_blocks, &mut mappings);
99+
100+
branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, basic_coverage_blocks));
101+
102+
mappings.extend(extract_mcdc_mappings(mir_body, hir_info.body_span, basic_coverage_blocks));
103+
}
104+
105+
if mappings.is_empty() && branch_pairs.is_empty() {
106+
return None;
107+
}
108+
109+
// Identify which BCBs have one or more mappings.
110+
let mut bcb_has_mappings = BitSet::new_empty(basic_coverage_blocks.num_nodes());
111+
let mut insert = |bcb| {
112+
bcb_has_mappings.insert(bcb);
113+
};
114+
let mut test_vector_bitmap_bytes = 0;
115+
for BcbMapping { kind, span: _ } in &mappings {
116+
match *kind {
117+
BcbMappingKind::Code(bcb) => insert(bcb),
118+
BcbMappingKind::MCDCBranch { true_bcb, false_bcb, .. } => {
119+
insert(true_bcb);
120+
insert(false_bcb);
121+
}
122+
BcbMappingKind::MCDCDecision { bitmap_idx, conditions_num, .. } => {
123+
// `bcb_has_mappings` is used for inject coverage counters
124+
// but they are not needed for decision BCBs.
125+
// While the length of test vector bitmap should be calculated here.
126+
test_vector_bitmap_bytes = test_vector_bitmap_bytes
127+
.max(bitmap_idx + (1_u32 << conditions_num as u32).div_ceil(8));
128+
}
129+
}
130+
}
131+
for &BcbBranchPair { true_bcb, false_bcb, .. } in &branch_pairs {
132+
insert(true_bcb);
133+
insert(false_bcb);
134+
}
135+
136+
Some(CoverageSpans { bcb_has_mappings, mappings, branch_pairs, test_vector_bitmap_bytes })
137+
}
138+
139+
fn resolve_block_markers(
140+
branch_info: &mir::coverage::BranchInfo,
141+
mir_body: &mir::Body<'_>,
142+
) -> IndexVec<BlockMarkerId, Option<BasicBlock>> {
143+
let mut block_markers = IndexVec::<BlockMarkerId, Option<BasicBlock>>::from_elem_n(
144+
None,
145+
branch_info.num_block_markers,
146+
);
147+
148+
// Fill out the mapping from block marker IDs to their enclosing blocks.
149+
for (bb, data) in mir_body.basic_blocks.iter_enumerated() {
150+
for statement in &data.statements {
151+
if let StatementKind::Coverage(CoverageKind::BlockMarker { id }) = statement.kind {
152+
block_markers[id] = Some(bb);
153+
}
154+
}
155+
}
156+
157+
block_markers
158+
}
159+
160+
// FIXME: There is currently a lot of redundancy between
161+
// `extract_branch_pairs` and `extract_mcdc_mappings`. This is needed so
162+
// that they can each be modified without interfering with the other, but in
163+
// the long term we should try to bring them together again when branch coverage
164+
// and MC/DC coverage support are more mature.
165+
166+
pub(super) fn extract_branch_pairs(
167+
mir_body: &mir::Body<'_>,
168+
hir_info: &ExtractedHirInfo,
169+
basic_coverage_blocks: &CoverageGraph,
170+
) -> Vec<BcbBranchPair> {
171+
let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { return vec![] };
172+
173+
let block_markers = resolve_block_markers(branch_info, mir_body);
174+
175+
branch_info
176+
.branch_spans
177+
.iter()
178+
.filter_map(|&BranchSpan { span: raw_span, true_marker, false_marker }| {
179+
// For now, ignore any branch span that was introduced by
180+
// expansion. This makes things like assert macros less noisy.
181+
if !raw_span.ctxt().outer_expn_data().is_root() {
182+
return None;
183+
}
184+
let (span, _) =
185+
unexpand_into_body_span_with_visible_macro(raw_span, hir_info.body_span)?;
186+
187+
let bcb_from_marker =
188+
|marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?);
189+
190+
let true_bcb = bcb_from_marker(true_marker)?;
191+
let false_bcb = bcb_from_marker(false_marker)?;
192+
193+
Some(BcbBranchPair { span, true_bcb, false_bcb })
194+
})
195+
.collect::<Vec<_>>()
196+
}
197+
198+
pub(super) fn extract_mcdc_mappings(
199+
mir_body: &mir::Body<'_>,
200+
body_span: Span,
201+
basic_coverage_blocks: &CoverageGraph,
202+
) -> Vec<BcbMapping> {
203+
let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else {
204+
return vec![];
205+
};
206+
207+
let block_markers = resolve_block_markers(branch_info, mir_body);
208+
209+
let bcb_from_marker =
210+
|marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?);
211+
212+
let check_branch_bcb =
213+
|raw_span: Span, true_marker: BlockMarkerId, false_marker: BlockMarkerId| {
214+
// For now, ignore any branch span that was introduced by
215+
// expansion. This makes things like assert macros less noisy.
216+
if !raw_span.ctxt().outer_expn_data().is_root() {
217+
return None;
218+
}
219+
let (span, _) = unexpand_into_body_span_with_visible_macro(raw_span, body_span)?;
220+
221+
let true_bcb = bcb_from_marker(true_marker)?;
222+
let false_bcb = bcb_from_marker(false_marker)?;
223+
Some((span, true_bcb, false_bcb))
224+
};
225+
226+
let mcdc_branch_filter_map = |&MCDCBranchSpan {
227+
span: raw_span,
228+
true_marker,
229+
false_marker,
230+
condition_info,
231+
decision_depth,
232+
}| {
233+
check_branch_bcb(raw_span, true_marker, false_marker).map(|(span, true_bcb, false_bcb)| {
234+
BcbMapping {
235+
kind: BcbMappingKind::MCDCBranch {
236+
true_bcb,
237+
false_bcb,
238+
condition_info,
239+
decision_depth,
240+
},
241+
span,
242+
}
243+
})
244+
};
245+
246+
let mut next_bitmap_idx = 0;
247+
248+
let decision_filter_map = |decision: &MCDCDecisionSpan| {
249+
let (span, _) = unexpand_into_body_span_with_visible_macro(decision.span, body_span)?;
250+
251+
let end_bcbs = decision
252+
.end_markers
253+
.iter()
254+
.map(|&marker| bcb_from_marker(marker))
255+
.collect::<Option<_>>()?;
256+
257+
let bitmap_idx = next_bitmap_idx;
258+
next_bitmap_idx += (1_u32 << decision.conditions_num).div_ceil(8);
259+
260+
Some(BcbMapping {
261+
kind: BcbMappingKind::MCDCDecision {
262+
end_bcbs,
263+
bitmap_idx,
264+
conditions_num: decision.conditions_num as u16,
265+
decision_depth: decision.decision_depth,
266+
},
267+
span,
268+
})
269+
};
270+
271+
std::iter::empty()
272+
.chain(branch_info.mcdc_branch_spans.iter().filter_map(mcdc_branch_filter_map))
273+
.chain(branch_info.mcdc_decision_spans.iter().filter_map(decision_filter_map))
274+
.collect::<Vec<_>>()
275+
}

compiler/rustc_mir_transform/src/coverage/mod.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ pub mod query;
22

33
mod counters;
44
mod graph;
5+
mod mappings;
56
mod spans;
67
#[cfg(test)]
78
mod tests;
89

910
use self::counters::{CounterIncrementSite, CoverageCounters};
1011
use self::graph::{BasicCoverageBlock, CoverageGraph};
11-
use self::spans::{BcbBranchPair, BcbMapping, BcbMappingKind, CoverageSpans};
12+
use self::mappings::{BcbBranchPair, BcbMapping, BcbMappingKind, CoverageSpans};
1213

1314
use crate::MirPass;
1415

@@ -70,7 +71,7 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
7071
////////////////////////////////////////////////////
7172
// Compute coverage spans from the `CoverageGraph`.
7273
let Some(coverage_spans) =
73-
spans::generate_coverage_spans(mir_body, &hir_info, &basic_coverage_blocks)
74+
mappings::generate_coverage_spans(mir_body, &hir_info, &basic_coverage_blocks)
7475
else {
7576
// No relevant spans were found in MIR, so skip instrumenting this function.
7677
return;

0 commit comments

Comments
 (0)