about summary refs log tree commit diff
diff options
context:
space:
mode:
authorzhuyunxing <zhuyunxing.zyx@alibaba-inc.com>2024-04-30 10:25:54 +0800
committerzhuyunxing <zhuyunxing.zyx@alibaba-inc.com>2024-04-30 12:17:35 +0800
commit6c8b492f02733120a05b257033db849a2a82c5f4 (patch)
tree7b0f16894feba8cc8f4553b0477261f6a6510eee
parente198c51f16e5ed8480bee455c3a85071cd64d947 (diff)
downloadrust-6c8b492f02733120a05b257033db849a2a82c5f4.tar.gz
rust-6c8b492f02733120a05b257033db849a2a82c5f4.zip
coverage. Split mcdc builder to a sub module of coverageinfo
-rw-r--r--compiler/rustc_mir_build/src/build/coverageinfo.rs273
-rw-r--r--compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs275
2 files changed, 279 insertions, 269 deletions
diff --git a/compiler/rustc_mir_build/src/build/coverageinfo.rs b/compiler/rustc_mir_build/src/build/coverageinfo.rs
index f0c0abfccec..e2a5f97a847 100644
--- a/compiler/rustc_mir_build/src/build/coverageinfo.rs
+++ b/compiler/rustc_mir_build/src/build/coverageinfo.rs
@@ -1,20 +1,16 @@
+mod mcdc;
 use std::assert_matches::assert_matches;
 use std::collections::hash_map::Entry;
-use std::collections::VecDeque;
 
 use rustc_data_structures::fx::FxHashMap;
-use rustc_middle::mir::coverage::{
-    BlockMarkerId, BranchSpan, ConditionId, ConditionInfo, CoverageKind, MCDCBranchSpan,
-    MCDCDecisionSpan,
-};
+use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind};
 use rustc_middle::mir::{self, BasicBlock, SourceInfo, UnOp};
-use rustc_middle::thir::{ExprId, ExprKind, LogicalOp, Thir};
+use rustc_middle::thir::{ExprId, ExprKind, Thir};
 use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::Span;
 
+use crate::build::coverageinfo::mcdc::MCDCInfoBuilder;
 use crate::build::{Builder, CFG};
-use crate::errors::MCDCExceedsConditionNumLimit;
 
 pub(crate) struct BranchInfoBuilder {
     /// Maps condition expressions to their enclosing `!`, for better instrumentation.
@@ -159,241 +155,6 @@ impl BranchInfoBuilder {
     }
 }
 
-/// 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_NUM_IN_DECISION: usize = 6;
-
-#[derive(Default)]
-struct MCDCDecisionCtx {
-    /// To construct condition evaluation tree.
-    decision_stack: VecDeque<ConditionInfo>,
-    processing_decision: Option<MCDCDecisionSpan>,
-}
-
-struct MCDCState {
-    decision_ctx_stack: Vec<MCDCDecisionCtx>,
-}
-
-impl MCDCState {
-    fn new() -> Self {
-        Self { decision_ctx_stack: vec![MCDCDecisionCtx::default()] }
-    }
-
-    /// Decision depth is given as a u16 to reduce the size of the `CoverageKind`,
-    /// as it is very unlikely that the depth ever reaches 2^16.
-    #[inline]
-    fn decision_depth(&self) -> u16 {
-        match u16::try_from(self.decision_ctx_stack.len())
-            .expect(
-                "decision depth did not fit in u16, this is likely to be an instrumentation error",
-            )
-            .checked_sub(1)
-        {
-            Some(d) => d,
-            None => bug!("Unexpected empty decision stack"),
-        }
-    }
-
-    // At first we assign ConditionIds for each sub expression.
-    // If the sub expression is composite, re-assign its ConditionId to its LHS and generate a new ConditionId for its RHS.
-    //
-    // Example: "x = (A && B) || (C && D) || (D && F)"
-    //
-    //      Visit Depth1:
-    //              (A && B) || (C && D) || (D && F)
-    //              ^-------LHS--------^    ^-RHS--^
-    //                      ID=1              ID=2
-    //
-    //      Visit LHS-Depth2:
-    //              (A && B) || (C && D)
-    //              ^-LHS--^    ^-RHS--^
-    //                ID=1        ID=3
-    //
-    //      Visit LHS-Depth3:
-    //               (A && B)
-    //               LHS   RHS
-    //               ID=1  ID=4
-    //
-    //      Visit RHS-Depth3:
-    //                         (C && D)
-    //                         LHS   RHS
-    //                         ID=3  ID=5
-    //
-    //      Visit RHS-Depth2:              (D && F)
-    //                                     LHS   RHS
-    //                                     ID=2  ID=6
-    //
-    //      Visit Depth1:
-    //              (A && B)  || (C && D)  || (D && F)
-    //              ID=1  ID=4   ID=3  ID=5   ID=2  ID=6
-    //
-    // A node ID of '0' always means MC/DC isn't being tracked.
-    //
-    // If a "next" node ID is '0', it means it's the end of the test vector.
-    //
-    // As the compiler tracks expression in pre-order, we can ensure that condition info of parents are always properly assigned when their children are visited.
-    // - 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".
-    // - 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".
-    fn record_conditions(&mut self, op: LogicalOp, span: Span) {
-        let decision_depth = self.decision_depth();
-        let Some(decision_ctx) = self.decision_ctx_stack.last_mut() else {
-            bug!("Unexpected empty decision_ctx_stack")
-        };
-        let decision = match decision_ctx.processing_decision.as_mut() {
-            Some(decision) => {
-                decision.span = decision.span.to(span);
-                decision
-            }
-            None => decision_ctx.processing_decision.insert(MCDCDecisionSpan {
-                span,
-                conditions_num: 0,
-                end_markers: vec![],
-                decision_depth,
-            }),
-        };
-
-        let parent_condition = decision_ctx.decision_stack.pop_back().unwrap_or_default();
-        let lhs_id = if parent_condition.condition_id == ConditionId::NONE {
-            decision.conditions_num += 1;
-            ConditionId::from(decision.conditions_num)
-        } else {
-            parent_condition.condition_id
-        };
-
-        decision.conditions_num += 1;
-        let rhs_condition_id = ConditionId::from(decision.conditions_num);
-
-        let (lhs, rhs) = match op {
-            LogicalOp::And => {
-                let lhs = ConditionInfo {
-                    condition_id: lhs_id,
-                    true_next_id: rhs_condition_id,
-                    false_next_id: parent_condition.false_next_id,
-                };
-                let rhs = ConditionInfo {
-                    condition_id: rhs_condition_id,
-                    true_next_id: parent_condition.true_next_id,
-                    false_next_id: parent_condition.false_next_id,
-                };
-                (lhs, rhs)
-            }
-            LogicalOp::Or => {
-                let lhs = ConditionInfo {
-                    condition_id: lhs_id,
-                    true_next_id: parent_condition.true_next_id,
-                    false_next_id: rhs_condition_id,
-                };
-                let rhs = ConditionInfo {
-                    condition_id: rhs_condition_id,
-                    true_next_id: parent_condition.true_next_id,
-                    false_next_id: parent_condition.false_next_id,
-                };
-                (lhs, rhs)
-            }
-        };
-        // We visit expressions tree in pre-order, so place the left-hand side on the top.
-        decision_ctx.decision_stack.push_back(rhs);
-        decision_ctx.decision_stack.push_back(lhs);
-    }
-
-    fn take_condition(
-        &mut self,
-        true_marker: BlockMarkerId,
-        false_marker: BlockMarkerId,
-    ) -> (Option<ConditionInfo>, Option<MCDCDecisionSpan>) {
-        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 Some(decision) = decision_ctx.processing_decision.as_mut() else {
-            bug!("Processing decision should have been created before any conditions are taken");
-        };
-        if condition_info.true_next_id == ConditionId::NONE {
-            decision.end_markers.push(true_marker);
-        }
-        if condition_info.false_next_id == ConditionId::NONE {
-            decision.end_markers.push(false_marker);
-        }
-
-        if decision_ctx.decision_stack.is_empty() {
-            (Some(condition_info), decision_ctx.processing_decision.take())
-        } else {
-            (Some(condition_info), None)
-        }
-    }
-}
-
-struct MCDCInfoBuilder {
-    branch_spans: Vec<MCDCBranchSpan>,
-    decision_spans: Vec<MCDCDecisionSpan>,
-    state: MCDCState,
-}
-
-impl MCDCInfoBuilder {
-    fn new() -> Self {
-        Self { branch_spans: vec![], decision_spans: vec![], state: MCDCState::new() }
-    }
-
-    fn visit_evaluated_condition(
-        &mut self,
-        tcx: TyCtxt<'_>,
-        source_info: SourceInfo,
-        true_block: BasicBlock,
-        false_block: BasicBlock,
-        mut inject_block_marker: impl FnMut(SourceInfo, BasicBlock) -> BlockMarkerId,
-    ) {
-        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.conditions_num {
-                0 => {
-                    unreachable!("Decision with no condition is not expected");
-                }
-                1..=MAX_CONDITIONS_NUM_IN_DECISION => {
-                    self.decision_spans.push(decision);
-                }
-                _ => {
-                    // Do not generate mcdc mappings and statements for decisions with too many conditions.
-                    let rebase_idx = self.branch_spans.len() - decision.conditions_num + 1;
-                    for branch in &mut self.branch_spans[rebase_idx..] {
-                        branch.condition_info = None;
-                    }
-
-                    // ConditionInfo of this branch shall also be reset.
-                    condition_info = None;
-
-                    tcx.dcx().emit_warn(MCDCExceedsConditionNumLimit {
-                        span: decision.span,
-                        conditions_num: decision.conditions_num,
-                        max_conditions_num: MAX_CONDITIONS_NUM_IN_DECISION,
-                    });
-                }
-            }
-        }
-        self.branch_spans.push(MCDCBranchSpan {
-            span: source_info.span,
-            condition_info,
-            true_marker,
-            false_marker,
-            decision_depth,
-        });
-    }
-
-    fn into_done(self) -> (Vec<MCDCDecisionSpan>, Vec<MCDCBranchSpan>) {
-        (self.decision_spans, self.branch_spans)
-    }
-}
-
 impl Builder<'_, '_> {
     /// If branch coverage is enabled, inject marker statements into `then_block`
     /// and `else_block`, and record their IDs in the table of branch spans.
@@ -434,30 +195,4 @@ impl Builder<'_, '_> {
 
         branch_info.add_two_way_branch(&mut self.cfg, source_info, then_block, else_block);
     }
-
-    pub(crate) fn visit_coverage_branch_operation(&mut self, logical_op: LogicalOp, span: Span) {
-        if let Some(branch_info) = self.coverage_branch_info.as_mut()
-            && let Some(mcdc_info) = branch_info.mcdc_info.as_mut()
-        {
-            mcdc_info.state.record_conditions(logical_op, span);
-        }
-    }
-
-    pub(crate) fn mcdc_increment_depth_if_enabled(&mut self) {
-        if let Some(branch_info) = self.coverage_branch_info.as_mut()
-            && let Some(mcdc_info) = branch_info.mcdc_info.as_mut()
-        {
-            mcdc_info.state.decision_ctx_stack.push(MCDCDecisionCtx::default());
-        };
-    }
-
-    pub(crate) fn mcdc_decrement_depth_if_enabled(&mut self) {
-        if let Some(branch_info) = self.coverage_branch_info.as_mut()
-            && let Some(mcdc_info) = branch_info.mcdc_info.as_mut()
-        {
-            if mcdc_info.state.decision_ctx_stack.pop().is_none() {
-                bug!("Unexpected empty decision stack");
-            }
-        };
-    }
 }
diff --git a/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs b/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs
new file mode 100644
index 00000000000..566dba460d4
--- /dev/null
+++ b/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs
@@ -0,0 +1,275 @@
+use std::collections::VecDeque;
+
+use rustc_middle::mir::coverage::{
+    BlockMarkerId, ConditionId, ConditionInfo, MCDCBranchSpan, MCDCDecisionSpan,
+};
+use rustc_middle::mir::{BasicBlock, SourceInfo};
+use rustc_middle::thir::LogicalOp;
+use rustc_middle::ty::TyCtxt;
+use rustc_span::Span;
+
+use crate::build::Builder;
+use crate::errors::MCDCExceedsConditionNumLimit;
+
+/// 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_NUM_IN_DECISION: usize = 6;
+
+#[derive(Default)]
+struct MCDCDecisionCtx {
+    /// To construct condition evaluation tree.
+    decision_stack: VecDeque<ConditionInfo>,
+    processing_decision: Option<MCDCDecisionSpan>,
+}
+
+struct MCDCState {
+    decision_ctx_stack: Vec<MCDCDecisionCtx>,
+}
+
+impl MCDCState {
+    fn new() -> Self {
+        Self { decision_ctx_stack: vec![MCDCDecisionCtx::default()] }
+    }
+
+    /// Decision depth is given as a u16 to reduce the size of the `CoverageKind`,
+    /// as it is very unlikely that the depth ever reaches 2^16.
+    #[inline]
+    fn decision_depth(&self) -> u16 {
+        match u16::try_from(self.decision_ctx_stack.len())
+            .expect(
+                "decision depth did not fit in u16, this is likely to be an instrumentation error",
+            )
+            .checked_sub(1)
+        {
+            Some(d) => d,
+            None => bug!("Unexpected empty decision stack"),
+        }
+    }
+
+    // At first we assign ConditionIds for each sub expression.
+    // If the sub expression is composite, re-assign its ConditionId to its LHS and generate a new ConditionId for its RHS.
+    //
+    // Example: "x = (A && B) || (C && D) || (D && F)"
+    //
+    //      Visit Depth1:
+    //              (A && B) || (C && D) || (D && F)
+    //              ^-------LHS--------^    ^-RHS--^
+    //                      ID=1              ID=2
+    //
+    //      Visit LHS-Depth2:
+    //              (A && B) || (C && D)
+    //              ^-LHS--^    ^-RHS--^
+    //                ID=1        ID=3
+    //
+    //      Visit LHS-Depth3:
+    //               (A && B)
+    //               LHS   RHS
+    //               ID=1  ID=4
+    //
+    //      Visit RHS-Depth3:
+    //                         (C && D)
+    //                         LHS   RHS
+    //                         ID=3  ID=5
+    //
+    //      Visit RHS-Depth2:              (D && F)
+    //                                     LHS   RHS
+    //                                     ID=2  ID=6
+    //
+    //      Visit Depth1:
+    //              (A && B)  || (C && D)  || (D && F)
+    //              ID=1  ID=4   ID=3  ID=5   ID=2  ID=6
+    //
+    // A node ID of '0' always means MC/DC isn't being tracked.
+    //
+    // If a "next" node ID is '0', it means it's the end of the test vector.
+    //
+    // As the compiler tracks expression in pre-order, we can ensure that condition info of parents are always properly assigned when their children are visited.
+    // - 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".
+    // - 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".
+    fn record_conditions(&mut self, op: LogicalOp, span: Span) {
+        let decision_depth = self.decision_depth();
+        let Some(decision_ctx) = self.decision_ctx_stack.last_mut() else {
+            bug!("Unexpected empty decision_ctx_stack")
+        };
+        let decision = match decision_ctx.processing_decision.as_mut() {
+            Some(decision) => {
+                decision.span = decision.span.to(span);
+                decision
+            }
+            None => decision_ctx.processing_decision.insert(MCDCDecisionSpan {
+                span,
+                conditions_num: 0,
+                end_markers: vec![],
+                decision_depth,
+            }),
+        };
+
+        let parent_condition = decision_ctx.decision_stack.pop_back().unwrap_or_default();
+        let lhs_id = if parent_condition.condition_id == ConditionId::NONE {
+            decision.conditions_num += 1;
+            ConditionId::from(decision.conditions_num)
+        } else {
+            parent_condition.condition_id
+        };
+
+        decision.conditions_num += 1;
+        let rhs_condition_id = ConditionId::from(decision.conditions_num);
+
+        let (lhs, rhs) = match op {
+            LogicalOp::And => {
+                let lhs = ConditionInfo {
+                    condition_id: lhs_id,
+                    true_next_id: rhs_condition_id,
+                    false_next_id: parent_condition.false_next_id,
+                };
+                let rhs = ConditionInfo {
+                    condition_id: rhs_condition_id,
+                    true_next_id: parent_condition.true_next_id,
+                    false_next_id: parent_condition.false_next_id,
+                };
+                (lhs, rhs)
+            }
+            LogicalOp::Or => {
+                let lhs = ConditionInfo {
+                    condition_id: lhs_id,
+                    true_next_id: parent_condition.true_next_id,
+                    false_next_id: rhs_condition_id,
+                };
+                let rhs = ConditionInfo {
+                    condition_id: rhs_condition_id,
+                    true_next_id: parent_condition.true_next_id,
+                    false_next_id: parent_condition.false_next_id,
+                };
+                (lhs, rhs)
+            }
+        };
+        // We visit expressions tree in pre-order, so place the left-hand side on the top.
+        decision_ctx.decision_stack.push_back(rhs);
+        decision_ctx.decision_stack.push_back(lhs);
+    }
+
+    fn take_condition(
+        &mut self,
+        true_marker: BlockMarkerId,
+        false_marker: BlockMarkerId,
+    ) -> (Option<ConditionInfo>, Option<MCDCDecisionSpan>) {
+        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 Some(decision) = decision_ctx.processing_decision.as_mut() else {
+            bug!("Processing decision should have been created before any conditions are taken");
+        };
+        if condition_info.true_next_id == ConditionId::NONE {
+            decision.end_markers.push(true_marker);
+        }
+        if condition_info.false_next_id == ConditionId::NONE {
+            decision.end_markers.push(false_marker);
+        }
+
+        if decision_ctx.decision_stack.is_empty() {
+            (Some(condition_info), decision_ctx.processing_decision.take())
+        } else {
+            (Some(condition_info), None)
+        }
+    }
+}
+
+pub struct MCDCInfoBuilder {
+    branch_spans: Vec<MCDCBranchSpan>,
+    decision_spans: Vec<MCDCDecisionSpan>,
+    state: MCDCState,
+}
+
+impl MCDCInfoBuilder {
+    pub fn new() -> Self {
+        Self { branch_spans: vec![], decision_spans: vec![], state: MCDCState::new() }
+    }
+
+    pub fn visit_evaluated_condition(
+        &mut self,
+        tcx: TyCtxt<'_>,
+        source_info: SourceInfo,
+        true_block: BasicBlock,
+        false_block: BasicBlock,
+        mut inject_block_marker: impl FnMut(SourceInfo, BasicBlock) -> BlockMarkerId,
+    ) {
+        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.conditions_num {
+                0 => {
+                    unreachable!("Decision with no condition is not expected");
+                }
+                1..=MAX_CONDITIONS_NUM_IN_DECISION => {
+                    self.decision_spans.push(decision);
+                }
+                _ => {
+                    // Do not generate mcdc mappings and statements for decisions with too many conditions.
+                    let rebase_idx = self.branch_spans.len() - decision.conditions_num + 1;
+                    for branch in &mut self.branch_spans[rebase_idx..] {
+                        branch.condition_info = None;
+                    }
+
+                    // ConditionInfo of this branch shall also be reset.
+                    condition_info = None;
+
+                    tcx.dcx().emit_warn(MCDCExceedsConditionNumLimit {
+                        span: decision.span,
+                        conditions_num: decision.conditions_num,
+                        max_conditions_num: MAX_CONDITIONS_NUM_IN_DECISION,
+                    });
+                }
+            }
+        }
+        self.branch_spans.push(MCDCBranchSpan {
+            span: source_info.span,
+            condition_info,
+            true_marker,
+            false_marker,
+            decision_depth,
+        });
+    }
+
+    pub fn into_done(self) -> (Vec<MCDCDecisionSpan>, Vec<MCDCBranchSpan>) {
+        (self.decision_spans, self.branch_spans)
+    }
+}
+
+impl Builder<'_, '_> {
+    pub(crate) fn visit_coverage_branch_operation(&mut self, logical_op: LogicalOp, span: Span) {
+        if let Some(branch_info) = self.coverage_branch_info.as_mut()
+            && let Some(mcdc_info) = branch_info.mcdc_info.as_mut()
+        {
+            mcdc_info.state.record_conditions(logical_op, span);
+        }
+    }
+
+    pub(crate) fn mcdc_increment_depth_if_enabled(&mut self) {
+        if let Some(branch_info) = self.coverage_branch_info.as_mut()
+            && let Some(mcdc_info) = branch_info.mcdc_info.as_mut()
+        {
+            mcdc_info.state.decision_ctx_stack.push(MCDCDecisionCtx::default());
+        };
+    }
+
+    pub(crate) fn mcdc_decrement_depth_if_enabled(&mut self) {
+        if let Some(branch_info) = self.coverage_branch_info.as_mut()
+            && let Some(mcdc_info) = branch_info.mcdc_info.as_mut()
+        {
+            if mcdc_info.state.decision_ctx_stack.pop().is_none() {
+                bug!("Unexpected empty decision stack");
+            }
+        };
+    }
+}