1
Fork 0

coverage: Eagerly convert coverage spans to a simpler form

This commit is contained in:
Zalathar 2024-06-16 01:25:05 +10:00
parent bf74fb1d2f
commit 88ade9c740
2 changed files with 55 additions and 49 deletions

View file

@ -28,11 +28,15 @@ pub(super) fn extract_refined_covspans(
let ExtractedCovspans { mut covspans, mut holes } = let ExtractedCovspans { mut covspans, mut holes } =
extract_covspans_and_holes_from_mir(mir_body, hir_info, basic_coverage_blocks); extract_covspans_and_holes_from_mir(mir_body, hir_info, basic_coverage_blocks);
// First, perform the passes that need macro information.
covspans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb)); covspans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb));
remove_unwanted_macro_spans(&mut covspans); remove_unwanted_macro_spans(&mut covspans);
split_visible_macro_spans(&mut covspans); split_visible_macro_spans(&mut covspans);
let compare_covspans = |a: &SpanFromMir, b: &SpanFromMir| { // We no longer need the extra information in `SpanFromMir`, so convert to `Covspan`.
let mut covspans = covspans.into_iter().map(SpanFromMir::into_covspan).collect::<Vec<_>>();
let compare_covspans = |a: &Covspan, b: &Covspan| {
compare_spans(a.span, b.span) compare_spans(a.span, b.span)
// After deduplication, we want to keep only the most-dominated BCB. // After deduplication, we want to keep only the most-dominated BCB.
.then_with(|| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb).reverse()) .then_with(|| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb).reverse())
@ -53,7 +57,7 @@ pub(super) fn extract_refined_covspans(
// and grouping them in buckets separated by the holes. // and grouping them in buckets separated by the holes.
let mut input_covspans = VecDeque::from(covspans); let mut input_covspans = VecDeque::from(covspans);
let mut fragments: Vec<SpanFromMir> = vec![]; let mut fragments = vec![];
// For each hole: // For each hole:
// - Identify the spans that are entirely or partly before the hole. // - Identify the spans that are entirely or partly before the hole.
@ -88,7 +92,7 @@ pub(super) fn extract_refined_covspans(
covspans.sort_by(compare_covspans); covspans.sort_by(compare_covspans);
let covspans = refine_sorted_spans(covspans); let covspans = refine_sorted_spans(covspans);
code_mappings.extend(covspans.into_iter().map(|RefinedCovspan { span, bcb }| { code_mappings.extend(covspans.into_iter().map(|Covspan { span, bcb }| {
// Each span produced by the refiner represents an ordinary code region. // Each span produced by the refiner represents an ordinary code region.
mappings::CodeMapping { span, bcb } mappings::CodeMapping { span, bcb }
})); }));
@ -145,23 +149,6 @@ fn split_visible_macro_spans(covspans: &mut Vec<SpanFromMir>) {
covspans.extend(extra_spans); covspans.extend(extra_spans);
} }
#[derive(Debug)]
struct RefinedCovspan {
span: Span,
bcb: BasicCoverageBlock,
}
impl RefinedCovspan {
fn is_mergeable(&self, other: &Self) -> bool {
self.bcb == other.bcb
}
fn merge_from(&mut self, other: &Self) {
debug_assert!(self.is_mergeable(other));
self.span = self.span.to(other.span);
}
}
/// Similar to `.drain(..)`, but stops just before it would remove an item not /// Similar to `.drain(..)`, but stops just before it would remove an item not
/// satisfying the predicate. /// satisfying the predicate.
fn drain_front_while<'a, T>( fn drain_front_while<'a, T>(
@ -175,18 +162,18 @@ fn drain_front_while<'a, T>(
/// those spans by removing spans that overlap in unwanted ways, and by merging /// those spans by removing spans that overlap in unwanted ways, and by merging
/// compatible adjacent spans. /// compatible adjacent spans.
#[instrument(level = "debug")] #[instrument(level = "debug")]
fn refine_sorted_spans(sorted_spans: Vec<SpanFromMir>) -> Vec<RefinedCovspan> { fn refine_sorted_spans(sorted_spans: Vec<Covspan>) -> Vec<Covspan> {
// Holds spans that have been read from the input vector, but haven't yet // Holds spans that have been read from the input vector, but haven't yet
// been committed to the output vector. // been committed to the output vector.
let mut pending = vec![]; let mut pending = vec![];
let mut refined = vec![]; let mut refined = vec![];
for curr in sorted_spans { for curr in sorted_spans {
pending.retain(|prev: &SpanFromMir| { pending.retain(|prev: &Covspan| {
if prev.span.hi() <= curr.span.lo() { if prev.span.hi() <= curr.span.lo() {
// There's no overlap between the previous/current covspans, // There's no overlap between the previous/current covspans,
// so move the previous one into the refined list. // so move the previous one into the refined list.
refined.push(RefinedCovspan { span: prev.span, bcb: prev.bcb }); refined.push(prev.clone());
false false
} else { } else {
// Otherwise, retain the previous covspan only if it has the // Otherwise, retain the previous covspan only if it has the
@ -199,26 +186,55 @@ fn refine_sorted_spans(sorted_spans: Vec<SpanFromMir>) -> Vec<RefinedCovspan> {
} }
// Drain the rest of the pending list into the refined list. // Drain the rest of the pending list into the refined list.
for prev in pending { refined.extend(pending);
refined.push(RefinedCovspan { span: prev.span, bcb: prev.bcb });
}
// Do one last merge pass, to simplify the output. // Do one last merge pass, to simplify the output.
debug!(?refined, "before merge"); debug!(?refined, "before merge");
refined.dedup_by(|b, a| { refined.dedup_by(|b, a| a.merge_if_eligible(b));
if a.is_mergeable(b) {
debug!(?a, ?b, "merging list-adjacent refined spans");
a.merge_from(b);
true
} else {
false
}
});
debug!(?refined, "after merge"); debug!(?refined, "after merge");
refined refined
} }
#[derive(Clone, Debug)]
struct Covspan {
span: Span,
bcb: BasicCoverageBlock,
}
impl Covspan {
/// Splits this covspan into 0-2 parts:
/// - The part that is strictly before the hole span, if any.
/// - The part that is strictly after the hole span, if any.
fn split_around_hole_span(&self, hole_span: Span) -> (Option<Self>, Option<Self>) {
let before = try {
let span = self.span.trim_end(hole_span)?;
Self { span, ..*self }
};
let after = try {
let span = self.span.trim_start(hole_span)?;
Self { span, ..*self }
};
(before, after)
}
/// If `self` and `other` can be merged (i.e. they have the same BCB),
/// mutates `self.span` to also include `other.span` and returns true.
///
/// Note that compatible covspans can be merged even if their underlying
/// spans are not overlapping/adjacent; any space between them will also be
/// part of the merged covspan.
fn merge_if_eligible(&mut self, other: &Self) -> bool {
if self.bcb != other.bcb {
return false;
}
self.span = self.span.to(other.span);
true
}
}
/// Compares two spans in (lo ascending, hi descending) order. /// Compares two spans in (lo ascending, hi descending) order.
fn compare_spans(a: Span, b: Span) -> std::cmp::Ordering { fn compare_spans(a: Span, b: Span) -> std::cmp::Ordering {
// First sort by span start. // First sort by span start.

View file

@ -9,6 +9,7 @@ use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
use crate::coverage::graph::{ use crate::coverage::graph::{
BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB, BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
}; };
use crate::coverage::spans::Covspan;
use crate::coverage::ExtractedHirInfo; use crate::coverage::ExtractedHirInfo;
pub(crate) struct ExtractedCovspans { pub(crate) struct ExtractedCovspans {
@ -306,19 +307,8 @@ impl SpanFromMir {
Self { span, visible_macro, bcb } Self { span, visible_macro, bcb }
} }
/// Splits this span into 0-2 parts: pub(crate) fn into_covspan(self) -> Covspan {
/// - The part that is strictly before the hole span, if any. let Self { span, visible_macro: _, bcb } = self;
/// - The part that is strictly after the hole span, if any. Covspan { span, bcb }
pub(crate) fn split_around_hole_span(&self, hole_span: Span) -> (Option<Self>, Option<Self>) {
let before = try {
let span = self.span.trim_end(hole_span)?;
Self { span, ..*self }
};
let after = try {
let span = self.span.trim_start(hole_span)?;
Self { span, ..*self }
};
(before, after)
} }
} }