Skip to content

Commit 8dbedb1

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 3b2f83b commit 8dbedb1

File tree

1 file changed

+38
-20
lines changed

1 file changed

+38
-20
lines changed

compiler/rustc_mir_build/src/build/coverageinfo.rs

+38-20
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,12 @@ impl BranchInfoBuilder {
108108
false_marker: BlockMarkerId,
109109
) -> Option<(u16, ConditionInfo)> {
110110
let mcdc_state = self.mcdc_state.as_mut()?;
111-
let decision_depth = mcdc_state.decision_depth;
111+
let decision_depth = mcdc_state.decision_depth();
112112
let (mut condition_info, decision_result) =
113113
mcdc_state.take_condition(true_marker, false_marker);
114+
// take_condition() returns Some for decision_result when the decision stack
115+
// is empty, i.e. when all the conditions of the decision were instrumented,
116+
// and the decision is "complete".
114117
if let Some(decision) = decision_result {
115118
match decision.conditions_num {
116119
0 => {
@@ -180,22 +183,27 @@ impl BranchInfoBuilder {
180183
/// This limit may be relaxed if the [upstream change](https://github.com/llvm/llvm-project/pull/82448) is merged.
181184
const MAX_CONDITIONS_NUM_IN_DECISION: usize = 6;
182185

183-
struct MCDCState {
186+
#[derive(Default)]
187+
struct MCDCDecisionCtx {
184188
/// To construct condition evaluation tree.
185189
decision_stack: VecDeque<ConditionInfo>,
186190
processing_decision: Option<MCDCDecisionSpan>,
187-
decision_depth: u16,
191+
}
192+
193+
struct MCDCState {
194+
decision_ctx_stack: Vec<MCDCDecisionCtx>,
188195
}
189196

190197
impl MCDCState {
191198
fn new_if_enabled(tcx: TyCtxt<'_>) -> Option<Self> {
192199
tcx.sess
193200
.instrument_coverage_mcdc()
194-
.then(|| Self {
195-
decision_stack: VecDeque::new(),
196-
processing_decision: None,
197-
decision_depth: 0,
198-
})
201+
.then(|| Self { decision_ctx_stack: vec![MCDCDecisionCtx::default()] })
202+
}
203+
204+
#[inline]
205+
fn decision_depth(&self) -> u16 {
206+
(self.decision_ctx_stack.len() - 1) as u16
199207
}
200208

201209
// At first we assign ConditionIds for each sub expression.
@@ -239,20 +247,23 @@ impl MCDCState {
239247
// - 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".
240248
// - 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".
241249
fn record_conditions(&mut self, op: LogicalOp, span: Span) {
242-
let decision = match self.processing_decision.as_mut() {
250+
let decision_depth = self.decision_depth();
251+
let decision_ctx =
252+
self.decision_ctx_stack.last_mut().expect("Unexpected empty decision_ctx_stack");
253+
let decision = match decision_ctx.processing_decision.as_mut() {
243254
Some(decision) => {
244255
decision.span = decision.span.to(span);
245256
decision
246257
}
247-
None => self.processing_decision.insert(MCDCDecisionSpan {
258+
None => decision_ctx.processing_decision.insert(MCDCDecisionSpan {
248259
span,
249260
conditions_num: 0,
250261
end_markers: vec![],
251-
decision_depth: 0,
262+
decision_depth: decision_depth,
252263
}),
253264
};
254265

255-
let parent_condition = self.decision_stack.pop_back().unwrap_or_default();
266+
let parent_condition = decision_ctx.decision_stack.pop_back().unwrap_or_default();
256267
let lhs_id = if parent_condition.condition_id == ConditionId::NONE {
257268
decision.conditions_num += 1;
258269
ConditionId::from(decision.conditions_num)
@@ -292,19 +303,21 @@ impl MCDCState {
292303
}
293304
};
294305
// We visit expressions tree in pre-order, so place the left-hand side on the top.
295-
self.decision_stack.push_back(rhs);
296-
self.decision_stack.push_back(lhs);
306+
decision_ctx.decision_stack.push_back(rhs);
307+
decision_ctx.decision_stack.push_back(lhs);
297308
}
298309

299310
fn take_condition(
300311
&mut self,
301312
true_marker: BlockMarkerId,
302313
false_marker: BlockMarkerId,
303314
) -> (Option<ConditionInfo>, Option<MCDCDecisionSpan>) {
304-
let Some(condition_info) = self.decision_stack.pop_back() else {
315+
let decision_ctx =
316+
self.decision_ctx_stack.last_mut().expect("Unexpected empty decision_ctx_stack");
317+
let Some(condition_info) = decision_ctx.decision_stack.pop_back() else {
305318
return (None, None);
306319
};
307-
let Some(decision) = self.processing_decision.as_mut() else {
320+
let Some(decision) = decision_ctx.processing_decision.as_mut() else {
308321
bug!("Processing decision should have been created before any conditions are taken");
309322
};
310323
if condition_info.true_next_id == ConditionId::NONE {
@@ -314,8 +327,8 @@ impl MCDCState {
314327
decision.end_markers.push(false_marker);
315328
}
316329

317-
if self.decision_stack.is_empty() {
318-
(Some(condition_info), self.processing_decision.take())
330+
if decision_ctx.decision_stack.is_empty() {
331+
(Some(condition_info), decision_ctx.processing_decision.take())
319332
} else {
320333
(Some(condition_info), None)
321334
}
@@ -391,15 +404,20 @@ impl Builder<'_, '_> {
391404
if let Some(branch_info) = self.coverage_branch_info.as_mut()
392405
&& let Some(mcdc_state) = branch_info.mcdc_state.as_mut()
393406
{
394-
mcdc_state.decision_depth += 1;
407+
mcdc_state.decision_ctx_stack.push(MCDCDecisionCtx::default());
395408
};
396409
}
397410

398411
pub(crate) fn mcdc_decrement_depth_if_enabled(&mut self) {
399412
if let Some(branch_info) = self.coverage_branch_info.as_mut()
400413
&& let Some(mcdc_state) = branch_info.mcdc_state.as_mut()
401414
{
402-
mcdc_state.decision_depth -= 1;
415+
#[allow(unused)]
416+
let Some(MCDCDecisionCtx { decision_stack, processing_decision }) =
417+
mcdc_state.decision_ctx_stack.pop()
418+
else {
419+
bug!("Unexpected empty Decision stack");
420+
};
403421
};
404422
}
405423
}

0 commit comments

Comments
 (0)