coverage: Deal with unused functions and their names in one place
This commit is contained in:
parent
75135aaf19
commit
b3c40cf374
3 changed files with 51 additions and 39 deletions
|
@ -73,12 +73,11 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
|
|||
// In a single designated CGU, also prepare covfun records for functions
|
||||
// in this crate that were instrumented for coverage, but are unused.
|
||||
if cx.codegen_unit.is_code_coverage_dead_code_cgu() {
|
||||
let mut unused_instances = unused::gather_unused_function_instances(cx);
|
||||
// Sort the unused instances by symbol name, for the same reason as the used ones.
|
||||
unused_instances.sort_by_cached_key(|&instance| tcx.symbol_name(instance).name);
|
||||
covfun_records.extend(unused_instances.into_iter().filter_map(|instance| {
|
||||
prepare_covfun_record(tcx, &mut global_file_table, instance, false)
|
||||
}));
|
||||
unused::prepare_covfun_records_for_unused_functions(
|
||||
cx,
|
||||
&mut global_file_table,
|
||||
&mut covfun_records,
|
||||
);
|
||||
}
|
||||
|
||||
// If there are no covfun records for this CGU, don't generate a covmap record.
|
||||
|
@ -97,33 +96,10 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
|
|||
// contain multiple covmap records from different compilation units.
|
||||
let filenames_hash = llvm_cov::hash_bytes(&filenames_buffer);
|
||||
|
||||
let mut unused_function_names = vec![];
|
||||
|
||||
for covfun in &covfun_records {
|
||||
unused_function_names.extend(covfun.mangled_function_name_if_unused());
|
||||
|
||||
covfun::generate_covfun_record(cx, filenames_hash, covfun)
|
||||
}
|
||||
|
||||
// For unused functions, we need to take their mangled names and store them
|
||||
// in a specially-named global array. LLVM's `InstrProfiling` pass will
|
||||
// detect this global and include those names in its `__llvm_prf_names`
|
||||
// section. (See `llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp`.)
|
||||
if !unused_function_names.is_empty() {
|
||||
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
|
||||
|
||||
let name_globals = unused_function_names
|
||||
.into_iter()
|
||||
.map(|mangled_function_name| cx.const_str(mangled_function_name).0)
|
||||
.collect::<Vec<_>>();
|
||||
let initializer = cx.const_array(cx.type_ptr(), &name_globals);
|
||||
|
||||
let array = llvm::add_global(cx.llmod, cx.val_ty(initializer), c"__llvm_coverage_names");
|
||||
llvm::set_global_constant(array, true);
|
||||
llvm::set_linkage(array, llvm::Linkage::InternalLinkage);
|
||||
llvm::set_initializer(array, initializer);
|
||||
}
|
||||
|
||||
// Generate the coverage map header, which contains the filenames used by
|
||||
// this CGU's coverage mappings, and store it in a well-known global.
|
||||
// (This is skipped if we returned early due to having no covfun records.)
|
||||
|
|
|
@ -37,14 +37,6 @@ pub(crate) struct CovfunRecord<'tcx> {
|
|||
regions: ffi::Regions,
|
||||
}
|
||||
|
||||
impl<'tcx> CovfunRecord<'tcx> {
|
||||
/// FIXME(Zalathar): Make this the responsibility of the code that determines
|
||||
/// which functions are unused.
|
||||
pub(crate) fn mangled_function_name_if_unused(&self) -> Option<&'tcx str> {
|
||||
(!self.is_used).then_some(self.mangled_function_name)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn prepare_covfun_record<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
global_file_table: &mut GlobalFileTable,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, ConstCodegenMethods};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_middle::mir;
|
||||
|
@ -6,6 +7,9 @@ use rustc_middle::ty::{self, TyCtxt};
|
|||
use rustc_span::def_id::DefIdSet;
|
||||
|
||||
use crate::common::CodegenCx;
|
||||
use crate::coverageinfo::mapgen::GlobalFileTable;
|
||||
use crate::coverageinfo::mapgen::covfun::{CovfunRecord, prepare_covfun_record};
|
||||
use crate::llvm;
|
||||
|
||||
/// Each CGU will normally only emit coverage metadata for the functions that it actually generates.
|
||||
/// But since we don't want unused functions to disappear from coverage reports, we also scan for
|
||||
|
@ -15,9 +19,48 @@ use crate::common::CodegenCx;
|
|||
/// coverage map (in a single designated CGU) so that we still emit coverage mappings for them.
|
||||
/// We also end up adding their symbol names to a special global array that LLVM will include in
|
||||
/// its embedded coverage data.
|
||||
pub(crate) fn gather_unused_function_instances<'tcx>(
|
||||
pub(crate) fn prepare_covfun_records_for_unused_functions<'tcx>(
|
||||
cx: &CodegenCx<'_, 'tcx>,
|
||||
) -> Vec<ty::Instance<'tcx>> {
|
||||
global_file_table: &mut GlobalFileTable,
|
||||
covfun_records: &mut Vec<CovfunRecord<'tcx>>,
|
||||
) {
|
||||
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
|
||||
|
||||
let mut unused_instances = gather_unused_function_instances(cx);
|
||||
// Sort the unused instances by symbol name, so that their order isn't hash-sensitive.
|
||||
unused_instances.sort_by_key(|instance| instance.symbol_name);
|
||||
|
||||
// Try to create a covfun record for each unused function.
|
||||
let mut name_globals = Vec::with_capacity(unused_instances.len());
|
||||
covfun_records.extend(unused_instances.into_iter().filter_map(|unused| try {
|
||||
let record = prepare_covfun_record(cx.tcx, global_file_table, unused.instance, false)?;
|
||||
// If successful, also store its symbol name in a global constant.
|
||||
name_globals.push(cx.const_str(unused.symbol_name.name).0);
|
||||
record
|
||||
}));
|
||||
|
||||
// Store the names of unused functions in a specially-named global array.
|
||||
// LLVM's `InstrProfilling` pass will detect this array, and include the
|
||||
// referenced names in its `__llvm_prf_names` section.
|
||||
// (See `llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp`.)
|
||||
if !name_globals.is_empty() {
|
||||
let initializer = cx.const_array(cx.type_ptr(), &name_globals);
|
||||
|
||||
let array = llvm::add_global(cx.llmod, cx.val_ty(initializer), c"__llvm_coverage_names");
|
||||
llvm::set_global_constant(array, true);
|
||||
llvm::set_linkage(array, llvm::Linkage::InternalLinkage);
|
||||
llvm::set_initializer(array, initializer);
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds a dummy function instance along with its symbol name, to avoid having
|
||||
/// to repeatedly query for the name.
|
||||
struct UnusedInstance<'tcx> {
|
||||
instance: ty::Instance<'tcx>,
|
||||
symbol_name: ty::SymbolName<'tcx>,
|
||||
}
|
||||
|
||||
fn gather_unused_function_instances<'tcx>(cx: &CodegenCx<'_, 'tcx>) -> Vec<UnusedInstance<'tcx>> {
|
||||
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
|
||||
|
||||
let tcx = cx.tcx;
|
||||
|
@ -45,6 +88,7 @@ pub(crate) fn gather_unused_function_instances<'tcx>(
|
|||
.copied()
|
||||
.filter(|&def_id| is_unused_fn(def_id))
|
||||
.map(|def_id| make_dummy_instance(tcx, def_id))
|
||||
.map(|instance| UnusedInstance { instance, symbol_name: tcx.symbol_name(instance) })
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue