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.
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
index a9be833..8c9dfcf 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
@@ -46,21 +46,17 @@ pub(crate) fn finalize(cx: &mut CodegenCx<'_, '_>) {
debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name());
// FIXME(#132395): Can this be none even when coverage is enabled?
- let instances_used = match cx.coverage_cx {
- Some(ref cx) => cx.instances_used.borrow(),
- None => return,
- };
+ let Some(ref coverage_cx) = cx.coverage_cx else { return };
- let mut covfun_records = instances_used
- .iter()
- .copied()
+ let mut covfun_records = coverage_cx
+ .instances_used()
+ .into_iter()
// Sort by symbol name, so that the global file table is built in an
// order that doesn't depend on the stable-hash-based order in which
// instances were visited during codegen.
.sorted_by_cached_key(|&instance| tcx.symbol_name(instance).name)
.filter_map(|instance| prepare_covfun_record(tcx, instance, true))
.collect::<Vec<_>>();
- drop(instances_used);
// In a single designated CGU, also prepare covfun records for functions
// in this crate that were instrumented for coverage, but are unused.
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
index eefbd7c..35c3223 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
@@ -5,7 +5,7 @@
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 @@
/// 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 @@ fn try_get_mcdc_condition_bitmap(
.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 @@ fn add_coverage(&mut self, instance: Instance<'tcx>, kind: &CoverageKind) {
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"