diff options
Diffstat (limited to 'compiler/rustc_mir_build/src')
| -rw-r--r-- | compiler/rustc_mir_build/src/builder/coverageinfo.rs | 59 | ||||
| -rw-r--r-- | compiler/rustc_mir_build/src/builder/coverageinfo/mcdc.rs | 295 | ||||
| -rw-r--r-- | compiler/rustc_mir_build/src/builder/expr/into.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_mir_build/src/builder/matches/mod.rs | 10 | ||||
| -rw-r--r-- | compiler/rustc_mir_build/src/errors.rs | 9 |
5 files changed, 7 insertions, 368 deletions
diff --git a/compiler/rustc_mir_build/src/builder/coverageinfo.rs b/compiler/rustc_mir_build/src/builder/coverageinfo.rs index aa43b273cff..14199c20921 100644 --- a/compiler/rustc_mir_build/src/builder/coverageinfo.rs +++ b/compiler/rustc_mir_build/src/builder/coverageinfo.rs @@ -8,11 +8,8 @@ use rustc_middle::thir::{ExprId, ExprKind, Pat, Thir}; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::LocalDefId; -use crate::builder::coverageinfo::mcdc::MCDCInfoBuilder; use crate::builder::{Builder, CFG}; -mod mcdc; - /// Collects coverage-related information during MIR building, to eventually be /// turned into a function's [`CoverageInfoHi`] when MIR building is complete. pub(crate) struct CoverageInfoBuilder { @@ -23,8 +20,6 @@ pub(crate) struct CoverageInfoBuilder { /// Present if branch coverage is enabled. branch_info: Option<BranchInfo>, - /// Present if MC/DC coverage is enabled. - mcdc_info: Option<MCDCInfoBuilder>, } #[derive(Default)] @@ -83,7 +78,6 @@ impl CoverageInfoBuilder { nots: FxHashMap::default(), markers: BlockMarkerGen::default(), branch_info: tcx.sess.instrument_coverage_branch().then(BranchInfo::default), - mcdc_info: tcx.sess.instrument_coverage_mcdc().then(MCDCInfoBuilder::new), }) } @@ -135,26 +129,11 @@ impl CoverageInfoBuilder { fn register_two_way_branch<'tcx>( &mut self, - tcx: TyCtxt<'tcx>, cfg: &mut CFG<'tcx>, source_info: SourceInfo, true_block: BasicBlock, false_block: BasicBlock, ) { - // Separate path for handling branches when MC/DC is enabled. - if let Some(mcdc_info) = self.mcdc_info.as_mut() { - let inject_block_marker = - |source_info, block| self.markers.inject_block_marker(cfg, source_info, block); - mcdc_info.visit_evaluated_condition( - tcx, - source_info, - true_block, - false_block, - inject_block_marker, - ); - return; - } - // Bail out if branch coverage is not enabled. let Some(branch_info) = self.branch_info.as_mut() else { return }; @@ -169,23 +148,14 @@ impl CoverageInfoBuilder { } pub(crate) fn into_done(self) -> Box<CoverageInfoHi> { - let Self { nots: _, markers: BlockMarkerGen { num_block_markers }, branch_info, mcdc_info } = - self; + let Self { nots: _, markers: BlockMarkerGen { num_block_markers }, branch_info } = self; let branch_spans = branch_info.map(|branch_info| branch_info.branch_spans).unwrap_or_default(); - 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_degraded_branch_spans, - mcdc_spans, - }) + Box::new(CoverageInfoHi { num_block_markers, branch_spans }) } } @@ -238,14 +208,7 @@ impl<'tcx> Builder<'_, 'tcx> { mir::TerminatorKind::if_(mir::Operand::Copy(place), true_block, false_block), ); - // Separate path for handling branches when MC/DC is enabled. - coverage_info.register_two_way_branch( - self.tcx, - &mut self.cfg, - source_info, - true_block, - false_block, - ); + coverage_info.register_two_way_branch(&mut self.cfg, source_info, true_block, false_block); let join_block = self.cfg.start_new_block(); self.cfg.goto(true_block, source_info, join_block); @@ -276,13 +239,7 @@ impl<'tcx> Builder<'_, 'tcx> { let source_info = SourceInfo { span: self.thir[expr_id].span, scope: self.source_scope }; - coverage_info.register_two_way_branch( - self.tcx, - &mut self.cfg, - source_info, - then_block, - else_block, - ); + coverage_info.register_two_way_branch(&mut self.cfg, source_info, then_block, else_block); } /// If branch coverage is enabled, inject marker statements into `true_block` @@ -299,12 +256,6 @@ impl<'tcx> Builder<'_, 'tcx> { let Some(coverage_info) = self.coverage_info.as_mut() else { return }; let source_info = SourceInfo { span: pattern.span, scope: self.source_scope }; - coverage_info.register_two_way_branch( - self.tcx, - &mut self.cfg, - source_info, - true_block, - false_block, - ); + coverage_info.register_two_way_branch(&mut self.cfg, source_info, true_block, false_block); } } diff --git a/compiler/rustc_mir_build/src/builder/coverageinfo/mcdc.rs b/compiler/rustc_mir_build/src/builder/coverageinfo/mcdc.rs deleted file mode 100644 index 6b4871dc1fc..00000000000 --- a/compiler/rustc_mir_build/src/builder/coverageinfo/mcdc.rs +++ /dev/null @@ -1,295 +0,0 @@ -use std::collections::VecDeque; - -use rustc_middle::bug; -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::builder::Builder; -use crate::errors::MCDCExceedsConditionLimit; - -/// 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 { - 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, - num_conditions: 0, - end_markers: vec![], - decision_depth, - }), - }; - - let parent_condition = decision_ctx.decision_stack.pop_back().unwrap_or_else(|| { - assert_eq!( - decision.num_conditions, 0, - "decision stack must be empty only for empty decision" - ); - decision.num_conditions += 1; - ConditionInfo { - condition_id: ConditionId::START, - true_next_id: None, - false_next_id: None, - } - }); - let lhs_id = parent_condition.condition_id; - - let rhs_condition_id = ConditionId::from(decision.num_conditions); - decision.num_conditions += 1; - let (lhs, rhs) = match op { - LogicalOp::And => { - let lhs = ConditionInfo { - condition_id: lhs_id, - true_next_id: Some(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: Some(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 try_finish_decision( - &mut self, - span: Span, - true_marker: BlockMarkerId, - false_marker: BlockMarkerId, - 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 { - 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"); - }; - if condition_info.true_next_id.is_none() { - decision.end_markers.push(true_marker); - } - 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() { - let conditions = std::mem::take(&mut decision_ctx.conditions); - decision_ctx.processing_decision.take().map(|decision| (decision, conditions)) - } else { - None - } - } -} - -pub(crate) struct MCDCInfoBuilder { - degraded_spans: Vec<MCDCBranchSpan>, - mcdc_spans: Vec<(MCDCDecisionSpan, Vec<MCDCBranchSpan>)>, - state: MCDCState, -} - -impl MCDCInfoBuilder { - pub(crate) fn new() -> Self { - Self { degraded_spans: vec![], mcdc_spans: vec![], state: MCDCState::new() } - } - - pub(crate) 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); - - // 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, 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.mcdc_spans.push((decision, conditions)); - } - _ => { - self.degraded_spans.extend(conditions); - - tcx.dcx().emit_warn(MCDCExceedsConditionLimit { - span: decision.span, - num_conditions, - max_conditions: MAX_CONDITIONS_IN_DECISION, - }); - } - } - } - } - - pub(crate) fn into_done( - self, - ) -> (Vec<(MCDCDecisionSpan, Vec<MCDCBranchSpan>)>, Vec<MCDCBranchSpan>) { - (self.mcdc_spans, self.degraded_spans) - } -} - -impl Builder<'_, '_> { - pub(crate) fn visit_coverage_branch_operation(&mut self, logical_op: LogicalOp, span: Span) { - if let Some(coverage_info) = self.coverage_info.as_mut() - && let Some(mcdc_info) = coverage_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(coverage_info) = self.coverage_info.as_mut() - && let Some(mcdc_info) = coverage_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(coverage_info) = self.coverage_info.as_mut() - && let Some(mcdc_info) = coverage_info.mcdc_info.as_mut() - && mcdc_info.state.decision_ctx_stack.pop().is_none() - { - bug!("Unexpected empty decision stack"); - }; - } -} diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 82b883a99a1..eb99c184bd2 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -159,8 +159,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let condition_scope = this.local_scope(); let source_info = this.source_info(expr.span); - this.visit_coverage_branch_operation(op, expr.span); - // We first evaluate the left-hand side of the predicate ... let (then_block, else_block) = this.in_if_then_scope(condition_scope, expr.span, |this| { diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 2c29b862841..9cdfb863330 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -125,15 +125,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let expr_span = expr.span; match expr.kind { - ExprKind::LogicalOp { op: op @ LogicalOp::And, lhs, rhs } => { - this.visit_coverage_branch_operation(op, expr_span); + ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => { let lhs_then_block = this.then_else_break_inner(block, lhs, args).into_block(); let rhs_then_block = this.then_else_break_inner(lhs_then_block, rhs, args).into_block(); rhs_then_block.unit() } - ExprKind::LogicalOp { op: op @ LogicalOp::Or, lhs, rhs } => { - this.visit_coverage_branch_operation(op, expr_span); + ExprKind::LogicalOp { op: LogicalOp::Or, lhs, rhs } => { let local_scope = this.local_scope(); let (lhs_success_block, failure_block) = this.in_if_then_scope(local_scope, expr_span, |this| { @@ -214,9 +212,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let temp_scope = args.temp_scope_override.unwrap_or_else(|| this.local_scope()); let mutability = Mutability::Mut; - // Increment the decision depth, in case we encounter boolean expressions - // further down. - this.mcdc_increment_depth_if_enabled(); let place = unpack!( block = this.as_temp( block, @@ -228,7 +223,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { mutability ) ); - this.mcdc_decrement_depth_if_enabled(); let operand = Operand::Move(Place::from(place)); diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 1a52c6c85cb..58c3de4a8b5 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -976,15 +976,6 @@ pub(crate) struct NonEmptyNeverPattern<'tcx> { } #[derive(Diagnostic)] -#[diag(mir_build_exceeds_mcdc_condition_limit)] -pub(crate) struct MCDCExceedsConditionLimit { - #[primary_span] - pub(crate) span: Span, - pub(crate) num_conditions: usize, - pub(crate) max_conditions: usize, -} - -#[derive(Diagnostic)] #[diag(mir_build_pattern_not_covered, code = E0005)] pub(crate) struct PatternNotCovered<'s, 'tcx> { #[primary_span] |
