coverage: Infer instances_used from pgo_func_name_var_map

In obscure circumstances, we would sometimes emit a covfun record for a
function with no physical coverage counters, causing `llvm-cov` to fail with
the cryptic error message:

    malformed instrumentation profile data: function name is empty

We can eliminate this mismatch by removing `instances_used` entirely, and
instead inferring its contents from the keys of `pgo_func_name_var_map`.

This makes it impossible for a "used" function to lack a PGO name entry.
This commit is contained in:
Zalathar
2025-07-27 21:19:07 +10:00
parent 052114f0c5
commit 24e2b4832b
2 changed files with 21 additions and 18 deletions

View File

@@ -5,7 +5,7 @@ use rustc_abi::Size;
use rustc_codegen_ssa::traits::{
BuilderMethods, ConstCodegenMethods, CoverageInfoBuilderMethods, MiscCodegenMethods,
};
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_middle::mir::coverage::CoverageKind;
use rustc_middle::ty::Instance;
use tracing::{debug, instrument};
@@ -20,9 +20,14 @@ mod mapgen;
/// Extra per-CGU context/state needed for coverage instrumentation.
pub(crate) struct CguCoverageContext<'ll, 'tcx> {
/// Coverage data for each instrumented function identified by DefId.
pub(crate) instances_used: RefCell<FxIndexSet<Instance<'tcx>>>,
pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
/// Associates function instances with an LLVM global that holds the
/// function's symbol name, as needed by LLVM coverage intrinsics.
///
/// Instances in this map are also considered "used" for the purposes of
/// emitting covfun records. Every covfun record holds a hash of its
/// symbol name, and `llvm-cov` will exit fatally if it can't resolve that
/// hash back to an entry in the binary's `__llvm_prf_names` linker section.
pub(crate) pgo_func_name_var_map: RefCell<FxIndexMap<Instance<'tcx>, &'ll llvm::Value>>,
pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, Vec<&'ll llvm::Value>>>,
covfun_section_name: OnceCell<CString>,
@@ -31,7 +36,6 @@ pub(crate) struct CguCoverageContext<'ll, 'tcx> {
impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> {
pub(crate) fn new() -> Self {
Self {
instances_used: RefCell::<FxIndexSet<_>>::default(),
pgo_func_name_var_map: Default::default(),
mcdc_condition_bitmap_map: Default::default(),
covfun_section_name: Default::default(),
@@ -53,6 +57,14 @@ impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> {
.and_then(|bitmap_map| bitmap_map.get(decision_depth as usize))
.copied() // Dereference Option<&&Value> to Option<&Value>
}
/// Returns the list of instances considered "used" in this CGU, as
/// inferred from the keys of `pgo_func_name_var_map`.
pub(crate) fn instances_used(&self) -> Vec<Instance<'tcx>> {
// Collecting into a Vec is way easier than trying to juggle RefCell
// projections, and this should only run once per CGU anyway.
self.pgo_func_name_var_map.borrow().keys().copied().collect::<Vec<_>>()
}
}
impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
@@ -151,11 +163,6 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
return;
};
// Mark the instance as used in this CGU, for coverage purposes.
// This includes functions that were not partitioned into this CGU,
// but were MIR-inlined into one of this CGU's functions.
coverage_cx.instances_used.borrow_mut().insert(instance);
match *kind {
CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!(
"marker statement {kind:?} should have been removed by CleanupPostBorrowck"