1
Fork 0

Auto merge of #124255 - RenjiSann:renji/mcdc-nested-expressions, r=Zalathar

MCDC coverage: support nested decision coverage

#123409 provided the initial MCDC coverage implementation.

As referenced in #124144, it does not currently support "nested" decisions, like the following example :

```rust
fn nested_if_in_condition(a: bool, b: bool, c: bool) {
    if a && if b || c { true } else { false } {
        say("yes");
    } else {
        say("no");
    }
}
```

Note that there is an if-expression (`if b || c ...`) embedded inside a boolean expression in the decision of an outer if-expression.

This PR proposes a workaround for this cases, by introducing a Decision context stack, and by handing several `temporary condition bitmaps` instead of just one.
When instrumenting boolean expressions, if the current node is a leaf condition (i.e. not a `||`/`&&` logical operator nor a `!` not operator), we insert a new decision context, such that if there are more boolean expressions inside the condition, they are handled as separate expressions.

On the codegen LLVM side, we allocate as many `temp_cond_bitmap`s as necessary to handle the maximum encountered decision depth.
This commit is contained in:
bors 2024-04-29 11:54:49 +00:00
commit 7a58674259
12 changed files with 699 additions and 70 deletions

View file

@ -132,7 +132,7 @@ pub enum CoverageKind {
///
/// If this statement does not survive MIR optimizations, the condition would never be
/// taken as evaluated.
CondBitmapUpdate { id: ConditionId, value: bool },
CondBitmapUpdate { id: ConditionId, value: bool, decision_depth: u16 },
/// Marks the point in MIR control flow represented by a evaluated decision.
///
@ -140,7 +140,7 @@ pub enum CoverageKind {
///
/// If this statement does not survive MIR optimizations, the decision would never be
/// taken as evaluated.
TestVectorBitmapUpdate { bitmap_idx: u32 },
TestVectorBitmapUpdate { bitmap_idx: u32, decision_depth: u16 },
}
impl Debug for CoverageKind {
@ -151,11 +151,17 @@ impl Debug for CoverageKind {
BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()),
CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()),
ExpressionUsed { id } => write!(fmt, "ExpressionUsed({:?})", id.index()),
CondBitmapUpdate { id, value } => {
write!(fmt, "CondBitmapUpdate({:?}, {:?})", id.index(), value)
CondBitmapUpdate { id, value, decision_depth } => {
write!(
fmt,
"CondBitmapUpdate({:?}, {:?}, depth={:?})",
id.index(),
value,
decision_depth
)
}
TestVectorBitmapUpdate { bitmap_idx } => {
write!(fmt, "TestVectorUpdate({:?})", bitmap_idx)
TestVectorBitmapUpdate { bitmap_idx, decision_depth } => {
write!(fmt, "TestVectorUpdate({:?}, depth={:?})", bitmap_idx, decision_depth)
}
}
}
@ -269,6 +275,9 @@ pub struct FunctionCoverageInfo {
pub mcdc_bitmap_bytes: u32,
pub expressions: IndexVec<ExpressionId, Expression>,
pub mappings: Vec<Mapping>,
/// The depth of the deepest decision is used to know how many
/// temp condbitmaps should be allocated for the function.
pub mcdc_max_decision_depth: u16,
}
/// Branch information recorded during THIR-to-MIR lowering, and stored in MIR.
@ -319,6 +328,7 @@ pub struct MCDCBranchSpan {
pub condition_info: Option<ConditionInfo>,
pub true_marker: BlockMarkerId,
pub false_marker: BlockMarkerId,
pub decision_depth: u16,
}
#[derive(Copy, Clone, Debug)]
@ -334,4 +344,5 @@ pub struct MCDCDecisionSpan {
pub span: Span,
pub conditions_num: usize,
pub end_markers: Vec<BlockMarkerId>,
pub decision_depth: u16,
}

View file

@ -496,20 +496,27 @@ fn write_coverage_branch_info(
)?;
}
for coverage::MCDCBranchSpan { span, condition_info, true_marker, false_marker } in
mcdc_branch_spans
for coverage::MCDCBranchSpan {
span,
condition_info,
true_marker,
false_marker,
decision_depth,
} in mcdc_branch_spans
{
writeln!(
w,
"{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",
"{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?}, depth: {decision_depth:?} }} => {span:?}",
condition_info.map(|info| info.condition_id)
)?;
}
for coverage::MCDCDecisionSpan { span, conditions_num, end_markers } in mcdc_decision_spans {
for coverage::MCDCDecisionSpan { span, conditions_num, end_markers, decision_depth } in
mcdc_decision_spans
{
writeln!(
w,
"{INDENT}coverage mcdc decision {{ conditions_num: {conditions_num:?}, end: {end_markers:?} }} => {span:?}"
"{INDENT}coverage mcdc decision {{ conditions_num: {conditions_num:?}, end: {end_markers:?}, depth: {decision_depth:?} }} => {span:?}"
)?;
}