Skip to content

Commit c462895

Browse files
authored
Rollup merge of rust-lang#144530 - Zalathar:instances-used, r=lqd
coverage: Infer `instances_used` from `pgo_func_name_var_map` In obscure circumstances involving macro-expanded spans, we would sometimes emit a covfun record for a function with no physical coverage counters, and therefore no corresponding entry in the “PGO names” section of the binary. The absence of that name entry causes `llvm-cov` to fail with the cryptic error message: ```text 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 is an attempt to eliminate the cause of rust-lang#141577 when re-landing changes like rust-lang#144298 in the future. I haven't been able to reproduce the underlying issue in an in-tree test, because the only known repro involves a non-trivial derive proc-macro that relies on `syn` and `proc-macro2`. But I have manually verified in a separate branch that this change would have prevented the reoccurrence of rust-lang#141577 (comment).
2 parents ec86930 + 89b6b0b commit c462895

File tree

2 files changed

+28
-22
lines changed

2 files changed

+28
-22
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,17 @@ pub(crate) fn finalize(cx: &mut CodegenCx<'_, '_>) {
4646
debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name());
4747

4848
// FIXME(#132395): Can this be none even when coverage is enabled?
49-
let instances_used = match cx.coverage_cx {
50-
Some(ref cx) => cx.instances_used.borrow(),
51-
None => return,
52-
};
49+
let Some(ref coverage_cx) = cx.coverage_cx else { return };
5350

54-
let mut covfun_records = instances_used
55-
.iter()
56-
.copied()
51+
let mut covfun_records = coverage_cx
52+
.instances_used()
53+
.into_iter()
5754
// Sort by symbol name, so that the global file table is built in an
5855
// order that doesn't depend on the stable-hash-based order in which
5956
// instances were visited during codegen.
6057
.sorted_by_cached_key(|&instance| tcx.symbol_name(instance).name)
6158
.filter_map(|instance| prepare_covfun_record(tcx, instance, true))
6259
.collect::<Vec<_>>();
63-
drop(instances_used);
6460

6561
// In a single designated CGU, also prepare covfun records for functions
6662
// in this crate that were instrumented for coverage, but are unused.

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_abi::Size;
55
use rustc_codegen_ssa::traits::{
66
BuilderMethods, ConstCodegenMethods, CoverageInfoBuilderMethods, MiscCodegenMethods,
77
};
8-
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
8+
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
99
use rustc_middle::mir::coverage::CoverageKind;
1010
use rustc_middle::ty::Instance;
1111
use tracing::{debug, instrument};
@@ -20,9 +20,14 @@ mod mapgen;
2020

2121
/// Extra per-CGU context/state needed for coverage instrumentation.
2222
pub(crate) struct CguCoverageContext<'ll, 'tcx> {
23-
/// Coverage data for each instrumented function identified by DefId.
24-
pub(crate) instances_used: RefCell<FxIndexSet<Instance<'tcx>>>,
25-
pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
23+
/// Associates function instances with an LLVM global that holds the
24+
/// function's symbol name, as needed by LLVM coverage intrinsics.
25+
///
26+
/// Instances in this map are also considered "used" for the purposes of
27+
/// emitting covfun records. Every covfun record holds a hash of its
28+
/// symbol name, and `llvm-cov` will exit fatally if it can't resolve that
29+
/// hash back to an entry in the binary's `__llvm_prf_names` linker section.
30+
pub(crate) pgo_func_name_var_map: RefCell<FxIndexMap<Instance<'tcx>, &'ll llvm::Value>>,
2631
pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, Vec<&'ll llvm::Value>>>,
2732

2833
covfun_section_name: OnceCell<CString>,
@@ -31,7 +36,6 @@ pub(crate) struct CguCoverageContext<'ll, 'tcx> {
3136
impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> {
3237
pub(crate) fn new() -> Self {
3338
Self {
34-
instances_used: RefCell::<FxIndexSet<_>>::default(),
3539
pgo_func_name_var_map: Default::default(),
3640
mcdc_condition_bitmap_map: Default::default(),
3741
covfun_section_name: Default::default(),
@@ -53,6 +57,14 @@ impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> {
5357
.and_then(|bitmap_map| bitmap_map.get(decision_depth as usize))
5458
.copied() // Dereference Option<&&Value> to Option<&Value>
5559
}
60+
61+
/// Returns the list of instances considered "used" in this CGU, as
62+
/// inferred from the keys of `pgo_func_name_var_map`.
63+
pub(crate) fn instances_used(&self) -> Vec<Instance<'tcx>> {
64+
// Collecting into a Vec is way easier than trying to juggle RefCell
65+
// projections, and this should only run once per CGU anyway.
66+
self.pgo_func_name_var_map.borrow().keys().copied().collect::<Vec<_>>()
67+
}
5668
}
5769

5870
impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
@@ -78,7 +90,10 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
7890
/// string, to hold the function name passed to LLVM intrinsic
7991
/// `instrprof.increment()`. The `Value` is only created once per instance.
8092
/// Multiple invocations with the same instance return the same `Value`.
81-
fn get_pgo_func_name_var(&self, instance: Instance<'tcx>) -> &'ll llvm::Value {
93+
///
94+
/// This has the side-effect of causing coverage codegen to consider this
95+
/// function "used", making it eligible to emit an associated covfun record.
96+
fn ensure_pgo_func_name_var(&self, instance: Instance<'tcx>) -> &'ll llvm::Value {
8297
debug!("getting pgo_func_name_var for instance={:?}", instance);
8398
let mut pgo_func_name_var_map = self.coverage_cx().pgo_func_name_var_map.borrow_mut();
8499
pgo_func_name_var_map.entry(instance).or_insert_with(|| {
@@ -102,7 +117,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
102117
return;
103118
}
104119

105-
let fn_name = self.get_pgo_func_name_var(instance);
120+
let fn_name = self.ensure_pgo_func_name_var(instance);
106121
let hash = self.const_u64(function_coverage_info.function_source_hash);
107122
let bitmap_bits = self.const_u32(function_coverage_info.mcdc_bitmap_bits as u32);
108123
self.mcdc_parameters(fn_name, hash, bitmap_bits);
@@ -151,19 +166,14 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
151166
return;
152167
};
153168

154-
// Mark the instance as used in this CGU, for coverage purposes.
155-
// This includes functions that were not partitioned into this CGU,
156-
// but were MIR-inlined into one of this CGU's functions.
157-
coverage_cx.instances_used.borrow_mut().insert(instance);
158-
159169
match *kind {
160170
CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!(
161171
"marker statement {kind:?} should have been removed by CleanupPostBorrowck"
162172
),
163173
CoverageKind::VirtualCounter { bcb }
164174
if let Some(&id) = ids_info.phys_counter_for_node.get(&bcb) =>
165175
{
166-
let fn_name = bx.get_pgo_func_name_var(instance);
176+
let fn_name = bx.ensure_pgo_func_name_var(instance);
167177
let hash = bx.const_u64(function_coverage_info.function_source_hash);
168178
let num_counters = bx.const_u32(ids_info.num_counters);
169179
let index = bx.const_u32(id.as_u32());
@@ -193,7 +203,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
193203
"bitmap index of the decision out of range"
194204
);
195205

196-
let fn_name = bx.get_pgo_func_name_var(instance);
206+
let fn_name = bx.ensure_pgo_func_name_var(instance);
197207
let hash = bx.const_u64(function_coverage_info.function_source_hash);
198208
let bitmap_index = bx.const_u32(bitmap_idx);
199209
bx.mcdc_tvbitmap_update(fn_name, hash, bitmap_index, cond_bitmap);

0 commit comments

Comments
 (0)