Skip to content

Commit 9fea26a

Browse files
committed
mcdc-coverage: Refactor the MCDC info building
1 parent cb55ae4 commit 9fea26a

File tree

2 files changed

+143
-60
lines changed

2 files changed

+143
-60
lines changed

compiler/rustc_mir_build/src/build/coverageinfo.rs

+135-60
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::collections::hash_map::Entry;
44
use rustc_data_structures::fx::FxHashMap;
55
use rustc_index::IndexVec;
66
use rustc_middle::mir::coverage::{
7-
BlockMarkerId, BranchSpan, CoverageKind, DecisionId, DecisionSpan,
7+
BlockMarkerId, BranchSpan, ConditionId, CoverageKind, DecisionId, DecisionSpan,
88
};
99
use rustc_middle::mir::{self, BasicBlock, UnOp};
1010
use rustc_middle::thir::{ExprId, ExprKind, Thir};
@@ -15,14 +15,7 @@ use rustc_span::Span;
1515
use crate::build::Builder;
1616
use crate::errors::MCDCNestedDecision;
1717

18-
pub(crate) struct BranchInfoBuilder {
19-
/// Maps condition expressions to their enclosing `!`, for better instrumentation.
20-
nots: FxHashMap<ExprId, NotInfo>,
21-
22-
num_block_markers: usize,
23-
branch_spans: Vec<BranchSpan>,
24-
25-
// MCDC decision stuff
18+
struct MCDCInfoBuilder {
2619
/// ID of the current decision.
2720
/// Do not use directly. Use the function instead, as it will hide
2821
/// the decision in the scope of nested decisions.
@@ -31,7 +24,54 @@ pub(crate) struct BranchInfoBuilder {
3124
/// nested decisions.
3225
nested_decision_level: u32,
3326
/// Vector for storing all the decisions with their span
34-
decisions: IndexVec<DecisionId, Span>,
27+
decision_spans: IndexVec<DecisionId, Span>,
28+
29+
next_condition_id: u32,
30+
}
31+
32+
impl MCDCInfoBuilder {
33+
/// Increase the nested decision level and return true if the
34+
/// decision can be instrumented (not in a nested condition).
35+
pub fn enter_decision(&mut self, span: Span) -> bool {
36+
self.nested_decision_level += 1;
37+
let can_mcdc = !self.in_nested_condition();
38+
39+
if can_mcdc {
40+
self.current_decision_id = Some(self.decision_spans.push(span));
41+
}
42+
43+
can_mcdc
44+
}
45+
46+
pub fn exit_decision(&mut self) {
47+
self.nested_decision_level -= 1;
48+
}
49+
50+
/// Return true if the current decision is located inside another decision.
51+
pub fn in_nested_condition(&self) -> bool {
52+
self.nested_decision_level > 1
53+
}
54+
55+
pub fn current_decision_id(&self) -> Option<DecisionId> {
56+
if self.in_nested_condition() { None } else { self.current_decision_id }
57+
}
58+
59+
pub fn next_condition_id(&mut self) -> u32 {
60+
let res = self.next_condition_id;
61+
self.next_condition_id += 1;
62+
res
63+
}
64+
}
65+
66+
pub(crate) struct BranchInfoBuilder {
67+
/// Maps condition expressions to their enclosing `!`, for better instrumentation.
68+
nots: FxHashMap<ExprId, NotInfo>,
69+
70+
num_block_markers: usize,
71+
branch_spans: Vec<BranchSpan>,
72+
73+
// MCDC decision stuff
74+
mcdc_info: Option<MCDCInfoBuilder>,
3575
}
3676

3777
#[derive(Clone, Copy)]
@@ -51,13 +91,21 @@ impl BranchInfoBuilder {
5191
if (tcx.sess.instrument_coverage_branch() || tcx.sess.instrument_coverage_mcdc())
5292
&& tcx.is_eligible_for_coverage(def_id)
5393
{
94+
let mcdc_info = if tcx.sess.instrument_coverage_mcdc() {
95+
Some(MCDCInfoBuilder {
96+
current_decision_id: None,
97+
nested_decision_level: 0,
98+
decision_spans: IndexVec::new(),
99+
next_condition_id: 0,
100+
})
101+
} else {
102+
None
103+
};
54104
Some(Self {
55105
nots: FxHashMap::default(),
56106
num_block_markers: 0,
57107
branch_spans: vec![],
58-
current_decision_id: None,
59-
nested_decision_level: 0,
60-
decisions: IndexVec::new(),
108+
mcdc_info,
61109
})
62110
} else {
63111
None
@@ -111,15 +159,18 @@ impl BranchInfoBuilder {
111159
}
112160

113161
pub(crate) fn into_done(self) -> Option<Box<mir::coverage::BranchInfo>> {
114-
let Self { nots: _, num_block_markers, branch_spans, decisions, .. } = self;
162+
let Self { nots: _, num_block_markers, branch_spans, mcdc_info, .. } = self;
115163

116164
if num_block_markers == 0 {
117165
assert!(branch_spans.is_empty());
118166
return None;
119167
}
120168

121169
let mut decision_spans = IndexVec::from_iter(
122-
decisions.into_iter().map(|span| DecisionSpan { span, num_conditions: 0 }),
170+
mcdc_info
171+
.map_or(IndexVec::new(), |mcdc_info| mcdc_info.decision_spans)
172+
.into_iter()
173+
.map(|span| DecisionSpan { span, num_conditions: 0 }),
123174
);
124175

125176
// Count the number of conditions linked to each decision.
@@ -135,32 +186,6 @@ impl BranchInfoBuilder {
135186
decision_spans,
136187
}))
137188
}
138-
139-
/// Increase the nested decision level and return true if the
140-
/// decision can be instrumented (not in a nested condition).
141-
pub fn enter_decision(&mut self, span: Span) -> bool {
142-
self.nested_decision_level += 1;
143-
let can_mcdc = !self.in_nested_condition();
144-
145-
if can_mcdc {
146-
self.current_decision_id = Some(self.decisions.push(span));
147-
}
148-
149-
can_mcdc
150-
}
151-
152-
pub fn exit_decision(&mut self) {
153-
self.nested_decision_level -= 1;
154-
}
155-
156-
/// Return true if the current decision is located inside another decision.
157-
pub fn in_nested_condition(&self) -> bool {
158-
self.nested_decision_level > 1
159-
}
160-
161-
pub fn current_decision_id(&self) -> Option<DecisionId> {
162-
if self.in_nested_condition() { None } else { self.current_decision_id }
163-
}
164189
}
165190

166191
impl Builder<'_, '_> {
@@ -206,7 +231,11 @@ impl Builder<'_, '_> {
206231
branch_info.branch_spans.push(BranchSpan {
207232
span: source_info.span,
208233
// FIXME(dprn): Handle case when MCDC is disabled better than just putting 0.
209-
decision_id: branch_info.current_decision_id.unwrap_or(DecisionId::from_u32(0)),
234+
decision_id: branch_info
235+
.mcdc_info
236+
.as_ref()
237+
.and_then(|mcdc_info| mcdc_info.current_decision_id())
238+
.unwrap_or(DecisionId::from_u32(0)),
210239
true_marker,
211240
false_marker,
212241
});
@@ -215,25 +244,24 @@ impl Builder<'_, '_> {
215244
/// If MCDC coverage is enabled, inject a decision entry marker in the given decision.
216245
/// return true
217246
pub(crate) fn begin_mcdc_decision_coverage(&mut self, expr_id: ExprId, block: BasicBlock) {
218-
// Early return if MCDC coverage is not enabled.
219-
if !self.tcx.sess.instrument_coverage_mcdc() {
220-
return;
221-
}
222-
let Some(branch_info) = self.coverage_branch_info.as_mut() else {
247+
// Get the MCDCInfoBuilder object, which existence implies that MCDC is enabled.
248+
let Some(BranchInfoBuilder { mcdc_info: Some(mcdc_info), .. }) =
249+
self.coverage_branch_info.as_mut()
250+
else {
223251
return;
224252
};
225253

226254
let span = self.thir[expr_id].span;
227255

228256
// enter_decision returns false if it detects nested decisions.
229-
if !branch_info.enter_decision(span) {
230-
// FIXME(dprn): do WARNING for nested decision.
257+
if !mcdc_info.enter_decision(span) {
258+
// FIXME(dprn): WARNING for nested decision does not seem to work properly
231259
debug!("MCDC: Unsupported nested decision");
232260
self.tcx.dcx().emit_warn(MCDCNestedDecision { span });
233261
return;
234262
}
235263

236-
let decision_id = branch_info.current_decision_id().expect("Should have returned.");
264+
let decision_id = mcdc_info.current_decision_id().expect("Should have returned.");
237265

238266
// Inject a decision marker
239267
let source_info = self.source_info(span);
@@ -248,16 +276,15 @@ impl Builder<'_, '_> {
248276

249277
/// If MCDC is enabled, and function is instrumented,
250278
pub(crate) fn end_mcdc_decision_coverage(&mut self) {
251-
// Early return if MCDC coverage is not enabled.
252-
if !self.tcx.sess.instrument_coverage_mcdc() {
253-
return;
254-
}
255-
let Some(branch_info) = self.coverage_branch_info.as_mut() else {
279+
// Get the MCDCInfoBuilder object, which existence implies that MCDC is enabled.
280+
let Some(BranchInfoBuilder { mcdc_info: Some(mcdc_info), .. }) =
281+
self.coverage_branch_info.as_mut()
282+
else {
256283
return;
257284
};
258285

259286
// Exit decision now so we can drop &mut to branch info
260-
branch_info.exit_decision();
287+
mcdc_info.exit_decision();
261288
}
262289

263290
/// If MCDC is enabled and the current decision is being instrumented,
@@ -268,16 +295,19 @@ impl Builder<'_, '_> {
268295
bb: BasicBlock,
269296
outcome: bool,
270297
) -> BasicBlock {
271-
let Some(branch_info) = self.coverage_branch_info.as_mut() else {
272-
// Coverage instrumentation is not enabled.
298+
// Get the MCDCInfoBuilder object, which existence implies that MCDC is enabled.
299+
let Some(BranchInfoBuilder { mcdc_info: Some(mcdc_info), .. }) =
300+
self.coverage_branch_info.as_mut()
301+
else {
273302
return bb;
274303
};
275-
let Some(decision_id) = branch_info.current_decision_id() else {
304+
305+
let Some(decision_id) = mcdc_info.current_decision_id() else {
276306
// Decision is not instrumented
277307
return bb;
278308
};
279309

280-
let span = branch_info.decisions[decision_id];
310+
let span = mcdc_info.decision_spans[decision_id];
281311
let source_info = self.source_info(span);
282312
let marker_statement = mir::Statement {
283313
source_info,
@@ -298,4 +328,49 @@ impl Builder<'_, '_> {
298328

299329
new_bb
300330
}
331+
332+
/// Add markers on the condition's basic blocks to ease the later MCDC instrumentation.
333+
pub(crate) fn visit_mcdc_condition(
334+
&mut self,
335+
condition_expr: ExprId,
336+
condition_block: BasicBlock,
337+
then_block: BasicBlock,
338+
else_block: BasicBlock,
339+
) {
340+
// Get the MCDCInfoBuilder object, which existence implies that MCDC is enabled.
341+
let Some(BranchInfoBuilder { mcdc_info: Some(mcdc_info), .. }) =
342+
self.coverage_branch_info.as_mut()
343+
else {
344+
return;
345+
};
346+
347+
let Some(decision_id) = mcdc_info.current_decision_id() else {
348+
// If current_decision_id() is None, the decision is not instrumented.
349+
return;
350+
};
351+
352+
353+
let id = ConditionId::from_u32(mcdc_info.next_condition_id());
354+
let span = self.thir[condition_expr].span;
355+
let source_info = self.source_info(span);
356+
357+
let mut inject_statement = |bb, cov_kind| {
358+
let statement =
359+
mir::Statement { source_info, kind: mir::StatementKind::Coverage(cov_kind) };
360+
self.cfg.basic_blocks[bb].statements.insert(0, statement);
361+
};
362+
363+
inject_statement(
364+
condition_block,
365+
CoverageKind::MCDCConditionEntryMarker { decision_id, id },
366+
);
367+
inject_statement(
368+
then_block,
369+
CoverageKind::MCDCConditionOutputMarker { decision_id, id, outcome: true },
370+
);
371+
inject_statement(
372+
else_block,
373+
CoverageKind::MCDCConditionOutputMarker { decision_id, id, outcome: false },
374+
);
375+
}
301376
}

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

+8
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
169169
then_block,
170170
else_block,
171171
);
172+
// Record MCDC coverage info
173+
// (Does nothing if mcdc coverage is not enabled.)
174+
this.visit_mcdc_condition(
175+
expr_id,
176+
block,
177+
then_block,
178+
else_block,
179+
);
172180

173181
let source_info = this.source_info(expr_span);
174182
this.cfg.terminate(block, source_info, term);

0 commit comments

Comments
 (0)