Skip to content

Commit 40d9dd9

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 67ebff7 commit 40d9dd9

File tree

1 file changed

+33
-18
lines changed

1 file changed

+33
-18
lines changed

compiler/rustc_mir_build/src/build/coverageinfo.rs

+33-18
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ impl BranchInfoBuilder {
111111
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,25 @@ 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>,
191+
}
192+
193+
struct MCDCState {
194+
decision_ctx_stack: Vec<MCDCDecisionCtx>,
195+
/// To track the nesting level of boolean expressions.
187196
decision_depth: u16,
188197
}
189198

190199
impl MCDCState {
191200
fn new_if_enabled(tcx: TyCtxt<'_>) -> Option<Self> {
192-
tcx.sess
193-
.instrument_coverage_mcdc()
194-
.then(|| Self {
195-
decision_stack: VecDeque::new(),
196-
processing_decision: None,
197-
decision_depth: 0,
198-
})
201+
tcx.sess.instrument_coverage_mcdc().then(|| Self {
202+
decision_ctx_stack: vec![MCDCDecisionCtx::default()],
203+
decision_depth: 0,
204+
})
199205
}
200206

201207
// At first we assign ConditionIds for each sub expression.
@@ -239,20 +245,21 @@ impl MCDCState {
239245
// - 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".
240246
// - 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".
241247
fn record_conditions(&mut self, op: LogicalOp, span: Span) {
242-
let decision = match self.processing_decision.as_mut() {
248+
let decision_ctx = &mut self.decision_ctx_stack[self.decision_depth as usize];
249+
let decision = match decision_ctx.processing_decision.as_mut() {
243250
Some(decision) => {
244251
decision.span = decision.span.to(span);
245252
decision
246253
}
247-
None => self.processing_decision.insert(MCDCDecisionSpan {
254+
None => decision_ctx.processing_decision.insert(MCDCDecisionSpan {
248255
span,
249256
conditions_num: 0,
250257
end_markers: vec![],
251-
decision_depth: 0,
258+
decision_depth: self.decision_depth,
252259
}),
253260
};
254261

255-
let parent_condition = self.decision_stack.pop_back().unwrap_or_default();
262+
let parent_condition = decision_ctx.decision_stack.pop_back().unwrap_or_default();
256263
let lhs_id = if parent_condition.condition_id == ConditionId::NONE {
257264
decision.conditions_num += 1;
258265
ConditionId::from(decision.conditions_num)
@@ -292,19 +299,20 @@ impl MCDCState {
292299
}
293300
};
294301
// 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);
302+
decision_ctx.decision_stack.push_back(rhs);
303+
decision_ctx.decision_stack.push_back(lhs);
297304
}
298305

299306
fn take_condition(
300307
&mut self,
301308
true_marker: BlockMarkerId,
302309
false_marker: BlockMarkerId,
303310
) -> (Option<ConditionInfo>, Option<MCDCDecisionSpan>) {
304-
let Some(condition_info) = self.decision_stack.pop_back() else {
311+
let decision_ctx = &mut self.decision_ctx_stack[self.decision_depth as usize];
312+
let Some(condition_info) = decision_ctx.decision_stack.pop_back() else {
305313
return (None, None);
306314
};
307-
let Some(decision) = self.processing_decision.as_mut() else {
315+
let Some(decision) = decision_ctx.processing_decision.as_mut() else {
308316
bug!("Processing decision should have been created before any conditions are taken");
309317
};
310318
if condition_info.true_next_id == ConditionId::NONE {
@@ -314,8 +322,8 @@ impl MCDCState {
314322
decision.end_markers.push(false_marker);
315323
}
316324

317-
if self.decision_stack.is_empty() {
318-
(Some(condition_info), self.processing_decision.take())
325+
if decision_ctx.decision_stack.is_empty() {
326+
(Some(condition_info), decision_ctx.processing_decision.take())
319327
} else {
320328
(Some(condition_info), None)
321329
}
@@ -391,6 +399,7 @@ impl Builder<'_, '_> {
391399
if let Some(branch_info) = self.coverage_branch_info.as_mut()
392400
&& let Some(mcdc_state) = branch_info.mcdc_state.as_mut()
393401
{
402+
mcdc_state.decision_ctx_stack.push(MCDCDecisionCtx::default());
394403
mcdc_state.decision_depth += 1;
395404
};
396405
}
@@ -399,6 +408,12 @@ impl Builder<'_, '_> {
399408
if let Some(branch_info) = self.coverage_branch_info.as_mut()
400409
&& let Some(mcdc_state) = branch_info.mcdc_state.as_mut()
401410
{
411+
#[allow(unused)]
412+
let Some(MCDCDecisionCtx { decision_stack, processing_decision }) =
413+
mcdc_state.decision_ctx_stack.pop()
414+
else {
415+
bug!("Unexpected empty Decision stack");
416+
};
402417
mcdc_state.decision_depth -= 1;
403418
};
404419
}

0 commit comments

Comments
 (0)