Skip to content

Commit

Permalink
coverage. Group MCDC decisions and conditions until instrument mappings
Browse files Browse the repository at this point in the history
  • Loading branch information
zhuyunxing committed Aug 29, 2024
1 parent 01a5a10 commit 2c0600a
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 164 deletions.
13 changes: 6 additions & 7 deletions compiler/rustc_middle/src/mir/coverage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,10 @@ pub struct CoverageInfoHi {
/// data structures without having to scan the entire body first.
pub num_block_markers: usize,
pub branch_spans: Vec<BranchSpan>,
pub mcdc_branch_spans: Vec<MCDCBranchSpan>,
pub mcdc_decision_spans: Vec<MCDCDecisionSpan>,
/// Branch spans generated by mcdc. Because of some limits mcdc builder give up generating
/// decisions including them so that they are handled as normal branch spans.
pub mcdc_degraded_branch_spans: Vec<MCDCBranchSpan>,
pub mcdc_spans: Vec<(MCDCDecisionSpan, Vec<MCDCBranchSpan>)>,
}

#[derive(Clone, Debug)]
Expand All @@ -299,12 +301,9 @@ pub struct ConditionInfo {
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
pub struct MCDCBranchSpan {
pub span: Span,
/// If `None`, this actually represents a normal branch span inserted for
/// code that was too complex for MC/DC.
pub condition_info: Option<ConditionInfo>,
pub condition_info: ConditionInfo,
pub true_marker: BlockMarkerId,
pub false_marker: BlockMarkerId,
pub decision_depth: u16,
}

#[derive(Copy, Clone, Debug)]
Expand All @@ -318,7 +317,7 @@ pub struct DecisionInfo {
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
pub struct MCDCDecisionSpan {
pub span: Span,
pub num_conditions: usize,
pub end_markers: Vec<BlockMarkerId>,
pub decision_depth: u16,
pub num_conditions: usize,
}
32 changes: 19 additions & 13 deletions compiler/rustc_middle/src/mir/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,8 +490,8 @@ fn write_coverage_info_hi(
let coverage::CoverageInfoHi {
num_block_markers: _,
branch_spans,
mcdc_branch_spans,
mcdc_decision_spans,
mcdc_degraded_branch_spans,
mcdc_spans,
} = coverage_info_hi;

// Only add an extra trailing newline if we printed at least one thing.
Expand All @@ -505,29 +505,35 @@ fn write_coverage_info_hi(
did_print = true;
}

for coverage::MCDCBranchSpan {
span,
condition_info,
true_marker,
false_marker,
decision_depth,
} in mcdc_branch_spans
for coverage::MCDCBranchSpan { span, true_marker, false_marker, .. } in
mcdc_degraded_branch_spans
{
writeln!(
w,
"{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?}, depth: {decision_depth:?} }} => {span:?}",
condition_info.map(|info| info.condition_id)
"{INDENT}coverage branch {{ true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",
)?;
did_print = true;
}

for coverage::MCDCDecisionSpan { span, num_conditions, end_markers, decision_depth } in
mcdc_decision_spans
for (
coverage::MCDCDecisionSpan { span, end_markers, decision_depth, num_conditions: _ },
conditions,
) in mcdc_spans
{
let num_conditions = conditions.len();
writeln!(
w,
"{INDENT}coverage mcdc decision {{ num_conditions: {num_conditions:?}, end: {end_markers:?}, depth: {decision_depth:?} }} => {span:?}"
)?;
for coverage::MCDCBranchSpan { span, condition_info, true_marker, false_marker } in
conditions
{
writeln!(
w,
"{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",
condition_info.condition_id
)?;
}
did_print = true;
}

Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_mir_build/src/build/coverageinfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,16 +175,16 @@ impl CoverageInfoBuilder {
let branch_spans =
branch_info.map(|branch_info| branch_info.branch_spans).unwrap_or_default();

let (mcdc_decision_spans, mcdc_branch_spans) =
let (mcdc_spans, mcdc_degraded_branch_spans) =
mcdc_info.map(MCDCInfoBuilder::into_done).unwrap_or_default();

// For simplicity, always return an info struct (without Option), even
// if there's nothing interesting in it.
Box::new(CoverageInfoHi {
num_block_markers,
branch_spans,
mcdc_branch_spans,
mcdc_decision_spans,
mcdc_degraded_branch_spans,
mcdc_spans,
})
}
}
Expand Down
81 changes: 48 additions & 33 deletions compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ struct MCDCDecisionCtx {
/// To construct condition evaluation tree.
decision_stack: VecDeque<ConditionInfo>,
processing_decision: Option<MCDCDecisionSpan>,
conditions: Vec<MCDCBranchSpan>,
}

struct MCDCState {
Expand Down Expand Up @@ -155,16 +156,29 @@ impl MCDCState {
decision_ctx.decision_stack.push_back(lhs);
}

fn take_condition(
fn try_finish_decision(
&mut self,
span: Span,
true_marker: BlockMarkerId,
false_marker: BlockMarkerId,
) -> (Option<ConditionInfo>, Option<MCDCDecisionSpan>) {
degraded_branches: &mut Vec<MCDCBranchSpan>,
) -> Option<(MCDCDecisionSpan, Vec<MCDCBranchSpan>)> {
let Some(decision_ctx) = self.decision_ctx_stack.last_mut() else {
bug!("Unexpected empty decision_ctx_stack")
};
let Some(condition_info) = decision_ctx.decision_stack.pop_back() else {
return (None, None);
let branch = MCDCBranchSpan {
span,
condition_info: ConditionInfo {
condition_id: ConditionId::START,
true_next_id: None,
false_next_id: None,
},
true_marker,
false_marker,
};
degraded_branches.push(branch);
return None;
};
let Some(decision) = decision_ctx.processing_decision.as_mut() else {
bug!("Processing decision should have been created before any conditions are taken");
Expand All @@ -175,24 +189,31 @@ impl MCDCState {
if condition_info.false_next_id.is_none() {
decision.end_markers.push(false_marker);
}
decision_ctx.conditions.push(MCDCBranchSpan {
span,
condition_info,
true_marker,
false_marker,
});

if decision_ctx.decision_stack.is_empty() {
(Some(condition_info), decision_ctx.processing_decision.take())
let conditions = std::mem::take(&mut decision_ctx.conditions);
decision_ctx.processing_decision.take().map(|decision| (decision, conditions))
} else {
(Some(condition_info), None)
None
}
}
}

pub(crate) struct MCDCInfoBuilder {
branch_spans: Vec<MCDCBranchSpan>,
decision_spans: Vec<MCDCDecisionSpan>,
degraded_spans: Vec<MCDCBranchSpan>,
mcdc_spans: Vec<(MCDCDecisionSpan, Vec<MCDCBranchSpan>)>,
state: MCDCState,
}

impl MCDCInfoBuilder {
pub(crate) fn new() -> Self {
Self { branch_spans: vec![], decision_spans: vec![], state: MCDCState::new() }
Self { degraded_spans: vec![], mcdc_spans: vec![], state: MCDCState::new() }
}

pub(crate) fn visit_evaluated_condition(
Expand All @@ -206,50 +227,44 @@ impl MCDCInfoBuilder {
let true_marker = inject_block_marker(source_info, true_block);
let false_marker = inject_block_marker(source_info, false_block);

let decision_depth = self.state.decision_depth();
let (mut condition_info, decision_result) =
self.state.take_condition(true_marker, false_marker);
// take_condition() returns Some for decision_result when the decision stack
// is empty, i.e. when all the conditions of the decision were instrumented,
// and the decision is "complete".
if let Some(decision) = decision_result {
match decision.num_conditions {
if let Some((decision, conditions)) = self.state.try_finish_decision(
source_info.span,
true_marker,
false_marker,
&mut self.degraded_spans,
) {
let num_conditions = conditions.len();
assert_eq!(
num_conditions, decision.num_conditions,
"final number of conditions is not correct"
);
match num_conditions {
0 => {
unreachable!("Decision with no condition is not expected");
}
1..=MAX_CONDITIONS_IN_DECISION => {
self.decision_spans.push(decision);
self.mcdc_spans.push((decision, conditions));
}
_ => {
// Do not generate mcdc mappings and statements for decisions with too many conditions.
// Therefore, first erase the condition info of the (N-1) previous branch spans.
let rebase_idx = self.branch_spans.len() - (decision.num_conditions - 1);
for branch in &mut self.branch_spans[rebase_idx..] {
branch.condition_info = None;
}

// Then, erase this last branch span's info too, for a total of N.
condition_info = None;
self.degraded_spans.extend(conditions);

tcx.dcx().emit_warn(MCDCExceedsConditionLimit {
span: decision.span,
num_conditions: decision.num_conditions,
num_conditions,
max_conditions: MAX_CONDITIONS_IN_DECISION,
});
}
}
}
self.branch_spans.push(MCDCBranchSpan {
span: source_info.span,
condition_info,
true_marker,
false_marker,
decision_depth,
});
}

pub(crate) fn into_done(self) -> (Vec<MCDCDecisionSpan>, Vec<MCDCBranchSpan>) {
(self.decision_spans, self.branch_spans)
pub(crate) fn into_done(
self,
) -> (Vec<(MCDCDecisionSpan, Vec<MCDCBranchSpan>)>, Vec<MCDCBranchSpan>) {
(self.mcdc_spans, self.degraded_spans)
}
}

Expand Down
Loading

0 comments on commit 2c0600a

Please sign in to comment.