about summary refs log tree commit diff
path: root/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs
diff options
context:
space:
mode:
authorzhuyunxing <zhuyunxing.zyx@alibaba-inc.com>2024-07-25 15:23:35 +0800
committerzhuyunxing <zhuyunxing.zyx@alibaba-inc.com>2024-10-08 11:15:24 +0800
commit6e3e19f714d0ff938c24901195f2c1be6e6e7f6f (patch)
tree97145e813de4926b8c55742776a1616dbad2038c /compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs
parent99bd601df5f65331b4751217eb533abeca7914cb (diff)
downloadrust-6e3e19f714d0ff938c24901195f2c1be6e6e7f6f.tar.gz
rust-6e3e19f714d0ff938c24901195f2c1be6e6e7f6f.zip
coverage. Adapt to mcdc mapping formats introduced by llvm 19
Diffstat (limited to 'compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs')
-rw-r--r--compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs88
1 files changed, 51 insertions, 37 deletions
diff --git a/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs b/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs
index 5c45a7a33a2..343d4000043 100644
--- a/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs
+++ b/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs
@@ -12,16 +12,16 @@ use rustc_span::Span;
 use crate::build::Builder;
 use crate::errors::MCDCExceedsConditionLimit;
 
-/// The MCDC bitmap scales exponentially (2^n) based on the number of conditions seen,
-/// So llvm sets a maximum value prevents the bitmap footprint from growing too large without the user's knowledge.
-/// This limit may be relaxed if the [upstream change](https://github.com/llvm/llvm-project/pull/82448) is merged.
-const MAX_CONDITIONS_IN_DECISION: usize = 6;
+/// LLVM uses `i16` to represent condition id. Hence `i16::MAX` is the hard limit for number of
+/// conditions in a decision.
+const MAX_CONDITIONS_IN_DECISION: usize = i16::MAX as usize;
 
 #[derive(Default)]
 struct MCDCDecisionCtx {
     /// To construct condition evaluation tree.
     decision_stack: VecDeque<ConditionInfo>,
     processing_decision: Option<MCDCDecisionSpan>,
+    conditions: Vec<MCDCBranchSpan>,
 }
 
 struct MCDCState {
@@ -155,16 +155,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");
@@ -175,24 +188,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(
@@ -206,50 +226,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)
     }
 }