about summary refs log tree commit diff
path: root/compiler/rustc_mir_transform/src/coverage/mod.rs
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-04-29 11:54:49 +0000
committerbors <bors@rust-lang.org>2024-04-29 11:54:49 +0000
commit7a5867425959b4b5d69334fa6f02150dc2a5d128 (patch)
tree7074df6418d7e361b71a600181cba27e7eb1f763 /compiler/rustc_mir_transform/src/coverage/mod.rs
parent90846015cc99aa90290c1f4ee95c571fff6901ef (diff)
parenteb422d5c7e6adccf83533ddb5a9836f6575bcd35 (diff)
downloadrust-7a5867425959b4b5d69334fa6f02150dc2a5d128.tar.gz
rust-7a5867425959b4b5d69334fa6f02150dc2a5d128.zip
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.
Diffstat (limited to 'compiler/rustc_mir_transform/src/coverage/mod.rs')
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mod.rs46
1 files changed, 31 insertions, 15 deletions
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index 0b15c52c281..159c099fac5 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -102,12 +102,23 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
 
     inject_mcdc_statements(mir_body, &basic_coverage_blocks, &coverage_spans);
 
+    let mcdc_max_decision_depth = coverage_spans
+        .mappings
+        .iter()
+        .filter_map(|bcb_mapping| match bcb_mapping.kind {
+            BcbMappingKind::MCDCDecision { decision_depth, .. } => Some(decision_depth),
+            _ => None,
+        })
+        .max()
+        .unwrap_or(0);
+
     mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
         function_source_hash: hir_info.function_source_hash,
         num_counters: coverage_counters.num_counters(),
         mcdc_bitmap_bytes: coverage_spans.test_vector_bitmap_bytes(),
         expressions: coverage_counters.into_expressions(),
         mappings,
+        mcdc_max_decision_depth,
     }));
 }
 
@@ -145,16 +156,17 @@ fn create_mappings<'tcx>(
         |BcbMapping { kind: bcb_mapping_kind, span }| {
             let kind = match *bcb_mapping_kind {
                 BcbMappingKind::Code(bcb) => MappingKind::Code(term_for_bcb(bcb)),
-                BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info: None } => {
-                    MappingKind::Branch {
-                        true_term: term_for_bcb(true_bcb),
-                        false_term: term_for_bcb(false_bcb),
-                    }
-                }
+                BcbMappingKind::MCDCBranch {
+                    true_bcb, false_bcb, condition_info: None, ..
+                } => MappingKind::Branch {
+                    true_term: term_for_bcb(true_bcb),
+                    false_term: term_for_bcb(false_bcb),
+                },
                 BcbMappingKind::MCDCBranch {
                     true_bcb,
                     false_bcb,
                     condition_info: Some(mcdc_params),
+                    ..
                 } => MappingKind::MCDCBranch {
                     true_term: term_for_bcb(true_bcb),
                     false_term: term_for_bcb(false_bcb),
@@ -246,24 +258,28 @@ fn inject_mcdc_statements<'tcx>(
     }
 
     // Inject test vector update first because `inject_statement` always insert new statement at head.
-    for (end_bcbs, bitmap_idx) in
+    for (end_bcbs, bitmap_idx, decision_depth) in
         coverage_spans.mappings.iter().filter_map(|mapping| match &mapping.kind {
-            BcbMappingKind::MCDCDecision { end_bcbs, bitmap_idx, .. } => {
-                Some((end_bcbs, *bitmap_idx))
+            BcbMappingKind::MCDCDecision { end_bcbs, bitmap_idx, decision_depth, .. } => {
+                Some((end_bcbs, *bitmap_idx, *decision_depth))
             }
             _ => None,
         })
     {
         for end in end_bcbs {
             let end_bb = basic_coverage_blocks[*end].leader_bb();
-            inject_statement(mir_body, CoverageKind::TestVectorBitmapUpdate { bitmap_idx }, end_bb);
+            inject_statement(
+                mir_body,
+                CoverageKind::TestVectorBitmapUpdate { bitmap_idx, decision_depth },
+                end_bb,
+            );
         }
     }
 
-    for (true_bcb, false_bcb, condition_id) in
+    for (true_bcb, false_bcb, condition_id, decision_depth) in
         coverage_spans.mappings.iter().filter_map(|mapping| match mapping.kind {
-            BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info } => {
-                Some((true_bcb, false_bcb, condition_info?.condition_id))
+            BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info, decision_depth } => {
+                Some((true_bcb, false_bcb, condition_info?.condition_id, decision_depth))
             }
             _ => None,
         })
@@ -271,13 +287,13 @@ fn inject_mcdc_statements<'tcx>(
         let true_bb = basic_coverage_blocks[true_bcb].leader_bb();
         inject_statement(
             mir_body,
-            CoverageKind::CondBitmapUpdate { id: condition_id, value: true },
+            CoverageKind::CondBitmapUpdate { id: condition_id, value: true, decision_depth },
             true_bb,
         );
         let false_bb = basic_coverage_blocks[false_bcb].leader_bb();
         inject_statement(
             mir_body,
-            CoverageKind::CondBitmapUpdate { id: condition_id, value: false },
+            CoverageKind::CondBitmapUpdate { id: condition_id, value: false, decision_depth },
             false_bb,
         );
     }