coverage: Encode function mappings without re-sorting them

The main change here is that `VirtualFileMapping` now uses an internal hashmap
to de-duplicate incoming global file IDs. That removes the need for
`encode_mappings_for_function` to re-sort its mappings by filename in order to
de-duplicate them.

(We still de-duplicate runs of identical filenames to save work, but this is
not load-bearing for correctness, so a sort is not necessary.)
This commit is contained in:
Zalathar 2023-09-28 22:36:40 +10:00
parent 88159cafa7
commit de4cfbca2e
2 changed files with 15 additions and 13 deletions

View File

@ -6,7 +6,7 @@
use itertools::Itertools as _; use itertools::Itertools as _;
use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods}; use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods};
use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_index::IndexVec; use rustc_index::IndexVec;
@ -205,11 +205,15 @@ struct LocalFileId {}
#[derive(Default)] #[derive(Default)]
struct VirtualFileMapping { struct VirtualFileMapping {
local_to_global: IndexVec<LocalFileId, u32>, local_to_global: IndexVec<LocalFileId, u32>,
global_to_local: FxIndexMap<u32, LocalFileId>,
} }
impl VirtualFileMapping { impl VirtualFileMapping {
fn push_global_id(&mut self, global_file_id: u32) -> LocalFileId { fn local_id_for_global(&mut self, global_file_id: u32) -> LocalFileId {
self.local_to_global.push(global_file_id) *self
.global_to_local
.entry(global_file_id)
.or_insert_with(|| self.local_to_global.push(global_file_id))
} }
fn into_vec(self) -> Vec<u32> { fn into_vec(self) -> Vec<u32> {
@ -226,7 +230,7 @@ fn encode_mappings_for_function(
global_file_table: &GlobalFileTable, global_file_table: &GlobalFileTable,
function_coverage: &FunctionCoverage<'_>, function_coverage: &FunctionCoverage<'_>,
) -> Vec<u8> { ) -> Vec<u8> {
let mut counter_regions = function_coverage.counter_regions().collect::<Vec<_>>(); let counter_regions = function_coverage.counter_regions();
if counter_regions.is_empty() { if counter_regions.is_empty() {
return Vec::new(); return Vec::new();
} }
@ -236,25 +240,23 @@ fn encode_mappings_for_function(
let mut virtual_file_mapping = VirtualFileMapping::default(); let mut virtual_file_mapping = VirtualFileMapping::default();
let mut mapping_regions = Vec::with_capacity(counter_regions.len()); let mut mapping_regions = Vec::with_capacity(counter_regions.len());
// Sort and group the list of (counter, region) mapping pairs by filename. // Group mappings into runs with the same filename, preserving the order
// (Preserve any further ordering imposed by `FunctionCoverage`.) // yielded by `FunctionCoverage`.
// Prepare file IDs for each filename, and prepare the mapping data so that // Prepare file IDs for each filename, and prepare the mapping data so that
// we can pass it through FFI to LLVM. // we can pass it through FFI to LLVM.
counter_regions.sort_by_key(|(_counter, region)| region.file_name); for (file_name, counter_regions_for_file) in
for counter_regions_for_file in &counter_regions.group_by(|(_counter, region)| region.file_name)
counter_regions.group_by(|(_, a), (_, b)| a.file_name == b.file_name)
{ {
// Look up the global file ID for this filename. // Look up the global file ID for this filename.
let file_name = counter_regions_for_file[0].1.file_name;
let global_file_id = global_file_table.global_file_id_for_file_name(file_name); let global_file_id = global_file_table.global_file_id_for_file_name(file_name);
// Associate that global file ID with a local file ID for this function. // Associate that global file ID with a local file ID for this function.
let local_file_id = virtual_file_mapping.push_global_id(global_file_id); let local_file_id = virtual_file_mapping.local_id_for_global(global_file_id);
debug!(" file id: {local_file_id:?} => global {global_file_id} = '{file_name:?}'"); debug!(" file id: {local_file_id:?} => global {global_file_id} = '{file_name:?}'");
// For each counter/region pair in this function+file, convert it to a // For each counter/region pair in this function+file, convert it to a
// form suitable for FFI. // form suitable for FFI.
for &(counter, region) in counter_regions_for_file { for (counter, region) in counter_regions_for_file {
let CodeRegion { file_name: _, start_line, start_col, end_line, end_col } = *region; let CodeRegion { file_name: _, start_line, start_col, end_line, end_col } = *region;
debug!("Adding counter {counter:?} to map for {region:?}"); debug!("Adding counter {counter:?} to map for {region:?}");

View File

@ -8,13 +8,13 @@
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] #![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![cfg_attr(not(bootstrap), doc(rust_logo))] #![cfg_attr(not(bootstrap), doc(rust_logo))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(exact_size_is_empty)]
#![feature(extern_types)] #![feature(extern_types)]
#![feature(hash_raw_entry)] #![feature(hash_raw_entry)]
#![feature(iter_intersperse)] #![feature(iter_intersperse)]
#![feature(let_chains)] #![feature(let_chains)]
#![feature(min_specialization)] #![feature(min_specialization)]
#![feature(never_type)] #![feature(never_type)]
#![feature(slice_group_by)]
#![feature(impl_trait_in_assoc_type)] #![feature(impl_trait_in_assoc_type)]
#![recursion_limit = "256"] #![recursion_limit = "256"]
#![allow(rustc::potential_query_instability)] #![allow(rustc::potential_query_instability)]