From e1a2babc0628a4d003ed04a7a59603b388cdbf5c Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 28 Dec 2023 13:23:01 +1100 Subject: [PATCH] coverage: Prepare mappings separately from injecting statements These two tasks historically needed to be interleaved, but after various recent changes (including #116046 and #116917) they can now be fully separated. --- .../rustc_mir_transform/src/coverage/mod.rs | 64 +++++++++++-------- .../rustc_mir_transform/src/coverage/spans.rs | 9 ++- 2 files changed, 44 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 078612aa59ca0..95562344ec813 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -8,7 +8,7 @@ mod spans; mod tests; use self::counters::{BcbCounter, CoverageCounters}; -use self::graph::CoverageGraph; +use self::graph::{BasicCoverageBlock, CoverageGraph}; use self::spans::CoverageSpans; use crate::MirPass; @@ -106,7 +106,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { self.coverage_counters .make_bcb_counters(&self.basic_coverage_blocks, bcb_has_coverage_spans); - let mappings = self.create_mappings_and_inject_coverage_statements(&coverage_spans); + let mappings = self.create_mappings(&coverage_spans); + self.inject_coverage_statements(bcb_has_coverage_spans); self.mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo { function_source_hash: self.hir_info.function_source_hash, @@ -116,13 +117,12 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { })); } - /// For each [`BcbCounter`] associated with a BCB node or BCB edge, create - /// any corresponding mappings (for BCB nodes only), and inject any necessary - /// coverage statements into MIR. - fn create_mappings_and_inject_coverage_statements( - &mut self, - coverage_spans: &CoverageSpans, - ) -> Vec { + /// For each coverage span extracted from MIR, create a corresponding + /// mapping. + /// + /// Precondition: All BCBs corresponding to those spans have been given + /// coverage counters. + fn create_mappings(&self, coverage_spans: &CoverageSpans) -> Vec { let source_map = self.tcx.sess.source_map(); let body_span = self.hir_info.body_span; @@ -131,30 +131,42 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { let file_name = Symbol::intern(&source_file.name.for_codegen(self.tcx.sess).to_string_lossy()); - let mut mappings = Vec::new(); + coverage_spans + .bcbs_with_coverage_spans() + // For each BCB with spans, get a coverage term for its counter. + .map(|(bcb, spans)| { + let term = self + .coverage_counters + .bcb_counter(bcb) + .expect("all BCBs with spans were given counters") + .as_term(); + (term, spans) + }) + // Flatten the spans into individual term/span pairs. + .flat_map(|(term, spans)| spans.iter().map(move |&span| (term, span))) + // Convert each span to a code region, and create the final mapping. + .map(|(term, span)| { + let code_region = make_code_region(source_map, file_name, span, body_span); + Mapping { term, code_region } + }) + .collect::>() + } - // Process the counters and spans associated with BCB nodes. + /// For each BCB node or BCB edge that has an associated coverage counter, + /// inject any necessary coverage statements into MIR. + fn inject_coverage_statements( + &mut self, + bcb_has_coverage_spans: impl Fn(BasicCoverageBlock) -> bool, + ) { + // Process the counters associated with BCB nodes. for (bcb, counter_kind) in self.coverage_counters.bcb_node_counters() { - let spans = coverage_spans.spans_for_bcb(bcb); - let has_mappings = !spans.is_empty(); - - // If this BCB has any coverage spans, add corresponding mappings to - // the mappings table. - if has_mappings { - let term = counter_kind.as_term(); - mappings.extend(spans.iter().map(|&span| { - let code_region = make_code_region(source_map, file_name, span, body_span); - Mapping { code_region, term } - })); - } - let do_inject = match counter_kind { // Counter-increment statements always need to be injected. BcbCounter::Counter { .. } => true, // The only purpose of expression-used statements is to detect // when a mapping is unreachable, so we only inject them for // expressions with one or more mappings. - BcbCounter::Expression { .. } => has_mappings, + BcbCounter::Expression { .. } => bcb_has_coverage_spans(bcb), }; if do_inject { inject_statement( @@ -192,8 +204,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // Inject a counter into the newly-created BB. inject_statement(self.mir_body, self.make_mir_coverage_kind(counter_kind), new_bb); } - - mappings } fn make_mir_coverage_kind(&self, counter_kind: &BcbCounter) -> CoverageKind { diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index ae43a18ad4e4f..ed091752187ef 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -48,8 +48,13 @@ impl CoverageSpans { !self.bcb_to_spans[bcb].is_empty() } - pub(super) fn spans_for_bcb(&self, bcb: BasicCoverageBlock) -> &[Span] { - &self.bcb_to_spans[bcb] + pub(super) fn bcbs_with_coverage_spans( + &self, + ) -> impl Iterator { + self.bcb_to_spans.iter_enumerated().filter_map(|(bcb, spans)| { + // Only yield BCBs that have at least one coverage span. + (!spans.is_empty()).then_some((bcb, spans.as_slice())) + }) } }