From cb147883b3cc496c584cbe2ed413071be873c4b6 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 6 Sep 2026 22:23:23 +1000 Subject: [PATCH] Migrate branch coverage to use CoveragePoint and HIR analysis While we still rely on injecting markers into the true and false blocks of a condition during MIR building, this approach avoids the need for a separate side-table, as the association between true/false markers can be recovered by matching their HirId. There is no longer any need to keep track of enclosing `!` expressions, because with HirId we can instead walk up the HIR parent chain to find the original condition. `CoverageEarlyInfo` and `CoverageKind::BlockMarker` have been removed, as they are no longer needed. --- .../src/coverageinfo/mod.rs | 2 +- compiler/rustc_middle/src/mir/coverage.rs | 54 +--- compiler/rustc_middle/src/mir/mod.rs | 11 - compiler/rustc_middle/src/mir/pretty.rs | 27 -- .../src/builder/coverageinfo.rs | 275 +++--------------- .../rustc_mir_build/src/builder/custom/mod.rs | 1 - .../src/builder/matches/mod.rs | 15 +- compiler/rustc_mir_build/src/builder/mod.rs | 11 +- .../src/coverage/branch.rs | 120 ++++++++ .../src/coverage/expansion.rs | 24 +- .../src/coverage/from_mir.rs | 10 +- .../src/coverage/mappings.rs | 50 +--- .../rustc_mir_transform/src/coverage/mod.rs | 1 + tests/coverage/branch/if-let.cov-map | 10 +- tests/coverage/branch/if-let.coverage | 6 +- tests/coverage/branch/let-else.cov-map | 4 +- ...rage_cleanup.main.CleanupPostBorrowck.diff | 6 +- ...erage_cleanup.main.InstrumentCoverage.diff | 6 +- .../coverage/instrument_coverage_cleanup.rs | 4 +- 19 files changed, 225 insertions(+), 412 deletions(-) create mode 100644 compiler/rustc_mir_transform/src/coverage/branch.rs diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index b33e6dbb3409a..2d189266e2ced 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -111,7 +111,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { }; match *kind { - CoverageKind::Point { .. } | CoverageKind::BlockMarker { .. } => unreachable!( + CoverageKind::Point { .. } => unreachable!( "marker statement {kind:?} should have been removed by CleanupPostBorrowck" ), CoverageKind::VirtualCounter { bcb } diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index e48ee0068c300..1e5448749929f 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -8,15 +8,6 @@ use rustc_index::{Idx, IndexVec}; use rustc_macros::{StableHash, TyDecodable, TyEncodable}; use rustc_span::Span; -rustc_index::newtype_index! { - /// Used by [`CoverageKind::BlockMarker`] to mark blocks during THIR-to-MIR - /// lowering, so that those blocks can be identified later. - #[stable_hash] - #[encodable] - #[debug_format = "BlockMarkerId({})"] - pub struct BlockMarkerId {} -} - rustc_index::newtype_index! { /// ID of a coverage counter. Values ascend from 0. /// @@ -76,11 +67,21 @@ impl Debug for CovTerm { pub enum PointKind { /// Inserted just before evaluating an expression. Expr, + /// Inserted when a one-sided `if` expression generates its synthetic `else {}`. /// The absent `else` has no node, so [`HirId`] is the `if` expression. ImplicitElse, + /// Inserted at the end of a function's body. [`HirId`] is the function itself. FunctionEnd, + + /// Inserted into the true-outcome and false-outcome blocks after branching on + /// a boolean condition or a fallible `let`. + /// + /// [`HirId`] is one of: + /// - The boolean expression being tested + /// - The initializer expression (RHS) of a fallible `let` + BranchOutcome { outcome: bool }, } #[derive(Clone, PartialEq, TyEncodable, TyDecodable, StableHash)] @@ -90,12 +91,6 @@ pub enum CoverageKind { /// indicated by [`PointKind`]. Injected during MIR building. Point { point_kind: PointKind, hir_id: HirId }, - /// Marks its enclosing basic block with an ID that can be referred to by - /// side data in [`CoverageEarlyInfo`]. - /// - /// Should be erased before codegen (at some point after `InstrumentCoverage`). - BlockMarker { id: BlockMarkerId }, - /// Marks its enclosing basic block with the ID of the coverage graph node /// that it was part of during the `InstrumentCoverage` MIR pass. /// @@ -110,7 +105,6 @@ impl Debug for CoverageKind { CoverageKind::Point { point_kind, hir_id } => { write!(fmt, "Point({point_kind:?}, {hir_id:?}") } - CoverageKind::BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()), CoverageKind::VirtualCounter { bcb } => write!(fmt, "VirtualCounter({bcb:?})"), } } @@ -122,7 +116,7 @@ impl CoverageKind { /// no longer needed after that pass. pub fn is_removed_after_analysis(&self) -> bool { match self { - CoverageKind::Point { .. } | CoverageKind::BlockMarker { .. } => true, + CoverageKind::Point { .. } => true, CoverageKind::VirtualCounter { .. } => false, } } @@ -184,32 +178,6 @@ pub struct CoverageMirInfo { pub mappings: Vec, } -/// Coverage information for a function, collected in advance at the THIR/MIR -/// boundary during MIR building, and attached to the corresponding `mir::Body`. -/// -/// This side-data is "early" in that it must be collected prior to the main -/// instrumentation step, in contrast to the main [`CoverageMirInfo`] produced -/// by instrumentation itself. -/// -/// Used by the `InstrumentCoverage` MIR pass. -#[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, StableHash)] -pub struct CoverageEarlyInfo { - /// 1 more than the highest-numbered [`CoverageKind::BlockMarker`] that was - /// injected into the MIR body. This makes it possible to allocate per-ID - /// data structures without having to scan the entire body first. - pub num_block_markers: usize, - pub branch_spans: Vec, -} - -#[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, StableHash)] -pub struct BranchSpan { - pub span: Span, - pub true_marker: BlockMarkerId, - pub false_marker: BlockMarkerId, -} - /// Contains information needed during codegen, obtained by inspecting the /// function's MIR after MIR optimizations. /// diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index fd3ba5c7fe02a..7d655263ef9a4 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -310,15 +310,6 @@ pub struct Body<'tcx> { pub tainted_by_errors: Option, - /// Coverage information collected at the THIR/MIR boundary during MIR - /// building, to be used by the `InstrumentCoverage` pass. - /// - /// Only present if coverage is enabled and this function is eligible. - /// Boxed to limit space overhead in non-coverage builds. - #[type_foldable(identity)] - #[type_visitable(ignore)] - pub coverage_early_info: Option>, - /// Per-function coverage information added by the `InstrumentCoverage` /// pass, to be used in conjunction with the coverage statements injected /// into this body's blocks. @@ -369,7 +360,6 @@ impl<'tcx> Body<'tcx> { is_polymorphic: false, injection_phase: None, tainted_by_errors, - coverage_early_info: None, coverage_mir_info: None, }; body.is_polymorphic = body.has_non_region_param(); @@ -400,7 +390,6 @@ impl<'tcx> Body<'tcx> { is_polymorphic: false, injection_phase: None, tainted_by_errors: None, - coverage_early_info: None, coverage_mir_info: None, }; body.is_polymorphic = body.has_non_region_param(); diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 7bb9b4ff8c375..fe09a218db3ad 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -630,9 +630,6 @@ fn write_mir_intro<'tcx>( // Add an empty line before the first block is printed. writeln!(w)?; - if let Some(early_info) = &body.coverage_early_info { - write_coverage_early_info(early_info, w)?; - } if let Some(mir_info) = &body.coverage_mir_info { write_coverage_mir_info(mir_info, w)?; } @@ -640,30 +637,6 @@ fn write_mir_intro<'tcx>( Ok(()) } -fn write_coverage_early_info( - early_info: &coverage::CoverageEarlyInfo, - w: &mut dyn io::Write, -) -> io::Result<()> { - let coverage::CoverageEarlyInfo { num_block_markers: _, branch_spans } = early_info; - - // Only add an extra trailing newline if we printed at least one thing. - let mut did_print = false; - - for coverage::BranchSpan { span, true_marker, false_marker } in branch_spans { - writeln!( - w, - "{INDENT}coverage branch {{ true: {true_marker:?}, false: {false_marker:?} }} => {span:?}", - )?; - did_print = true; - } - - if did_print { - writeln!(w)?; - } - - Ok(()) -} - fn write_coverage_mir_info( mir_info: &coverage::CoverageMirInfo, w: &mut dyn io::Write, diff --git a/compiler/rustc_mir_build/src/builder/coverageinfo.rs b/compiler/rustc_mir_build/src/builder/coverageinfo.rs index bb45cb74ae74f..a6df0a7890fbf 100644 --- a/compiler/rustc_mir_build/src/builder/coverageinfo.rs +++ b/compiler/rustc_mir_build/src/builder/coverageinfo.rs @@ -1,184 +1,9 @@ -use std::assert_matches; -use std::collections::hash_map::Entry; - -use rustc_data_structures::fx::FxHashMap; use rustc_hir::HirId; -use rustc_middle::mir::coverage::{ - BlockMarkerId, BranchSpan, CoverageEarlyInfo, CoverageKind, PointKind, -}; -use rustc_middle::mir::{self, BasicBlock, SourceInfo, Statement, UnOp}; -use rustc_middle::thir::{self, ExprId, ExprKind, Pat, Thir}; -use rustc_middle::ty::TyCtxt; -use rustc_span::def_id::LocalDefId; - -use crate::builder::{Builder, CFG}; - -/// Collects coverage-related information during MIR building, to eventually be -/// turned into a function's [`CoverageEarlyInfo`] when MIR building is complete. -/// -/// FIXME(Zalathar): Now that we have [`CoverageKind::Point`], we should be able -/// to remove this and perform HIR-aware analysis during instrumentation instead. -pub(crate) struct CoverageInfoBuilder { - /// Maps condition expressions to their enclosing `!`, for better instrumentation. - nots: FxHashMap, - - markers: BlockMarkerGen, - - /// Present if branch coverage is enabled. - branch_info: Option, -} - -#[derive(Default)] -struct BranchInfo { - branch_spans: Vec, -} - -#[derive(Clone, Copy)] -struct NotInfo { - /// When visiting the associated expression as a branch condition, treat this - /// enclosing `!` as the branch condition instead. - enclosing_not: ExprId, - /// True if the associated expression is nested within an odd number of `!` - /// expressions relative to `enclosing_not` (inclusive of `enclosing_not`). - is_flipped: bool, -} - -#[derive(Default)] -struct BlockMarkerGen { - num_block_markers: usize, -} - -impl BlockMarkerGen { - fn next_block_marker_id(&mut self) -> BlockMarkerId { - let id = BlockMarkerId::from_usize(self.num_block_markers); - self.num_block_markers += 1; - id - } - - fn inject_block_marker( - &mut self, - cfg: &mut CFG<'_>, - source_info: SourceInfo, - block: BasicBlock, - ) -> BlockMarkerId { - let id = self.next_block_marker_id(); - let marker_statement = mir::Statement::new( - source_info, - mir::StatementKind::Coverage(CoverageKind::BlockMarker { id }), - ); - cfg.push(block, marker_statement); - - id - } -} - -impl CoverageInfoBuilder { - /// Creates a new coverage info builder, but only if coverage instrumentation - /// is enabled and `def_id` represents a function that is eligible for coverage. - pub(crate) fn new_if_enabled(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { - if !tcx.sess.instrument_coverage() || !tcx.is_eligible_for_coverage(def_id) { - return None; - } - - Some(Self { - nots: FxHashMap::default(), - markers: BlockMarkerGen::default(), - branch_info: tcx.sess.instrument_coverage_branch().then(BranchInfo::default), - }) - } - - /// Unary `!` expressions inside an `if` condition are lowered by lowering - /// their argument instead, and then reversing the then/else arms of that `if`. - /// - /// That's awkward for branch coverage instrumentation, so to work around that - /// we pre-emptively visit any affected `!` expressions, and record extra - /// information that [`Builder::visit_coverage_branch_condition`] can use to - /// synthesize branch instrumentation for the enclosing `!`. - pub(crate) fn visit_unary_not(&mut self, thir: &Thir<'_>, unary_not: ExprId) { - assert_matches!(thir[unary_not].kind, ExprKind::Unary { op: UnOp::Not, .. }); - - // The information collected by this visitor is only needed when branch - // coverage or higher is enabled. - if self.branch_info.is_none() { - return; - } - - self.visit_with_not_info( - thir, - unary_not, - // Set `is_flipped: false` for the `!` itself, so that its enclosed - // expression will have `is_flipped: true`. - NotInfo { enclosing_not: unary_not, is_flipped: false }, - ); - } - - fn visit_with_not_info(&mut self, thir: &Thir<'_>, expr_id: ExprId, not_info: NotInfo) { - match self.nots.entry(expr_id) { - // This expression has already been marked by an enclosing `!`. - Entry::Occupied(_) => return, - Entry::Vacant(entry) => entry.insert(not_info), - }; - - match thir[expr_id].kind { - ExprKind::Unary { op: UnOp::Not, arg } => { - // Invert the `is_flipped` flag for the contents of this `!`. - let not_info = NotInfo { is_flipped: !not_info.is_flipped, ..not_info }; - self.visit_with_not_info(thir, arg, not_info); - } - ExprKind::Scope { value, .. } => self.visit_with_not_info(thir, value, not_info), - ExprKind::ValueExpr { source } => self.visit_with_not_info(thir, source, not_info), - // All other expressions (including `&&` and `||`) don't need any - // special handling of their contents, so stop visiting. - _ => {} - } - } - - fn register_two_way_branch<'tcx>( - &mut self, - cfg: &mut CFG<'tcx>, - source_info: SourceInfo, - true_block: BasicBlock, - false_block: BasicBlock, - ) { - // Bail out if branch coverage is not enabled. - let Some(branch_info) = self.branch_info.as_mut() else { return }; - - let true_marker = self.markers.inject_block_marker(cfg, source_info, true_block); - let false_marker = self.markers.inject_block_marker(cfg, source_info, false_block); - - branch_info.branch_spans.push(BranchSpan { - span: source_info.span, - true_marker, - false_marker, - }); - } - - pub(crate) fn into_done(self) -> Box { - 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(); - - // For simplicity, always return an info struct (without Option), even - // if there's nothing interesting in it. - Box::new(CoverageEarlyInfo { num_block_markers, branch_spans }) - } - - pub(crate) fn as_done(&self) -> Box { - let &Self { nots: _, markers: BlockMarkerGen { num_block_markers }, ref branch_info } = - self; +use rustc_middle::mir::coverage::{CoverageKind, PointKind}; +use rustc_middle::mir::{self, BasicBlock, SourceInfo, Statement}; +use rustc_middle::thir; - let branch_spans = branch_info - .as_ref() - .map(|branch_info| branch_info.branch_spans.as_slice()) - .unwrap_or_default() - .to_owned(); - - // For simplicity, always return an info struct (without Option), even - // if there's nothing interesting in it. - Box::new(CoverageEarlyInfo { num_block_markers, branch_spans }) - } -} +use crate::builder::Builder; impl<'tcx> Builder<'_, 'tcx> { /// Does nothing if `-Cinstrument-coverage` is not enabled. @@ -211,8 +36,7 @@ impl<'tcx> Builder<'_, 'tcx> { if !self.tcx.sess.instrument_coverage() { return; } - // Recover the full HirId by combining a local ID with the function's owner ID. - let hir_id = HirId { owner: self.hir_id.owner, local_id: if_expr.temp_scope_id }; + let hir_id = self.recover_hir_id_for_expr(if_expr); self.push_coverage_point_inner(block, source_info, PointKind::ImplicitElse, hir_id); } @@ -232,6 +56,36 @@ impl<'tcx> Builder<'_, 'tcx> { self.push_coverage_point_inner(block, source_info, PointKind::FunctionEnd, fn_hir_id); } + /// Does nothing if branch coverage is not enabled. + /// + /// Otherwise, pushes marker statements to `true_block` and `false_block` + /// indicating that a branch to one of those blocks occurred due to inspection + /// of `scrutinee_expr`. + pub(crate) fn push_coverage_points_for_branch_outcomes( + &mut self, + scrutinee_expr: &thir::Expr<'tcx>, + true_block: BasicBlock, + false_block: BasicBlock, + ) { + if !self.tcx.sess.instrument_coverage_branch() { + return; + } + + let hir_id = self.recover_hir_id_for_expr(scrutinee_expr); + let source_info = self.source_info(scrutinee_expr.span); + let pk_branch_outcome = |outcome: bool| PointKind::BranchOutcome { outcome }; + self.push_coverage_point_inner(true_block, source_info, pk_branch_outcome(true), hir_id); + self.push_coverage_point_inner(false_block, source_info, pk_branch_outcome(false), hir_id); + } + + /// Recovers the full [`HirId`] for a THIR expression by combining its local ID + /// with the current function's owner ID. + fn recover_hir_id_for_expr(&self, expr: &thir::Expr<'tcx>) -> HirId { + // Note that we can't call `hir_id.expect_owner()`, because it would fail + // if we're inside a closure, for example. + HirId { owner: self.hir_id.owner, local_id: expr.temp_scope_id } + } + fn push_coverage_point_inner( &mut self, block: BasicBlock, @@ -252,30 +106,28 @@ impl<'tcx> Builder<'_, 'tcx> { /// that will let us track the value of the condition in `place`. pub(crate) fn visit_coverage_standalone_condition( &mut self, - mut expr_id: ExprId, // Expression giving the span of the condition + expr_id: thir::ExprId, // Expression being inspected place: mir::Place<'tcx>, // Already holds the boolean condition value block: &mut BasicBlock, ) { // Bail out if condition coverage is not enabled for this function. - let Some(coverage_info) = self.coverage_info.as_mut() else { return }; if !self.tcx.sess.instrument_coverage_condition() { return; }; // Remove any wrappers, so that we can inspect the real underlying expression. - while let ExprKind::ValueExpr { source: inner } | ExprKind::Scope { value: inner, .. } = - self.thir[expr_id].kind + let mut expr = &self.thir[expr_id]; + while let thir::ExprKind::ValueExpr { source: inner } + | thir::ExprKind::Scope { value: inner, .. } = expr.kind { - expr_id = inner; + expr = &self.thir[inner]; } // If the expression is a lazy logical op, it will naturally get branch // coverage as part of its normal lowering, so we can disregard it here. - if let ExprKind::LogicalOp { .. } = self.thir[expr_id].kind { + if let thir::ExprKind::LogicalOp { .. } = expr.kind { return; } - let source_info = SourceInfo { span: self.thir[expr_id].span, scope: self.source_scope }; - // Using the boolean value that has already been stored in `place`, set up // control flow in the shape of a diamond, so that we can place separate // marker statements in the true and false blocks. The coverage MIR pass @@ -288,6 +140,7 @@ impl<'tcx> Builder<'_, 'tcx> { // \ / // join_block + let source_info = self.source_info(expr.span); let true_block = self.cfg.start_new_block(); let false_block = self.cfg.start_new_block(); self.cfg.terminate( @@ -296,7 +149,7 @@ impl<'tcx> Builder<'_, 'tcx> { mir::TerminatorKind::if_(mir::Operand::Copy(place), true_block, false_block), ); - coverage_info.register_two_way_branch(&mut self.cfg, source_info, true_block, false_block); + self.push_coverage_points_for_branch_outcomes(expr, true_block, false_block); let join_block = self.cfg.start_new_block(); self.cfg.goto(true_block, source_info, join_block); @@ -304,46 +157,4 @@ impl<'tcx> Builder<'_, 'tcx> { // Any subsequent codegen in the caller should use the new join block. *block = join_block; } - - /// If branch coverage is enabled, inject marker statements into `true_block` - /// and `false_block`, and record their IDs in the table of branch spans. - pub(crate) fn visit_coverage_branch_condition( - &mut self, - mut expr_id: ExprId, - mut true_block: BasicBlock, - mut false_block: BasicBlock, - ) { - // Bail out if coverage is not enabled for this function. - let Some(coverage_info) = self.coverage_info.as_mut() else { return }; - - // If this condition expression is nested within one or more `!` expressions, - // replace it with the enclosing `!` collected by `visit_unary_not`. - if let Some(&NotInfo { enclosing_not, is_flipped }) = coverage_info.nots.get(&expr_id) { - expr_id = enclosing_not; - if is_flipped { - std::mem::swap(&mut true_block, &mut false_block); - } - } - - let source_info = SourceInfo { span: self.thir[expr_id].span, scope: self.source_scope }; - - coverage_info.register_two_way_branch(&mut self.cfg, source_info, true_block, false_block); - } - - /// If branch coverage is enabled, inject marker statements into `true_block` - /// and `false_block`, and record their IDs in the table of branches. - /// - /// Used to instrument let-else and if-let (including let-chains) for branch coverage. - pub(crate) fn visit_coverage_conditional_let( - &mut self, - pattern: &Pat<'tcx>, // Pattern that has been matched when the true path is taken - true_block: BasicBlock, - false_block: BasicBlock, - ) { - // Bail out if coverage is not enabled for this function. - 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(&mut self.cfg, source_info, true_block, false_block); - } } diff --git a/compiler/rustc_mir_build/src/builder/custom/mod.rs b/compiler/rustc_mir_build/src/builder/custom/mod.rs index 4c74613b79454..ac066cfb82e7a 100644 --- a/compiler/rustc_mir_build/src/builder/custom/mod.rs +++ b/compiler/rustc_mir_build/src/builder/custom/mod.rs @@ -60,7 +60,6 @@ pub(super) fn build_custom_mir<'tcx>( tainted_by_errors: None, injection_phase: None, pass_count: 0, - coverage_early_info: None, coverage_mir_info: None, }; diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 01505fb9ec8ae..fb6d9761cdf29 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -144,14 +144,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // then invert the meaning of the true/false blocks. // This avoids an intermediate temporary for negating the condition value. // See . - - // Improve branch coverage instrumentation by noting conditions - // nested within one or more `!` expressions. - // (Skipped if branch coverage is not enabled.) - if let Some(coverage_info) = this.coverage_info.as_mut() { - coverage_info.visit_unary_not(this.thir, expr_id); - } - let local_scope = this.local_scope(); let (true_block, false_block) = this.in_if_then_scope(local_scope, expr_span, |this| { @@ -205,7 +197,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Record branch coverage info for this condition. // (Does nothing if branch coverage is not enabled.) - this.visit_coverage_branch_condition(expr_id, true_block, false_block); + this.push_coverage_points_for_branch_outcomes(expr, true_block, false_block); let source_info = this.source_info(expr_span); this.cfg.terminate(block, source_info, term); @@ -2329,7 +2321,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { scope_span: Span, declare_let_bindings: DeclareLetBindings, ) -> BlockAnd<()> { - let expr_span = self.thir[expr_id].span; + let expr = &self.thir[expr_id]; + let expr_span = expr.span; let scrutinee = unpack!(block = self.lower_scrutinee(block, expr_id)); let built_tree = self.lower_match_tree( block, @@ -2365,7 +2358,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let success = self.bind_pattern(self.source_info(pat.span), branch, &[], expr_span, None); // If branch coverage is enabled, record this branch. - self.visit_coverage_conditional_let(pat, success, built_tree.otherwise_block); + self.push_coverage_points_for_branch_outcomes(expr, success, built_tree.otherwise_block); success.unit() } diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 00ef35173b4b9..910c049b693e9 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -226,10 +226,6 @@ struct Builder<'a, 'tcx> { // the root (most of them do) and saves us from retracing many sub-paths // many times, and rechecking many nodes. lint_level_roots_cache: GrowableBitSet, - - /// Collects additional coverage information during MIR building. - /// Only present if coverage is enabled and this function is eligible. - coverage_info: Option, } type CaptureMap<'tcx> = SortedIndexMultiMap>; @@ -812,7 +808,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { unit_temp: None, var_debug_info: vec![], lint_level_roots_cache: GrowableBitSet::new_empty(), - coverage_info: coverageinfo::CoverageInfoBuilder::new_if_enabled(tcx, def), }; assert_eq!(builder.cfg.start_new_block(), START_BLOCK); @@ -824,7 +819,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { #[allow(dead_code)] fn dump_for_debugging(&self) { - let mut body = Body::new( + let body = Body::new( MirSource::item(self.def_id.to_def_id()), self.cfg.basic_blocks.clone(), self.source_scopes.clone(), @@ -836,14 +831,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.coroutine.clone(), None, ); - body.coverage_early_info = self.coverage_info.as_ref().map(|b| b.as_done()); let writer = pretty::MirWriter::new(self.tcx); writer.write_mir_fn(&body, &mut std::io::stdout()).unwrap(); } fn finish(self) -> Body<'tcx> { - let mut body = Body::new( + let body = Body::new( MirSource::item(self.def_id.to_def_id()), self.cfg.basic_blocks, self.source_scopes, @@ -855,7 +849,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.coroutine, None, ); - body.coverage_early_info = self.coverage_info.map(|b| b.into_done()); let writer = pretty::MirWriter::new(self.tcx); for (index, block) in body.basic_blocks.iter().enumerate() { diff --git a/compiler/rustc_mir_transform/src/coverage/branch.rs b/compiler/rustc_mir_transform/src/coverage/branch.rs new file mode 100644 index 0000000000000..0bd7d079df3c7 --- /dev/null +++ b/compiler/rustc_mir_transform/src/coverage/branch.rs @@ -0,0 +1,120 @@ +//! Analysis and instrumentation for branch coverage. + +use std::mem; + +use rustc_ast::UnOp; +use rustc_data_structures::fx::FxIndexMap; +use rustc_hir::{self as hir, HirId}; +use rustc_middle::mir; +use rustc_middle::mir::coverage::{BasicCoverageBlock, CoverageKind, PointKind}; +use rustc_middle::ty::TyCtxt; +use rustc_span::Span; + +use crate::coverage::graph::CoverageGraph; + +#[derive(Debug)] +pub(crate) struct BranchSpan { + pub(crate) span: Span, + pub(crate) true_bcb: BasicCoverageBlock, + pub(crate) false_bcb: BasicCoverageBlock, +} + +#[derive(Default)] +struct BranchPair { + true_bcb: Option, + false_bcb: Option, +} + +pub(crate) fn extract_branch_spans<'tcx>( + tcx: TyCtxt<'tcx>, + mir_body: &mir::Body<'tcx>, + graph: &CoverageGraph, +) -> Vec { + let mut map = FxIndexMap::::default(); + + // Scan through MIR basic blocks that are part of the coverage graph, to + // reconstruct pairs of `(true_bcb, false_bcb)` each associated with a HirId. + for (bcb, bcb_data) in graph.iter_enumerated() { + for &bb in &bcb_data.basic_blocks { + for stmt in &mir_body[bb].statements { + if let mir::StatementKind::Coverage(cov_kind) = &stmt.kind + && let &CoverageKind::Point { point_kind, hir_id } = cov_kind + && let PointKind::BranchOutcome { outcome } = point_kind + { + let branch = map.entry(hir_id).or_default(); + if outcome { branch.true_bcb = Some(bcb) } else { branch.false_bcb = Some(bcb) } + } + } + } + } + + // For each branch pair, inspect HIR to find a span to use for the branch mapping. + map.into_iter() + .filter_map(|(hir_id, branch_pair)| choose_branch_span(tcx, hir_id, branch_pair)) + .collect::>() +} + +fn choose_branch_span<'tcx>( + tcx: TyCtxt<'tcx>, + mut hir_id: HirId, + branch_pair: BranchPair, +) -> Option { + let BranchPair { true_bcb, false_bcb } = branch_pair; + + let mut true_bcb = true_bcb?; + let mut false_bcb = false_bcb?; + + if !matches!(tcx.hir_node(hir_id), hir::Node::Expr(_)) { + return None; + } + + let mut parents = tcx.hir_parent_iter(hir_id).peekable(); + + // Check if `hir_id` is the scrutinee of a let-else statement. + // ``` + // let Some(x) = scrutinee_expr else { ... } + // ^^^^^^^^^^^^^^ - if `hir_id` is this expression, + // ^^^^^^^^^^^^^^^^^^^^^^^^ - use this span + // ``` + if let Some(&(_, parent)) = parents.peek() + && let hir::Node::LetStmt(parent_let) = parent + && parent_let.els.is_some() + && let Some(init) = parent_let.init + && init.hir_id == hir_id + { + // The span of the let-else statement includes the whole else block, which + // we don't want. Instead, combine the pattern and initializer spans. + let span = parent_let.pat.span.to(init.span); + return Some(BranchSpan { span, true_bcb, false_bcb }); + } + + // Otherwise, `hir_id` should be a boolean condition, or the scrutinee of a let-expression. + // We might need to traverse up to a node with a more relevant span. + while let Some((_, parent)) = parents.next() + && let hir::Node::Expr(parent_expr) = parent + { + match parent_expr.kind { + // MIR building effectively lowers `if !cond` by treating it as `if cond` + // and then swapping the true/false blocks. Here we undo that. + hir::ExprKind::Unary(UnOp::Not, not_arg) if not_arg.hir_id == hir_id => { + hir_id = parent_expr.hir_id; + mem::swap(&mut true_bcb, &mut false_bcb); + } + // MIR building also discards the no-op cast in `if !(cond as bool)`. + // Without this case, we wouldn't be able to recover the outer `!`. + hir::ExprKind::Cast(cast_arg, _ty) if cast_arg.hir_id == hir_id => { + hir_id = parent_expr.hir_id; + } + // For let-expressions, we recorded the `hir_id` of the scrutinee. + // We want to use the span of the let-expression itself instead. + hir::ExprKind::Let(let_expr) if let_expr.init.hir_id == hir_id => { + hir_id = parent_expr.hir_id; + break; + } + _ => break, + } + } + + let span = tcx.hir_span(hir_id); + Some(BranchSpan { span, true_bcb, false_bcb }) +} diff --git a/compiler/rustc_mir_transform/src/coverage/expansion.rs b/compiler/rustc_mir_transform/src/coverage/expansion.rs index 5e8d261ae1f41..a88030638a2d1 100644 --- a/compiler/rustc_mir_transform/src/coverage/expansion.rs +++ b/compiler/rustc_mir_transform/src/coverage/expansion.rs @@ -1,13 +1,14 @@ use itertools::Itertools; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; use rustc_middle::mir; -use rustc_middle::mir::coverage::{BasicCoverageBlock, BranchSpan}; +use rustc_middle::mir::coverage::BasicCoverageBlock; +use rustc_middle::ty::TyCtxt; use rustc_span::{ExpnKind, Span, SyntaxContext}; -use crate::coverage::from_mir; use crate::coverage::graph::CoverageGraph; use crate::coverage::hir_info::ExtractedHirInfo; use crate::coverage::mappings::MappingsError; +use crate::coverage::{branch, from_mir}; #[derive(Clone, Copy, Debug)] pub(crate) struct SpanWithBcb { @@ -59,8 +60,8 @@ pub(crate) struct ExpnNode { /// creating a single code mapping representing an entire child expansion. pub(crate) minmax_bcbs: Option, - /// Branch spans (recorded during MIR building) belonging to this expansion. - pub(crate) branch_spans: Vec, + /// Branch spans belonging to this expansion. + pub(crate) branch_spans: Vec, /// Hole spans belonging to this expansion, to be carved out from the /// code spans during span refinement. @@ -98,8 +99,9 @@ impl ExpnNode { /// Extracts raw span/BCB pairs from potentially-different syntax contexts, and /// arranges them into an "expansion tree" based on their expansion call-sites. -pub(crate) fn build_expn_tree( - mir_body: &mir::Body<'_>, +pub(crate) fn build_expn_tree<'tcx>( + tcx: TyCtxt<'tcx>, + mir_body: &mir::Body<'tcx>, hir_info: &ExtractedHirInfo, graph: &CoverageGraph, ) -> Result { @@ -171,12 +173,12 @@ pub(crate) fn build_expn_tree( node.hole_spans.push(hole_span); } - // Associate each branch span (recorded during MIR building) with its - // corresponding expansion tree node. - if let Some(early_info) = mir_body.coverage_early_info.as_deref() { - for branch_span in &early_info.branch_spans { + // Associate each branch span with its corresponding expansion tree node. + if tcx.sess.instrument_coverage_branch() { + let branch_spans = branch::extract_branch_spans(tcx, mir_body, graph); + for branch_span in branch_spans { if let Some(node) = nodes.get_mut(&branch_span.span.ctxt()) { - node.branch_spans.push(BranchSpan::clone(branch_span)); + node.branch_spans.push(branch_span); } } } diff --git a/compiler/rustc_mir_transform/src/coverage/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/from_mir.rs index cff81d02f7837..8c18d3b1864f1 100644 --- a/compiler/rustc_mir_transform/src/coverage/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/from_mir.rs @@ -1,5 +1,5 @@ +use rustc_middle::mir; use rustc_middle::mir::coverage::{CoverageKind, PointKind}; -use rustc_middle::mir::{self, Statement, StatementKind}; use rustc_span::Span; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; @@ -47,15 +47,17 @@ pub(crate) fn extract_raw_spans_from_mir<'tcx>( /// return it; otherwise return `None`. fn filtered_statement_span<'tcx>( hir_info: &ExtractedHirInfo, - statement: &Statement<'tcx>, + statement: &mir::Statement<'tcx>, ) -> Option { - let StatementKind::Coverage(CoverageKind::Point { point_kind, hir_id }) = statement.kind else { + let mir::StatementKind::Coverage(CoverageKind::Point { point_kind, hir_id }) = statement.kind + else { return None; }; match point_kind { // These PointKind variants contribute to normal code spans. - // (Other variants added in the future might want to return None here.) PointKind::Expr | PointKind::ImplicitElse | PointKind::FunctionEnd => {} + // Ignore branch-outcome points for normal span extraction. + PointKind::BranchOutcome { .. } => return None, } if hir_info.nodes_to_ignore.contains(&hir_id) { return None; diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs index 4ffc6d294aa88..5ce75ab8e4a1d 100644 --- a/compiler/rustc_mir_transform/src/coverage/mappings.rs +++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs @@ -1,11 +1,9 @@ -use rustc_index::IndexVec; -use rustc_middle::mir::coverage::{ - BlockMarkerId, BranchSpan, CoverageEarlyInfo, CoverageKind, Mapping, MappingKind, -}; -use rustc_middle::mir::{self, BasicBlock, StatementKind}; +use rustc_middle::mir; +use rustc_middle::mir::coverage::{Mapping, MappingKind}; use rustc_middle::ty::TyCtxt; use rustc_span::ExpnKind; +use crate::coverage::branch; use crate::coverage::expansion::{self, ExpnTree}; use crate::coverage::graph::CoverageGraph; use crate::coverage::hir_info::ExtractedHirInfo; @@ -31,14 +29,14 @@ pub(crate) fn extract_mappings_from_mir<'tcx>( hir_info: &ExtractedHirInfo, graph: &CoverageGraph, ) -> Result { - let expn_tree = expansion::build_expn_tree(mir_body, hir_info, graph)?; + let expn_tree = expansion::build_expn_tree(tcx, mir_body, hir_info, graph)?; let mut mappings = vec![]; // Extract ordinary code mappings from MIR statement/terminator spans. extract_refined_covspans(tcx, hir_info, graph, &expn_tree, &mut mappings); - extract_branch_mappings(mir_body, hir_info, graph, &expn_tree, &mut mappings); + extract_branch_mappings(hir_info, &expn_tree, &mut mappings); if mappings.is_empty() { tracing::debug!("no mappings were extracted"); @@ -47,37 +45,11 @@ pub(crate) fn extract_mappings_from_mir<'tcx>( Ok(ExtractedMappings { mappings }) } -fn resolve_block_markers( - early_info: &CoverageEarlyInfo, - mir_body: &mir::Body<'_>, -) -> IndexVec> { - let mut block_markers = IndexVec::>::from_elem_n( - None, - early_info.num_block_markers, - ); - - // Fill out the mapping from block marker IDs to their enclosing blocks. - for (bb, data) in mir_body.basic_blocks.iter_enumerated() { - for statement in &data.statements { - if let StatementKind::Coverage(CoverageKind::BlockMarker { id }) = statement.kind { - block_markers[id] = Some(bb); - } - } - } - - block_markers -} - fn extract_branch_mappings( - mir_body: &mir::Body<'_>, hir_info: &ExtractedHirInfo, - graph: &CoverageGraph, expn_tree: &ExpnTree, mappings: &mut Vec, ) { - let Some(early_info) = mir_body.coverage_early_info.as_deref() else { return }; - let block_markers = resolve_block_markers(early_info, mir_body); - // For now, ignore any branch span that was introduced by // expansion. This makes things like assert macros less noisy. let Some(node) = expn_tree.get(hir_info.body_span.ctxt()) else { return }; @@ -85,14 +57,10 @@ fn extract_branch_mappings( return; } - mappings.extend(node.branch_spans.iter().filter_map( - |&BranchSpan { span, true_marker, false_marker }| try { - let bcb_from_marker = |marker: BlockMarkerId| graph.bcb_from_bb(block_markers[marker]?); - - let true_bcb = bcb_from_marker(true_marker)?; - let false_bcb = bcb_from_marker(false_marker)?; - - Mapping { span, kind: MappingKind::Branch { true_bcb, false_bcb } } + mappings.extend(node.branch_spans.iter().map( + |&branch::BranchSpan { span, true_bcb, false_bcb }| Mapping { + span, + kind: MappingKind::Branch { true_bcb, false_bcb }, }, )); } diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 6c5f8bfe3e7d4..c9790d14f349e 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -8,6 +8,7 @@ use crate::coverage::counters::BcbCountersData; use crate::coverage::graph::CoverageGraph; use crate::coverage::mappings::ExtractedMappings; +mod branch; mod counters; mod expansion; mod from_mir; diff --git a/tests/coverage/branch/if-let.cov-map b/tests/coverage/branch/if-let.cov-map index 6d6f3a6e3432b..53c2fa77d5516 100644 --- a/tests/coverage/branch/if-let.cov-map +++ b/tests/coverage/branch/if-let.cov-map @@ -1,5 +1,5 @@ Function name: if_let::if_let -Raw bytes (48): 0x[01, 01, 01, 01, 05, 08, 01, 0c, 01, 00, 1f, 01, 01, 05, 00, 0e, 01, 02, 08, 00, 1b, 20, 02, 05, 00, 0c, 00, 13, 02, 01, 09, 00, 0f, 05, 02, 09, 00, 14, 01, 02, 05, 00, 10, 01, 01, 01, 00, 02] +Raw bytes (48): 0x[01, 01, 01, 01, 05, 08, 01, 0c, 01, 00, 1f, 01, 01, 05, 00, 0e, 01, 02, 08, 00, 1b, 20, 02, 05, 00, 08, 00, 1b, 02, 01, 09, 00, 0f, 05, 02, 09, 00, 14, 01, 02, 05, 00, 10, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => $DIR/if-let.rs Number of expressions: 1 @@ -8,7 +8,7 @@ Number of file 0 mappings: 8 - Code(Counter(0)) at (prev + 12, 1) to (start + 0, 31) - Code(Counter(0)) at (prev + 1, 5) to (start + 0, 14) - Code(Counter(0)) at (prev + 2, 8) to (start + 0, 27) -- Branch { true: Expression(0, Sub), false: Counter(1) } at (prev + 0, 12) to (start + 0, 19) +- Branch { true: Expression(0, Sub), false: Counter(1) } at (prev + 0, 8) to (start + 0, 27) true = (c0 - c1) false = c1 - Code(Expression(0, Sub)) at (prev + 1, 9) to (start + 0, 15) @@ -19,7 +19,7 @@ Number of file 0 mappings: 8 Highest counter ID seen: c1 Function name: if_let::if_let_chain -Raw bytes (74): 0x[01, 01, 08, 01, 05, 01, 1f, 05, 09, 01, 1f, 05, 09, 01, 1f, 05, 09, 05, 09, 0a, 01, 17, 01, 00, 32, 01, 01, 08, 00, 17, 20, 02, 05, 00, 0c, 00, 13, 02, 01, 0c, 00, 1b, 20, 16, 09, 00, 10, 00, 17, 16, 02, 09, 00, 0f, 16, 01, 09, 00, 0f, 1f, 02, 09, 00, 18, 01, 02, 05, 00, 10, 01, 01, 01, 00, 02] +Raw bytes (74): 0x[01, 01, 08, 01, 05, 01, 1f, 05, 09, 01, 1f, 05, 09, 01, 1f, 05, 09, 05, 09, 0a, 01, 17, 01, 00, 32, 01, 01, 08, 00, 17, 20, 02, 05, 00, 08, 00, 17, 02, 01, 0c, 00, 1b, 20, 16, 09, 00, 0c, 00, 1b, 16, 02, 09, 00, 0f, 16, 01, 09, 00, 0f, 1f, 02, 09, 00, 18, 01, 02, 05, 00, 10, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => $DIR/if-let.rs Number of expressions: 8 @@ -34,12 +34,12 @@ Number of expressions: 8 Number of file 0 mappings: 10 - Code(Counter(0)) at (prev + 23, 1) to (start + 0, 50) - Code(Counter(0)) at (prev + 1, 8) to (start + 0, 23) -- Branch { true: Expression(0, Sub), false: Counter(1) } at (prev + 0, 12) to (start + 0, 19) +- Branch { true: Expression(0, Sub), false: Counter(1) } at (prev + 0, 8) to (start + 0, 23) true = (c0 - c1) false = c1 - Code(Expression(0, Sub)) at (prev + 1, 12) to (start + 0, 27) = (c0 - c1) -- Branch { true: Expression(5, Sub), false: Counter(2) } at (prev + 0, 16) to (start + 0, 23) +- Branch { true: Expression(5, Sub), false: Counter(2) } at (prev + 0, 12) to (start + 0, 27) true = (c0 - (c1 + c2)) false = c2 - Code(Expression(5, Sub)) at (prev + 2, 9) to (start + 0, 15) diff --git a/tests/coverage/branch/if-let.coverage b/tests/coverage/branch/if-let.coverage index 6b086d43e565a..bfde5fa33ba9c 100644 --- a/tests/coverage/branch/if-let.coverage +++ b/tests/coverage/branch/if-let.coverage @@ -14,7 +14,7 @@ LL| | LL| 3| if let Some(x) = input { ------------------ - | Branch (LL:12): [True: 2, False: 1] + | Branch (LL:8): [True: 2, False: 1] ------------------ LL| 2| say(x); LL| | } else { @@ -26,11 +26,11 @@ LL| 15|fn if_let_chain(a: Option<&str>, b: Option<&str>) { LL| 15| if let Some(x) = a ------------------ - | Branch (LL:12): [True: 12, False: 3] + | Branch (LL:8): [True: 12, False: 3] ------------------ LL| 12| && let Some(y) = b ------------------ - | Branch (LL:16): [True: 8, False: 4] + | Branch (LL:12): [True: 8, False: 4] ------------------ LL| | { LL| 8| say(x); diff --git a/tests/coverage/branch/let-else.cov-map b/tests/coverage/branch/let-else.cov-map index df5d53acccb46..1183db1ea3aca 100644 --- a/tests/coverage/branch/let-else.cov-map +++ b/tests/coverage/branch/let-else.cov-map @@ -1,5 +1,5 @@ Function name: let_else::let_else -Raw bytes (48): 0x[01, 01, 01, 01, 05, 08, 01, 0c, 01, 00, 21, 01, 01, 05, 00, 0e, 20, 02, 05, 02, 09, 00, 10, 01, 00, 13, 00, 18, 05, 01, 09, 00, 14, 05, 01, 09, 00, 0f, 02, 03, 05, 00, 0b, 01, 01, 01, 00, 02] +Raw bytes (48): 0x[01, 01, 01, 01, 05, 08, 01, 0c, 01, 00, 21, 01, 01, 05, 00, 0e, 20, 02, 05, 02, 09, 00, 18, 01, 00, 13, 00, 18, 05, 01, 09, 00, 14, 05, 01, 09, 00, 0f, 02, 03, 05, 00, 0b, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => $DIR/let-else.rs Number of expressions: 1 @@ -7,7 +7,7 @@ Number of expressions: 1 Number of file 0 mappings: 8 - Code(Counter(0)) at (prev + 12, 1) to (start + 0, 33) - Code(Counter(0)) at (prev + 1, 5) to (start + 0, 14) -- Branch { true: Expression(0, Sub), false: Counter(1) } at (prev + 2, 9) to (start + 0, 16) +- Branch { true: Expression(0, Sub), false: Counter(1) } at (prev + 2, 9) to (start + 0, 24) true = (c0 - c1) false = c1 - Code(Counter(0)) at (prev + 0, 19) to (start + 0, 24) diff --git a/tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff index 9c5e603c69aec..e20e4e6655b70 100644 --- a/tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff +++ b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff @@ -5,8 +5,6 @@ let mut _0: (); let mut _1: bool; - coverage branch { true: BlockMarkerId(0), false: BlockMarkerId(1) } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0) - coverage Code { bcb: bcb0 } => $DIR/instrument_coverage_cleanup.rs:13:1: 13:10 (#0); coverage Code { bcb: bcb0 } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0); coverage Code { bcb: bcb3 } => $DIR/instrument_coverage_cleanup.rs:14:37: 14:39 (#0); @@ -38,7 +36,7 @@ bb2: { Coverage::VirtualCounter(bcb1); -- Coverage::BlockMarker(1); +- Coverage::Point(BranchOutcome { outcome: true }, HirId(DefId(0:3 ~ instrument_coverage_cleanup[820a]::main).4); - Coverage::Point(ImplicitElse, HirId(DefId(0:3 ~ instrument_coverage_cleanup[820a]::main).2); + nop; + nop; @@ -48,7 +46,7 @@ bb3: { Coverage::VirtualCounter(bcb3); -- Coverage::BlockMarker(0); +- Coverage::Point(BranchOutcome { outcome: false }, HirId(DefId(0:3 ~ instrument_coverage_cleanup[820a]::main).4); - Coverage::Point(Expr, HirId(DefId(0:3 ~ instrument_coverage_cleanup[820a]::main).11); + nop; + nop; diff --git a/tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff index 4365ae794d8ce..a12a2f0ee825d 100644 --- a/tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff +++ b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff @@ -5,8 +5,6 @@ let mut _0: (); let mut _1: bool; - coverage branch { true: BlockMarkerId(0), false: BlockMarkerId(1) } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0) - + coverage Code { bcb: bcb0 } => $DIR/instrument_coverage_cleanup.rs:13:1: 13:10 (#0); + coverage Code { bcb: bcb0 } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0); + coverage Code { bcb: bcb3 } => $DIR/instrument_coverage_cleanup.rs:14:37: 14:39 (#0); @@ -32,7 +30,7 @@ bb2: { + Coverage::VirtualCounter(bcb1); - Coverage::BlockMarker(1); + Coverage::Point(BranchOutcome { outcome: true }, HirId(DefId(0:3 ~ instrument_coverage_cleanup[820a]::main).4); Coverage::Point(ImplicitElse, HirId(DefId(0:3 ~ instrument_coverage_cleanup[820a]::main).2); _0 = const (); goto -> bb4; @@ -40,7 +38,7 @@ bb3: { + Coverage::VirtualCounter(bcb3); - Coverage::BlockMarker(0); + Coverage::Point(BranchOutcome { outcome: false }, HirId(DefId(0:3 ~ instrument_coverage_cleanup[820a]::main).4); Coverage::Point(Expr, HirId(DefId(0:3 ~ instrument_coverage_cleanup[820a]::main).11); _0 = const (); goto -> bb4; diff --git a/tests/mir-opt/coverage/instrument_coverage_cleanup.rs b/tests/mir-opt/coverage/instrument_coverage_cleanup.rs index d725148e52707..33bc81d06741d 100644 --- a/tests/mir-opt/coverage/instrument_coverage_cleanup.rs +++ b/tests/mir-opt/coverage/instrument_coverage_cleanup.rs @@ -2,7 +2,7 @@ // inserted during MIR building (after InstrumentCoverage is done with them), // but leaves the statements that were added by InstrumentCoverage. // -// Removed statement kinds: Point, BlockMarker +// Removed statement kinds: Point // Retained statement kinds: VirtualCounter //@ test-mir-pass: InstrumentCoverage @@ -15,7 +15,5 @@ fn main() { } // CHECK-NOT: Coverage::Point -// CHECK-NOT: Coverage::BlockMarker // CHECK: Coverage::VirtualCounter // CHECK-NOT: Coverage::Point -// CHECK-NOT: Coverage::BlockMarker