Skip to content

Commit 55c6929

Browse files
committed
mcdc-coverage: Use decision context stack to handle nested decisions.
- Introduce MCDCDecisionCtx - Use a stack of MCDCDecisionCtx to handle nested decisions
1 parent 3089fcc commit 55c6929

File tree

1 file changed

+36
-20
lines changed

1 file changed

+36
-20
lines changed

compiler/rustc_mir_build/src/build/coverageinfo.rs

+36-20
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,12 @@ impl BranchInfoBuilder {
103103
false_marker: BlockMarkerId,
104104
) -> Option<(u16, ConditionInfo)> {
105105
let mcdc_state = self.mcdc_state.as_mut()?;
106-
let decision_depth = mcdc_state.decision_depth;
106+
let decision_depth = mcdc_state.decision_depth();
107107
let (mut condition_info, decision_result) =
108108
mcdc_state.take_condition(true_marker, false_marker);
109+
// take_condition() returns Some for decision_result when the decision stack
110+
// is empty, i.e. when all the conditions of the decision were instrumented,
111+
// and the decision is "complete".
109112
if let Some(decision) = decision_result {
110113
match decision.conditions_num {
111114
0 => {
@@ -200,20 +203,28 @@ impl BranchInfoBuilder {
200203
/// This limit may be relaxed if the [upstream change](https://github.com/llvm/llvm-project/pull/82448) is merged.
201204
const MAX_CONDITIONS_NUM_IN_DECISION: usize = 6;
202205

203-
struct MCDCState {
206+
#[derive(Default)]
207+
struct MCDCDecisionCtx {
204208
/// To construct condition evaluation tree.
205209
decision_stack: VecDeque<ConditionInfo>,
206210
processing_decision: Option<MCDCDecisionSpan>,
207-
decision_depth: u16,
211+
}
212+
213+
struct MCDCState {
214+
decision_ctx_stack: Vec<MCDCDecisionCtx>,
208215
}
209216

210217
impl MCDCState {
211218
fn new_if_enabled(tcx: TyCtxt<'_>) -> Option<Self> {
212-
tcx.sess.instrument_coverage_mcdc().then(|| Self {
213-
decision_stack: VecDeque::new(),
214-
processing_decision: None,
215-
decision_depth: 0,
216-
})
219+
tcx.sess
220+
.instrument_coverage_mcdc()
221+
.then(|| Self { decision_ctx_stack: vec![MCDCDecisionCtx::default()] })
222+
}
223+
224+
#[inline]
225+
fn decision_depth(&self) -> u16 {
226+
self.decision_ctx_stack.len().checked_sub(1).expect("Unexpected empty decision stack")
227+
as u16
217228
}
218229

219230
// At first we assign ConditionIds for each sub expression.
@@ -257,20 +268,23 @@ impl MCDCState {
257268
// - If the op is AND, the "false_next" of LHS and RHS should be the parent's "false_next". While "true_next" of the LHS is the RHS, the "true next" of RHS is the parent's "true_next".
258269
// - If the op is OR, the "true_next" of LHS and RHS should be the parent's "true_next". While "false_next" of the LHS is the RHS, the "false next" of RHS is the parent's "false_next".
259270
fn record_conditions(&mut self, op: LogicalOp, span: Span) {
260-
let decision = match self.processing_decision.as_mut() {
271+
let decision_depth = self.decision_depth();
272+
let decision_ctx =
273+
self.decision_ctx_stack.last_mut().expect("Unexpected empty decision_ctx_stack");
274+
let decision = match decision_ctx.processing_decision.as_mut() {
261275
Some(decision) => {
262276
decision.span = decision.span.to(span);
263277
decision
264278
}
265-
None => self.processing_decision.insert(MCDCDecisionSpan {
279+
None => decision_ctx.processing_decision.insert(MCDCDecisionSpan {
266280
span,
267281
conditions_num: 0,
268282
end_markers: vec![],
269-
decision_depth: 0,
283+
decision_depth: decision_depth,
270284
}),
271285
};
272286

273-
let parent_condition = self.decision_stack.pop_back().unwrap_or_default();
287+
let parent_condition = decision_ctx.decision_stack.pop_back().unwrap_or_default();
274288
let lhs_id = if parent_condition.condition_id == ConditionId::NONE {
275289
decision.conditions_num += 1;
276290
ConditionId::from(decision.conditions_num)
@@ -310,19 +324,21 @@ impl MCDCState {
310324
}
311325
};
312326
// We visit expressions tree in pre-order, so place the left-hand side on the top.
313-
self.decision_stack.push_back(rhs);
314-
self.decision_stack.push_back(lhs);
327+
decision_ctx.decision_stack.push_back(rhs);
328+
decision_ctx.decision_stack.push_back(lhs);
315329
}
316330

317331
fn take_condition(
318332
&mut self,
319333
true_marker: BlockMarkerId,
320334
false_marker: BlockMarkerId,
321335
) -> (Option<ConditionInfo>, Option<MCDCDecisionSpan>) {
322-
let Some(condition_info) = self.decision_stack.pop_back() else {
336+
let decision_ctx =
337+
self.decision_ctx_stack.last_mut().expect("Unexpected empty decision_ctx_stack");
338+
let Some(condition_info) = decision_ctx.decision_stack.pop_back() else {
323339
return (None, None);
324340
};
325-
let Some(decision) = self.processing_decision.as_mut() else {
341+
let Some(decision) = decision_ctx.processing_decision.as_mut() else {
326342
bug!("Processing decision should have been created before any conditions are taken");
327343
};
328344
if condition_info.true_next_id == ConditionId::NONE {
@@ -332,8 +348,8 @@ impl MCDCState {
332348
decision.end_markers.push(false_marker);
333349
}
334350

335-
if self.decision_stack.is_empty() {
336-
(Some(condition_info), self.processing_decision.take())
351+
if decision_ctx.decision_stack.is_empty() {
352+
(Some(condition_info), decision_ctx.processing_decision.take())
337353
} else {
338354
(Some(condition_info), None)
339355
}
@@ -399,15 +415,15 @@ impl Builder<'_, '_> {
399415
if let Some(branch_info) = self.coverage_branch_info.as_mut()
400416
&& let Some(mcdc_state) = branch_info.mcdc_state.as_mut()
401417
{
402-
mcdc_state.decision_depth += 1;
418+
mcdc_state.decision_ctx_stack.push(MCDCDecisionCtx::default());
403419
};
404420
}
405421

406422
pub(crate) fn mcdc_decrement_depth_if_enabled(&mut self) {
407423
if let Some(branch_info) = self.coverage_branch_info.as_mut()
408424
&& let Some(mcdc_state) = branch_info.mcdc_state.as_mut()
409425
{
410-
mcdc_state.decision_depth -= 1;
426+
mcdc_state.decision_ctx_stack.pop().expect("Unexpected empty decision stack");
411427
};
412428
}
413429
}

0 commit comments

Comments
 (0)