Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert #133418 (Store coverage source regions as Span) due to regression #133606 #133608

Merged
merged 1 commit into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 26 additions & 10 deletions compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId};
use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId, SourceRegion};

use crate::coverageinfo::mapgen::LocalFileId;

/// Must match the layout of `LLVMRustCounterKind`.
#[derive(Copy, Clone, Debug)]
Expand Down Expand Up @@ -124,31 +126,45 @@ pub(crate) struct CoverageSpan {
/// Local index into the function's local-to-global file ID table.
/// The value at that index is itself an index into the coverage filename
/// table in the CGU's `__llvm_covmap` section.
pub(crate) file_id: u32,
file_id: u32,

/// 1-based starting line of the source code span.
pub(crate) start_line: u32,
start_line: u32,
/// 1-based starting column of the source code span.
pub(crate) start_col: u32,
start_col: u32,
/// 1-based ending line of the source code span.
pub(crate) end_line: u32,
end_line: u32,
/// 1-based ending column of the source code span. High bit must be unset.
pub(crate) end_col: u32,
end_col: u32,
}

impl CoverageSpan {
pub(crate) fn from_source_region(
local_file_id: LocalFileId,
code_region: &SourceRegion,
) -> Self {
let file_id = local_file_id.as_u32();
let &SourceRegion { start_line, start_col, end_line, end_col } = code_region;
// Internally, LLVM uses the high bit of `end_col` to distinguish between
// code regions and gap regions, so it can't be used by the column number.
assert!(end_col & (1u32 << 31) == 0, "high bit of `end_col` must be unset: {end_col:#X}");
Self { file_id, start_line, start_col, end_line, end_col }
}
}

/// Must match the layout of `LLVMRustCoverageCodeRegion`.
#[derive(Clone, Debug)]
#[repr(C)]
pub(crate) struct CodeRegion {
pub(crate) cov_span: CoverageSpan,
pub(crate) span: CoverageSpan,
pub(crate) counter: Counter,
}

/// Must match the layout of `LLVMRustCoverageBranchRegion`.
#[derive(Clone, Debug)]
#[repr(C)]
pub(crate) struct BranchRegion {
pub(crate) cov_span: CoverageSpan,
pub(crate) span: CoverageSpan,
pub(crate) true_counter: Counter,
pub(crate) false_counter: Counter,
}
Expand All @@ -157,7 +173,7 @@ pub(crate) struct BranchRegion {
#[derive(Clone, Debug)]
#[repr(C)]
pub(crate) struct MCDCBranchRegion {
pub(crate) cov_span: CoverageSpan,
pub(crate) span: CoverageSpan,
pub(crate) true_counter: Counter,
pub(crate) false_counter: Counter,
pub(crate) mcdc_branch_params: mcdc::BranchParameters,
Expand All @@ -167,6 +183,6 @@ pub(crate) struct MCDCBranchRegion {
#[derive(Clone, Debug)]
#[repr(C)]
pub(crate) struct MCDCDecisionRegion {
pub(crate) cov_span: CoverageSpan,
pub(crate) span: CoverageSpan,
pub(crate) mcdc_decision_params: mcdc::DecisionParameters,
}
14 changes: 7 additions & 7 deletions compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ use rustc_data_structures::fx::FxIndexSet;
use rustc_index::bit_set::BitSet;
use rustc_middle::mir::coverage::{
CounterId, CovTerm, Expression, ExpressionId, FunctionCoverageInfo, Mapping, MappingKind, Op,
SourceRegion,
};
use rustc_middle::ty::Instance;
use rustc_span::Span;
use tracing::{debug, instrument};

use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind};
Expand Down Expand Up @@ -220,16 +220,16 @@ impl<'tcx> FunctionCoverage<'tcx> {
})
}

/// Yields all this function's coverage mappings, after simplifying away
/// unused counters and counter expressions.
pub(crate) fn mapping_spans(
/// Converts this function's coverage mappings into an intermediate form
/// that will be used by `mapgen` when preparing for FFI.
pub(crate) fn counter_regions(
&self,
) -> impl Iterator<Item = (MappingKind, Span)> + ExactSizeIterator + Captures<'_> {
) -> impl Iterator<Item = (MappingKind, &SourceRegion)> + ExactSizeIterator {
self.function_coverage_info.mappings.iter().map(move |mapping| {
let &Mapping { ref kind, span } = mapping;
let Mapping { kind, source_region } = mapping;
let kind =
kind.map_terms(|term| if self.is_zero_term(term) { CovTerm::Zero } else { term });
(kind, span)
(kind, source_region)
})
}

Expand Down
129 changes: 63 additions & 66 deletions compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
mod spans;

use std::ffi::CString;
use std::sync::Arc;
use std::iter;

use itertools::Itertools as _;
use rustc_abi::Align;
use rustc_codegen_ssa::traits::{
BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods,
};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_index::IndexVec;
use rustc_middle::mir::coverage::MappingKind;
Expand All @@ -17,7 +15,7 @@ use rustc_middle::{bug, mir};
use rustc_session::RemapFileNameExt;
use rustc_session::config::RemapPathScopeComponents;
use rustc_span::def_id::DefIdSet;
use rustc_span::{SourceFile, StableSourceFileId};
use rustc_span::{Span, Symbol};
use rustc_target::spec::HasTargetSpec;
use tracing::debug;

Expand Down Expand Up @@ -74,11 +72,11 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
.map(|(instance, function_coverage)| (instance, function_coverage.into_finished()))
.collect::<Vec<_>>();

let all_files = function_coverage_entries
let all_file_names = function_coverage_entries
.iter()
.map(|(_, fn_cov)| fn_cov.function_coverage_info.body_span)
.map(|span| tcx.sess.source_map().lookup_source_file(span.lo()));
let global_file_table = GlobalFileTable::new(all_files);
.map(|span| span_file_name(tcx, span));
let global_file_table = GlobalFileTable::new(all_file_names);

// Encode all filenames referenced by coverage mappings in this CGU.
let filenames_buffer = global_file_table.make_filenames_buffer(tcx);
Expand All @@ -105,8 +103,15 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
encode_mappings_for_function(tcx, &global_file_table, &function_coverage);

if coverage_mapping_buffer.is_empty() {
debug!("function has no mappings to embed; skipping");
continue;
if function_coverage.is_used() {
bug!(
"A used function should have had coverage mapping data but did not: {}",
mangled_function_name
);
} else {
debug!("unused function had no coverage mapping data: {}", mangled_function_name);
continue;
}
}

if !is_used {
Expand Down Expand Up @@ -143,62 +148,54 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
}
}

/// Maps "global" (per-CGU) file ID numbers to their underlying source files.
/// Maps "global" (per-CGU) file ID numbers to their underlying filenames.
struct GlobalFileTable {
/// This "raw" table doesn't include the working dir, so a file's
/// This "raw" table doesn't include the working dir, so a filename's
/// global ID is its index in this set **plus one**.
raw_file_table: FxIndexMap<StableSourceFileId, Arc<SourceFile>>,
raw_file_table: FxIndexSet<Symbol>,
}

impl GlobalFileTable {
fn new(all_files: impl IntoIterator<Item = Arc<SourceFile>>) -> Self {
// Collect all of the files into a set. Files usually come in contiguous
// runs, so we can dedup adjacent ones to save work.
let mut raw_file_table = all_files
.into_iter()
.dedup_by(|a, b| a.stable_id == b.stable_id)
.map(|f| (f.stable_id, f))
.collect::<FxIndexMap<StableSourceFileId, Arc<SourceFile>>>();
fn new(all_file_names: impl IntoIterator<Item = Symbol>) -> Self {
// Collect all of the filenames into a set. Filenames usually come in
// contiguous runs, so we can dedup adjacent ones to save work.
let mut raw_file_table = all_file_names.into_iter().dedup().collect::<FxIndexSet<Symbol>>();

// Sort the file table by its underlying filenames.
raw_file_table.sort_unstable_by(|_, a, _, b| {
Ord::cmp(&a.name, &b.name).then_with(|| Ord::cmp(&a.stable_id, &b.stable_id))
});
// Sort the file table by its actual string values, not the arbitrary
// ordering of its symbols.
raw_file_table.sort_unstable_by(|a, b| a.as_str().cmp(b.as_str()));

Self { raw_file_table }
}

fn global_file_id_for_file(&self, file: &SourceFile) -> GlobalFileId {
let raw_id = self.raw_file_table.get_index_of(&file.stable_id).unwrap_or_else(|| {
bug!("file not found in prepared global file table: {:?}", file.name);
fn global_file_id_for_file_name(&self, file_name: Symbol) -> GlobalFileId {
let raw_id = self.raw_file_table.get_index_of(&file_name).unwrap_or_else(|| {
bug!("file name not found in prepared global file table: {file_name}");
});
// The raw file table doesn't include an entry for the working dir
// (which has ID 0), so add 1 to get the correct ID.
GlobalFileId::from_usize(raw_id + 1)
}

fn make_filenames_buffer(&self, tcx: TyCtxt<'_>) -> Vec<u8> {
let mut table = Vec::with_capacity(self.raw_file_table.len() + 1);

// LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
// requires setting the first filename to the compilation directory.
// Since rustc generates coverage maps with relative paths, the
// compilation directory can be combined with the relative paths
// to get absolute paths, if needed.
table.push(
tcx.sess
.opts
.working_dir
.for_scope(tcx.sess, RemapPathScopeComponents::MACRO)
.to_string_lossy(),
);

// Add the regular entries after the base directory.
table.extend(self.raw_file_table.values().map(|file| {
file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy()
}));

llvm_cov::write_filenames_to_buffer(table.iter().map(|f| f.as_ref()))
use rustc_session::RemapFileNameExt;
use rustc_session::config::RemapPathScopeComponents;
let working_dir: &str = &tcx
.sess
.opts
.working_dir
.for_scope(tcx.sess, RemapPathScopeComponents::MACRO)
.to_string_lossy();

// Insert the working dir at index 0, before the other filenames.
let filenames =
iter::once(working_dir).chain(self.raw_file_table.iter().map(Symbol::as_str));
llvm_cov::write_filenames_to_buffer(filenames)
}
}

Expand All @@ -211,7 +208,7 @@ rustc_index::newtype_index! {
/// An index into a function's list of global file IDs. That underlying list
/// of local-to-global mappings will be embedded in the function's record in
/// the `__llvm_covfun` linker section.
struct LocalFileId {}
pub(crate) struct LocalFileId {}
}

/// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU)
Expand All @@ -237,6 +234,13 @@ impl VirtualFileMapping {
}
}

fn span_file_name(tcx: TyCtxt<'_>, span: Span) -> Symbol {
let source_file = tcx.sess.source_map().lookup_source_file(span.lo());
let name =
source_file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy();
Symbol::intern(&name)
}

/// Using the expressions and counter regions collected for a single function,
/// generate the variable-sized payload of its corresponding `__llvm_covfun`
/// entry. The payload is returned as a vector of bytes.
Expand All @@ -247,13 +251,11 @@ fn encode_mappings_for_function(
global_file_table: &GlobalFileTable,
function_coverage: &FunctionCoverage<'_>,
) -> Vec<u8> {
let mapping_spans = function_coverage.mapping_spans();
if mapping_spans.is_empty() {
let counter_regions = function_coverage.counter_regions();
if counter_regions.is_empty() {
return Vec::new();
}

let fn_cov_info = function_coverage.function_coverage_info;

let expressions = function_coverage.counter_expressions().collect::<Vec<_>>();

let mut virtual_file_mapping = VirtualFileMapping::default();
Expand All @@ -263,47 +265,42 @@ fn encode_mappings_for_function(
let mut mcdc_decision_regions = vec![];

// Currently a function's mappings must all be in the same file as its body span.
let source_map = tcx.sess.source_map();
let source_file = source_map.lookup_source_file(fn_cov_info.body_span.lo());
let file_name = span_file_name(tcx, function_coverage.function_coverage_info.body_span);

// Look up the global file ID for that file.
let global_file_id = global_file_table.global_file_id_for_file(&source_file);
// Look up the global file ID for that filename.
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.
let local_file_id = virtual_file_mapping.local_id_for_global(global_file_id);
debug!(" file id: {local_file_id:?} => {global_file_id:?} = '{file_name:?}'");

let make_cov_span = |span| {
spans::make_coverage_span(local_file_id, source_map, fn_cov_info, &source_file, span)
};

// For each coverage mapping span 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.
for (mapping_kind, span) in mapping_spans {
debug!("Adding counter {mapping_kind:?} to map for {span:?}");
let Some(cov_span) = make_cov_span(span) else { continue };
for (mapping_kind, region) in counter_regions {
debug!("Adding counter {mapping_kind:?} to map for {region:?}");
let span = ffi::CoverageSpan::from_source_region(local_file_id, region);
match mapping_kind {
MappingKind::Code(term) => {
code_regions
.push(ffi::CodeRegion { cov_span, counter: ffi::Counter::from_term(term) });
code_regions.push(ffi::CodeRegion { span, counter: ffi::Counter::from_term(term) });
}
MappingKind::Branch { true_term, false_term } => {
branch_regions.push(ffi::BranchRegion {
cov_span,
span,
true_counter: ffi::Counter::from_term(true_term),
false_counter: ffi::Counter::from_term(false_term),
});
}
MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => {
mcdc_branch_regions.push(ffi::MCDCBranchRegion {
cov_span,
span,
true_counter: ffi::Counter::from_term(true_term),
false_counter: ffi::Counter::from_term(false_term),
mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params),
});
}
MappingKind::MCDCDecision(mcdc_decision_params) => {
mcdc_decision_regions.push(ffi::MCDCDecisionRegion {
cov_span,
span,
mcdc_decision_params: ffi::mcdc::DecisionParameters::from(mcdc_decision_params),
});
}
Expand Down
Loading
Loading