Skip to content

Commit e964ea5

Browse files
committed
coverage: Emit mappings for unused functions without generating stubs
1 parent 249624b commit e964ea5

File tree

2 files changed

+37
-74
lines changed

2 files changed

+37
-74
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

+30-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::coverageinfo::ffi::CounterMappingRegion;
44
use crate::coverageinfo::map_data::FunctionCoverage;
55
use crate::llvm;
66

7-
use rustc_codegen_ssa::traits::ConstMethods;
7+
use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods};
88
use rustc_data_structures::fx::FxIndexSet;
99
use rustc_hir::def::DefKind;
1010
use rustc_hir::def_id::DefId;
@@ -94,8 +94,14 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
9494
// Generate the LLVM IR representation of the coverage map and store it in a well-known global
9595
let cov_data_val = generate_coverage_map(cx, version, filenames_size, filenames_val);
9696

97+
let mut unused_function_names = Vec::new();
98+
9799
let covfun_section_name = coverageinfo::covfun_section_name(cx);
98100
for (mangled_function_name, source_hash, is_used, coverage_mapping_buffer) in function_data {
101+
if !is_used {
102+
unused_function_names.push(mangled_function_name);
103+
}
104+
99105
save_function_record(
100106
cx,
101107
&covfun_section_name,
@@ -107,6 +113,25 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
107113
);
108114
}
109115

116+
// For unused functions, we need to take their mangled names and store them
117+
// in a specially-named global array. LLVM's `InstrProfiling` pass will
118+
// detect this global and include those names in its `__llvm_prf_names`
119+
// section. (See `llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp`.)
120+
if !unused_function_names.is_empty() {
121+
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
122+
123+
let name_globals = unused_function_names
124+
.into_iter()
125+
.map(|mangled_function_name| cx.const_str(mangled_function_name).0)
126+
.collect::<Vec<_>>();
127+
let initializer = cx.const_array(cx.type_ptr(), &name_globals);
128+
129+
let array = llvm::add_global(cx.llmod, cx.val_ty(initializer), "__llvm_coverage_names");
130+
llvm::set_global_constant(array, true);
131+
llvm::set_linkage(array, llvm::Linkage::InternalLinkage);
132+
llvm::set_initializer(array, initializer);
133+
}
134+
110135
// Save the coverage data value to LLVM IR
111136
coverageinfo::save_cov_data_to_mod(cx, cov_data_val);
112137
}
@@ -290,10 +315,10 @@ fn save_function_record(
290315
/// `DefId`s (`tcx` query `mir_keys`) minus the codegenned `DefId`s (`tcx` query
291316
/// `codegened_and_inlined_items`).
292317
///
293-
/// These unused functions are then codegen'd in one of the CGUs which is marked as the
294-
/// "code coverage dead code cgu" during the partitioning process. This prevents us from generating
295-
/// code regions for the same function more than once which can lead to linker errors regarding
296-
/// duplicate symbols.
318+
/// These unused functions don't need to be codegenned, but we do need to add them to the function
319+
/// coverage map (in a single designated CGU) so that we still emit coverage mappings for them.
320+
/// We also end up adding their symbol names to a special global array that LLVM will include in
321+
/// its embedded coverage data.
297322
fn add_unused_functions(cx: &CodegenCx<'_, '_>) {
298323
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
299324

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

+7-69
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use crate::llvm;
22

3-
use crate::abi::Abi;
43
use crate::builder::Builder;
54
use crate::common::CodegenCx;
65
use crate::coverageinfo::ffi::{CounterExpression, CounterMappingRegion};
@@ -12,26 +11,20 @@ use rustc_codegen_ssa::traits::{
1211
StaticMethods,
1312
};
1413
use rustc_data_structures::fx::FxHashMap;
15-
use rustc_hir as hir;
1614
use rustc_hir::def_id::DefId;
1715
use rustc_llvm::RustString;
1816
use rustc_middle::bug;
19-
use rustc_middle::mir::coverage::{CounterId, CoverageKind, FunctionCoverageInfo};
17+
use rustc_middle::mir::coverage::{CoverageKind, FunctionCoverageInfo};
2018
use rustc_middle::mir::Coverage;
21-
use rustc_middle::ty;
22-
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt};
23-
use rustc_middle::ty::GenericArgs;
24-
use rustc_middle::ty::Instance;
25-
use rustc_middle::ty::Ty;
19+
use rustc_middle::ty::layout::HasTyCtxt;
20+
use rustc_middle::ty::{self, GenericArgs, Instance, TyCtxt};
2621

2722
use std::cell::RefCell;
2823

2924
pub(crate) mod ffi;
3025
pub(crate) mod map_data;
3126
pub mod mapgen;
3227

33-
const UNUSED_FUNCTION_COUNTER_ID: CounterId = CounterId::START;
34-
3528
const VAR_ALIGN_BYTES: usize = 8;
3629

3730
/// A context object for maintaining all state needed by the coverageinfo module.
@@ -77,22 +70,8 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
7770
}
7871
}
7972

80-
/// Functions with MIR-based coverage are normally codegenned _only_ if
81-
/// called. LLVM coverage tools typically expect every function to be
82-
/// defined (even if unused), with at least one call to LLVM intrinsic
83-
/// `instrprof.increment`.
84-
///
85-
/// Codegen a small function that will never be called, with one counter
86-
/// that will never be incremented.
87-
///
88-
/// For used/called functions, the coverageinfo was already added to the
89-
/// `function_coverage_map` (keyed by function `Instance`) during codegen.
90-
/// But in this case, since the unused function was _not_ previously
91-
/// codegenned, collect the function coverage info from MIR and add an
92-
/// "unused" entry to the function coverage map.
9373
fn define_unused_fn(&self, def_id: DefId, function_coverage_info: &'tcx FunctionCoverageInfo) {
94-
let instance = declare_unused_fn(self, def_id);
95-
codegen_unused_fn_and_counter(self, instance);
74+
let instance = declare_unused_fn(self.tcx, def_id);
9675
add_unused_function_coverage(self, instance, function_coverage_info);
9776
}
9877
}
@@ -159,10 +138,8 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
159138
}
160139
}
161140

162-
fn declare_unused_fn<'tcx>(cx: &CodegenCx<'_, 'tcx>, def_id: DefId) -> Instance<'tcx> {
163-
let tcx = cx.tcx;
164-
165-
let instance = Instance::new(
141+
fn declare_unused_fn<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Instance<'tcx> {
142+
Instance::new(
166143
def_id,
167144
GenericArgs::for_item(tcx, def_id, |param, _| {
168145
if let ty::GenericParamDefKind::Lifetime = param.kind {
@@ -171,46 +148,7 @@ fn declare_unused_fn<'tcx>(cx: &CodegenCx<'_, 'tcx>, def_id: DefId) -> Instance<
171148
tcx.mk_param_from_def(param)
172149
}
173150
}),
174-
);
175-
176-
let llfn = cx.declare_fn(
177-
tcx.symbol_name(instance).name,
178-
cx.fn_abi_of_fn_ptr(
179-
ty::Binder::dummy(tcx.mk_fn_sig(
180-
[Ty::new_unit(tcx)],
181-
Ty::new_unit(tcx),
182-
false,
183-
hir::Unsafety::Unsafe,
184-
Abi::Rust,
185-
)),
186-
ty::List::empty(),
187-
),
188-
None,
189-
);
190-
191-
llvm::set_linkage(llfn, llvm::Linkage::PrivateLinkage);
192-
llvm::set_visibility(llfn, llvm::Visibility::Default);
193-
194-
assert!(cx.instances.borrow_mut().insert(instance, llfn).is_none());
195-
196-
instance
197-
}
198-
199-
fn codegen_unused_fn_and_counter<'tcx>(cx: &CodegenCx<'_, 'tcx>, instance: Instance<'tcx>) {
200-
let llfn = cx.get_fn(instance);
201-
let llbb = Builder::append_block(cx, llfn, "unused_function");
202-
let mut bx = Builder::build(cx, llbb);
203-
let fn_name = bx.get_pgo_func_name_var(instance);
204-
let hash = bx.const_u64(0);
205-
let num_counters = bx.const_u32(1);
206-
let index = bx.const_u32(u32::from(UNUSED_FUNCTION_COUNTER_ID));
207-
debug!(
208-
"codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?},
209-
index={:?}) for unused function: {:?}",
210-
fn_name, hash, num_counters, index, instance
211-
);
212-
bx.instrprof_increment(fn_name, hash, num_counters, index);
213-
bx.ret_void();
151+
)
214152
}
215153

216154
fn add_unused_function_coverage<'tcx>(

0 commit comments

Comments
 (0)