1
Fork 0

Rollup merge of #117042 - Zalathar:file-table, r=cjgillot

coverage: Emit the filenames section before encoding per-function mappings

When embedding coverage information in LLVM IR (and ultimately in the resulting binary), there are two main things that each CGU needs to emit:

- A single `__llvm_covmap` record containing a coverage header, which mostly consists of a list of filenames used by the CGU's coverage mappings.
- Several `__llvm_covfun` records, one for each instrumented function, each of which contains the hash of the list of filenames in the header.

There is a kind of loose cyclic dependency between the two: we need the hash of the file table before we can emit the covfun records, but we need to traverse all of the instrumented functions in order to build the file table.

The existing code works by processing the individual functions first. It lazily adds filenames to the file table, and stores the mostly-complete function records in a temporary list. After this it hashes the file table, emits the header (containing the file table), and then uses the hash to emit all of the function records.

This PR reverses that order: first we traverse all of the functions (without trying to prepare their function records) to build a *complete* file table, and then emit it immediately. At this point we have the file table hash, so we can then proceed to build and emit all of the function records, without needing to store them in an intermediate list.

---

Along the way, this PR makes some necessary changes that are also worthwhile in their own right:
- We split `FunctionCoverage` into distinct collector/finished phases, which neatly avoids some borrow-checker hassles when extracting a function's final expression/mapping data.
- We avoid having to re-sort a function's mappings when preparing the list of filenames that it uses.
This commit is contained in:
Matthias Krüger 2023-10-23 08:12:39 +02:00 committed by GitHub
commit dde77f7a33
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 154 additions and 106 deletions

View file

@ -3601,6 +3601,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"cstr", "cstr",
"itertools",
"libc", "libc",
"measureme", "measureme",
"object", "object",

View file

@ -9,6 +9,7 @@ test = false
[dependencies] [dependencies]
bitflags = "1.0" bitflags = "1.0"
cstr = "0.2" cstr = "0.2"
itertools = "0.10.5"
libc = "0.2" libc = "0.2"
measureme = "10.0.0" measureme = "10.0.0"
object = { version = "0.32.0", default-features = false, features = [ object = { version = "0.32.0", default-features = false, features = [

View file

@ -1,16 +1,18 @@
use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind}; use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind};
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::fx::FxIndexSet;
use rustc_index::bit_set::BitSet; use rustc_index::bit_set::BitSet;
use rustc_middle::mir::coverage::{ use rustc_middle::mir::coverage::{
CodeRegion, CounterId, CovTerm, Expression, ExpressionId, FunctionCoverageInfo, Mapping, Op, CodeRegion, CounterId, CovTerm, Expression, ExpressionId, FunctionCoverageInfo, Mapping, Op,
}; };
use rustc_middle::ty::Instance; use rustc_middle::ty::Instance;
use rustc_span::Symbol;
/// Holds all of the coverage mapping data associated with a function instance, /// Holds all of the coverage mapping data associated with a function instance,
/// collected during traversal of `Coverage` statements in the function's MIR. /// collected during traversal of `Coverage` statements in the function's MIR.
#[derive(Debug)] #[derive(Debug)]
pub struct FunctionCoverage<'tcx> { pub struct FunctionCoverageCollector<'tcx> {
/// Coverage info that was attached to this function by the instrumentor. /// Coverage info that was attached to this function by the instrumentor.
function_coverage_info: &'tcx FunctionCoverageInfo, function_coverage_info: &'tcx FunctionCoverageInfo,
is_used: bool, is_used: bool,
@ -26,7 +28,7 @@ pub struct FunctionCoverage<'tcx> {
expressions_seen: BitSet<ExpressionId>, expressions_seen: BitSet<ExpressionId>,
} }
impl<'tcx> FunctionCoverage<'tcx> { impl<'tcx> FunctionCoverageCollector<'tcx> {
/// Creates a new set of coverage data for a used (called) function. /// Creates a new set of coverage data for a used (called) function.
pub fn new( pub fn new(
instance: Instance<'tcx>, instance: Instance<'tcx>,
@ -76,11 +78,6 @@ impl<'tcx> FunctionCoverage<'tcx> {
} }
} }
/// Returns true for a used (called) function, and false for an unused function.
pub fn is_used(&self) -> bool {
self.is_used
}
/// Marks a counter ID as having been seen in a counter-increment statement. /// Marks a counter ID as having been seen in a counter-increment statement.
#[instrument(level = "debug", skip(self))] #[instrument(level = "debug", skip(self))]
pub(crate) fn mark_counter_id_seen(&mut self, id: CounterId) { pub(crate) fn mark_counter_id_seen(&mut self, id: CounterId) {
@ -165,64 +162,71 @@ impl<'tcx> FunctionCoverage<'tcx> {
ZeroExpressions(zero_expressions) ZeroExpressions(zero_expressions)
} }
pub(crate) fn into_finished(self) -> FunctionCoverage<'tcx> {
let zero_expressions = self.identify_zero_expressions();
let FunctionCoverageCollector { function_coverage_info, is_used, counters_seen, .. } = self;
FunctionCoverage { function_coverage_info, is_used, counters_seen, zero_expressions }
}
}
pub(crate) struct FunctionCoverage<'tcx> {
function_coverage_info: &'tcx FunctionCoverageInfo,
is_used: bool,
counters_seen: BitSet<CounterId>,
zero_expressions: ZeroExpressions,
}
impl<'tcx> FunctionCoverage<'tcx> {
/// Returns true for a used (called) function, and false for an unused function.
pub(crate) fn is_used(&self) -> bool {
self.is_used
}
/// Return the source hash, generated from the HIR node structure, and used to indicate whether /// Return the source hash, generated from the HIR node structure, and used to indicate whether
/// or not the source code structure changed between different compilations. /// or not the source code structure changed between different compilations.
pub fn source_hash(&self) -> u64 { pub fn source_hash(&self) -> u64 {
if self.is_used { self.function_coverage_info.function_source_hash } else { 0 } if self.is_used { self.function_coverage_info.function_source_hash } else { 0 }
} }
/// Generate an array of CounterExpressions, and an iterator over all `Counter`s and their /// Returns an iterator over all filenames used by this function's mappings.
/// associated `Regions` (from which the LLVM-specific `CoverageMapGenerator` will create pub(crate) fn all_file_names(&self) -> impl Iterator<Item = Symbol> + Captures<'_> {
/// `CounterMappingRegion`s. self.function_coverage_info.mappings.iter().map(|mapping| mapping.code_region.file_name)
pub fn get_expressions_and_counter_regions(
&self,
) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &CodeRegion)>) {
let zero_expressions = self.identify_zero_expressions();
let counter_expressions = self.counter_expressions(&zero_expressions);
// Expression IDs are indices into `self.expressions`, and on the LLVM
// side they will be treated as indices into `counter_expressions`, so
// the two vectors should correspond 1:1.
assert_eq!(self.function_coverage_info.expressions.len(), counter_expressions.len());
let counter_regions = self.counter_regions(zero_expressions);
(counter_expressions, counter_regions)
} }
/// Convert this function's coverage expression data into a form that can be /// Convert this function's coverage expression data into a form that can be
/// passed through FFI to LLVM. /// passed through FFI to LLVM.
fn counter_expressions(&self, zero_expressions: &ZeroExpressions) -> Vec<CounterExpression> { pub(crate) fn counter_expressions(
&self,
) -> impl Iterator<Item = CounterExpression> + ExactSizeIterator + Captures<'_> {
// We know that LLVM will optimize out any unused expressions before // We know that LLVM will optimize out any unused expressions before
// producing the final coverage map, so there's no need to do the same // producing the final coverage map, so there's no need to do the same
// thing on the Rust side unless we're confident we can do much better. // thing on the Rust side unless we're confident we can do much better.
// (See `CounterExpressionsMinimizer` in `CoverageMappingWriter.cpp`.) // (See `CounterExpressionsMinimizer` in `CoverageMappingWriter.cpp`.)
let counter_from_operand = |operand: CovTerm| match operand { let counter_from_operand = |operand: CovTerm| match operand {
CovTerm::Expression(id) if zero_expressions.contains(id) => Counter::ZERO, CovTerm::Expression(id) if self.zero_expressions.contains(id) => Counter::ZERO,
_ => Counter::from_term(operand), _ => Counter::from_term(operand),
}; };
self.function_coverage_info self.function_coverage_info.expressions.iter().map(move |&Expression { lhs, op, rhs }| {
.expressions CounterExpression {
.iter()
.map(|&Expression { lhs, op, rhs }| CounterExpression {
lhs: counter_from_operand(lhs), lhs: counter_from_operand(lhs),
kind: match op { kind: match op {
Op::Add => ExprKind::Add, Op::Add => ExprKind::Add,
Op::Subtract => ExprKind::Subtract, Op::Subtract => ExprKind::Subtract,
}, },
rhs: counter_from_operand(rhs), rhs: counter_from_operand(rhs),
}) }
.collect::<Vec<_>>() })
} }
/// Converts this function's coverage mappings into an intermediate form /// Converts this function's coverage mappings into an intermediate form
/// that will be used by `mapgen` when preparing for FFI. /// that will be used by `mapgen` when preparing for FFI.
fn counter_regions( pub(crate) fn counter_regions(
&self, &self,
zero_expressions: ZeroExpressions, ) -> impl Iterator<Item = (Counter, &CodeRegion)> + ExactSizeIterator {
) -> impl Iterator<Item = (Counter, &CodeRegion)> {
// Historically, mappings were stored directly in counter/expression // Historically, mappings were stored directly in counter/expression
// statements in MIR, and MIR optimizations would sometimes remove them. // statements in MIR, and MIR optimizations would sometimes remove them.
// That's mostly no longer true, so now we detect cases where that would // That's mostly no longer true, so now we detect cases where that would
@ -230,7 +234,7 @@ impl<'tcx> FunctionCoverage<'tcx> {
let counter_for_term = move |term: CovTerm| { let counter_for_term = move |term: CovTerm| {
let force_to_zero = match term { let force_to_zero = match term {
CovTerm::Counter(id) => !self.counters_seen.contains(id), CovTerm::Counter(id) => !self.counters_seen.contains(id),
CovTerm::Expression(id) => zero_expressions.contains(id), CovTerm::Expression(id) => self.zero_expressions.contains(id),
CovTerm::Zero => false, CovTerm::Zero => false,
}; };
if force_to_zero { Counter::ZERO } else { Counter::from_term(term) } if force_to_zero { Counter::ZERO } else { Counter::from_term(term) }

View file

@ -1,11 +1,12 @@
use crate::common::CodegenCx; use crate::common::CodegenCx;
use crate::coverageinfo; use crate::coverageinfo;
use crate::coverageinfo::ffi::CounterMappingRegion; use crate::coverageinfo::ffi::CounterMappingRegion;
use crate::coverageinfo::map_data::FunctionCoverage; use crate::coverageinfo::map_data::{FunctionCoverage, FunctionCoverageCollector};
use crate::llvm; use crate::llvm;
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;
@ -57,11 +58,32 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
return; return;
} }
let mut global_file_table = GlobalFileTable::new(tcx); let function_coverage_entries = function_coverage_map
.into_iter()
.map(|(instance, function_coverage)| (instance, function_coverage.into_finished()))
.collect::<Vec<_>>();
let all_file_names =
function_coverage_entries.iter().flat_map(|(_, fn_cov)| fn_cov.all_file_names());
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);
let filenames_size = filenames_buffer.len();
let filenames_val = cx.const_bytes(&filenames_buffer);
let filenames_ref = coverageinfo::hash_bytes(&filenames_buffer);
// Generate the coverage map header, which contains the filenames used by
// this CGU's coverage mappings, and store it in a well-known global.
let cov_data_val = generate_coverage_map(cx, version, filenames_size, filenames_val);
coverageinfo::save_cov_data_to_mod(cx, cov_data_val);
let mut unused_function_names = Vec::new();
let covfun_section_name = coverageinfo::covfun_section_name(cx);
// Encode coverage mappings and generate function records // Encode coverage mappings and generate function records
let mut function_data = Vec::new(); for (instance, function_coverage) in function_coverage_entries {
for (instance, function_coverage) in function_coverage_map {
debug!("Generate function coverage for {}, {:?}", cx.codegen_unit.name(), instance); debug!("Generate function coverage for {}, {:?}", cx.codegen_unit.name(), instance);
let mangled_function_name = tcx.symbol_name(instance).name; let mangled_function_name = tcx.symbol_name(instance).name;
@ -69,7 +91,7 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
let is_used = function_coverage.is_used(); let is_used = function_coverage.is_used();
let coverage_mapping_buffer = let coverage_mapping_buffer =
encode_mappings_for_function(&mut global_file_table, &function_coverage); encode_mappings_for_function(&global_file_table, &function_coverage);
if coverage_mapping_buffer.is_empty() { if coverage_mapping_buffer.is_empty() {
if function_coverage.is_used() { if function_coverage.is_used() {
@ -83,23 +105,6 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
} }
} }
function_data.push((mangled_function_name, source_hash, is_used, coverage_mapping_buffer));
}
// Encode all filenames referenced by counters/expressions in this module
let filenames_buffer = global_file_table.into_filenames_buffer();
let filenames_size = filenames_buffer.len();
let filenames_val = cx.const_bytes(&filenames_buffer);
let filenames_ref = coverageinfo::hash_bytes(&filenames_buffer);
// Generate the LLVM IR representation of the coverage map and store it in a well-known global
let cov_data_val = generate_coverage_map(cx, version, filenames_size, filenames_val);
let mut unused_function_names = Vec::new();
let covfun_section_name = coverageinfo::covfun_section_name(cx);
for (mangled_function_name, source_hash, is_used, coverage_mapping_buffer) in function_data {
if !is_used { if !is_used {
unused_function_names.push(mangled_function_name); unused_function_names.push(mangled_function_name);
} }
@ -133,92 +138,125 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
llvm::set_linkage(array, llvm::Linkage::InternalLinkage); llvm::set_linkage(array, llvm::Linkage::InternalLinkage);
llvm::set_initializer(array, initializer); llvm::set_initializer(array, initializer);
} }
// Save the coverage data value to LLVM IR
coverageinfo::save_cov_data_to_mod(cx, cov_data_val);
} }
/// Maps "global" (per-CGU) file ID numbers to their underlying filenames.
struct GlobalFileTable { struct GlobalFileTable {
global_file_table: FxIndexSet<Symbol>, /// 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: FxIndexSet<Symbol>,
} }
impl GlobalFileTable { impl GlobalFileTable {
fn new(tcx: TyCtxt<'_>) -> Self { fn new(all_file_names: impl IntoIterator<Item = Symbol>) -> Self {
let mut global_file_table = FxIndexSet::default(); // 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 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_name(&self, file_name: Symbol) -> u32 {
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.
(raw_id + 1) as u32
}
fn make_filenames_buffer(&self, tcx: TyCtxt<'_>) -> Vec<u8> {
// LLVM Coverage Mapping Format version 6 (zero-based encoded as 5) // LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
// requires setting the first filename to the compilation directory. // requires setting the first filename to the compilation directory.
// Since rustc generates coverage maps with relative paths, the // Since rustc generates coverage maps with relative paths, the
// compilation directory can be combined with the relative paths // compilation directory can be combined with the relative paths
// to get absolute paths, if needed. // to get absolute paths, if needed.
use rustc_session::RemapFileNameExt; use rustc_session::RemapFileNameExt;
let working_dir = let working_dir: &str = &tcx.sess.opts.working_dir.for_codegen(&tcx.sess).to_string_lossy();
Symbol::intern(&tcx.sess.opts.working_dir.for_codegen(&tcx.sess).to_string_lossy());
global_file_table.insert(working_dir);
Self { global_file_table }
}
fn global_file_id_for_file_name(&mut self, file_name: Symbol) -> u32 {
let (global_file_id, _) = self.global_file_table.insert_full(file_name);
global_file_id as u32
}
fn into_filenames_buffer(self) -> Vec<u8> {
// This method takes `self` so that the caller can't accidentally
// modify the original file table after encoding it into a buffer.
llvm::build_byte_buffer(|buffer| { llvm::build_byte_buffer(|buffer| {
coverageinfo::write_filenames_section_to_buffer( coverageinfo::write_filenames_section_to_buffer(
self.global_file_table.iter().map(Symbol::as_str), // Insert the working dir at index 0, before the other filenames.
std::iter::once(working_dir).chain(self.raw_file_table.iter().map(Symbol::as_str)),
buffer, buffer,
); );
}) })
} }
} }
rustc_index::newtype_index! {
// Tell the newtype macro to not generate `Encode`/`Decode` impls.
#[custom_encodable]
struct LocalFileId {}
}
/// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU)
/// file IDs.
#[derive(Default)]
struct VirtualFileMapping {
local_to_global: IndexVec<LocalFileId, u32>,
global_to_local: FxIndexMap<u32, LocalFileId>,
}
impl VirtualFileMapping {
fn local_id_for_global(&mut self, global_file_id: u32) -> LocalFileId {
*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> {
self.local_to_global.raw
}
}
/// Using the expressions and counter regions collected for a single function, /// Using the expressions and counter regions collected for a single function,
/// generate the variable-sized payload of its corresponding `__llvm_covfun` /// generate the variable-sized payload of its corresponding `__llvm_covfun`
/// entry. The payload is returned as a vector of bytes. /// entry. The payload is returned as a vector of bytes.
/// ///
/// Newly-encountered filenames will be added to the global file table. /// Newly-encountered filenames will be added to the global file table.
fn encode_mappings_for_function( fn encode_mappings_for_function(
global_file_table: &mut GlobalFileTable, global_file_table: &GlobalFileTable,
function_coverage: &FunctionCoverage<'_>, function_coverage: &FunctionCoverage<'_>,
) -> Vec<u8> { ) -> Vec<u8> {
let (expressions, counter_regions) = function_coverage.get_expressions_and_counter_regions(); let counter_regions = function_coverage.counter_regions();
let mut counter_regions = counter_regions.collect::<Vec<_>>();
if counter_regions.is_empty() { if counter_regions.is_empty() {
return Vec::new(); return Vec::new();
} }
let mut virtual_file_mapping = IndexVec::<u32, u32>::new(); let expressions = function_coverage.counter_expressions().collect::<Vec<_>>();
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 (or allocate) 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: u32 = virtual_file_mapping.push(global_file_id); let local_file_id = virtual_file_mapping.local_id_for_global(global_file_id);
debug!(" file id: local {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:?}");
mapping_regions.push(CounterMappingRegion::code_region( mapping_regions.push(CounterMappingRegion::code_region(
counter, counter,
local_file_id, local_file_id.as_u32(),
start_line, start_line,
start_col, start_col,
end_line, end_line,
@ -230,7 +268,7 @@ fn encode_mappings_for_function(
// Encode the function's coverage mappings into a buffer. // Encode the function's coverage mappings into a buffer.
llvm::build_byte_buffer(|buffer| { llvm::build_byte_buffer(|buffer| {
coverageinfo::write_mapping_to_buffer( coverageinfo::write_mapping_to_buffer(
virtual_file_mapping.raw, virtual_file_mapping.into_vec(),
expressions, expressions,
mapping_regions, mapping_regions,
buffer, buffer,
@ -419,7 +457,7 @@ fn add_unused_function_coverage<'tcx>(
) { ) {
// An unused function's mappings will automatically be rewritten to map to // An unused function's mappings will automatically be rewritten to map to
// zero, because none of its counters/expressions are marked as seen. // zero, because none of its counters/expressions are marked as seen.
let function_coverage = FunctionCoverage::unused(instance, function_coverage_info); let function_coverage = FunctionCoverageCollector::unused(instance, function_coverage_info);
if let Some(coverage_context) = cx.coverage_context() { if let Some(coverage_context) = cx.coverage_context() {
coverage_context.function_coverage_map.borrow_mut().insert(instance, function_coverage); coverage_context.function_coverage_map.borrow_mut().insert(instance, function_coverage);

View file

@ -3,7 +3,7 @@ use crate::llvm;
use crate::builder::Builder; use crate::builder::Builder;
use crate::common::CodegenCx; use crate::common::CodegenCx;
use crate::coverageinfo::ffi::{CounterExpression, CounterMappingRegion}; use crate::coverageinfo::ffi::{CounterExpression, CounterMappingRegion};
use crate::coverageinfo::map_data::FunctionCoverage; use crate::coverageinfo::map_data::FunctionCoverageCollector;
use libc::c_uint; use libc::c_uint;
use rustc_codegen_ssa::traits::{ use rustc_codegen_ssa::traits::{
@ -29,7 +29,8 @@ const VAR_ALIGN_BYTES: usize = 8;
/// A context object for maintaining all state needed by the coverageinfo module. /// A context object for maintaining all state needed by the coverageinfo module.
pub struct CrateCoverageContext<'ll, 'tcx> { pub struct CrateCoverageContext<'ll, 'tcx> {
/// Coverage data for each instrumented function identified by DefId. /// Coverage data for each instrumented function identified by DefId.
pub(crate) function_coverage_map: RefCell<FxHashMap<Instance<'tcx>, FunctionCoverage<'tcx>>>, pub(crate) function_coverage_map:
RefCell<FxHashMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>>>,
pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>, pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
} }
@ -41,7 +42,9 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
} }
} }
pub fn take_function_coverage_map(&self) -> FxHashMap<Instance<'tcx>, FunctionCoverage<'tcx>> { pub fn take_function_coverage_map(
&self,
) -> FxHashMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>> {
self.function_coverage_map.replace(FxHashMap::default()) self.function_coverage_map.replace(FxHashMap::default())
} }
} }
@ -93,7 +96,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
let mut coverage_map = coverage_context.function_coverage_map.borrow_mut(); let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
let func_coverage = coverage_map let func_coverage = coverage_map
.entry(instance) .entry(instance)
.or_insert_with(|| FunctionCoverage::new(instance, function_coverage_info)); .or_insert_with(|| FunctionCoverageCollector::new(instance, function_coverage_info));
let Coverage { kind } = coverage; let Coverage { kind } = coverage;
match *kind { match *kind {

View file

@ -8,12 +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(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)]

View file

@ -3,12 +3,12 @@
WINDOWS: $__llvm_profile_runtime_user = comdat any WINDOWS: $__llvm_profile_runtime_user = comdat any
CHECK: @__covrec_{{[A-F0-9]+}}u = linkonce_odr hidden constant
CHECK-SAME: section "[[INSTR_PROF_COVFUN]]"[[COMDAT_IF_SUPPORTED]], align 8
CHECK: @__llvm_coverage_mapping = private constant CHECK: @__llvm_coverage_mapping = private constant
CHECK-SAME: section "[[INSTR_PROF_COVMAP]]", align 8 CHECK-SAME: section "[[INSTR_PROF_COVMAP]]", align 8
CHECK: @__covrec_{{[A-F0-9]+}}u = linkonce_odr hidden constant
CHECK-SAME: section "[[INSTR_PROF_COVFUN]]"[[COMDAT_IF_SUPPORTED]], align 8
WINDOWS: @__llvm_profile_runtime = external{{.*}}global i32 WINDOWS: @__llvm_profile_runtime = external{{.*}}global i32
CHECK: @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called = {{private|internal}} global CHECK: @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called = {{private|internal}} global