Skip to content

Commit e7e6782

Browse files
committed
coverage: Move unused-function synthesis into its own submodule
1 parent 725afd2 commit e7e6782

File tree

3 files changed

+188
-180
lines changed

3 files changed

+188
-180
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

Lines changed: 2 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@ use crate::common::CodegenCx;
22
use crate::coverageinfo;
33
use crate::coverageinfo::ffi::CounterMappingRegion;
44
use crate::coverageinfo::map_data::FunctionCoverage;
5+
use crate::coverageinfo::unused;
56
use crate::llvm;
67

78
use rustc_codegen_ssa::traits::ConstMethods;
89
use rustc_data_structures::fx::FxIndexSet;
9-
use rustc_hir::def::DefKind;
10-
use rustc_hir::def_id::DefId;
1110
use rustc_index::IndexVec;
1211
use rustc_middle::bug;
13-
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
1412
use rustc_middle::mir::coverage::CodeRegion;
1513
use rustc_middle::ty::TyCtxt;
1614
use rustc_span::Symbol;
@@ -43,7 +41,7 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
4341
// MIR `Coverage` code regions to the `function_coverage_map`, before calling
4442
// `ctx.take_function_coverage_map()`.
4543
if cx.codegen_unit.is_code_coverage_dead_code_cgu() {
46-
add_unused_functions(cx);
44+
unused::add_unused_functions(cx);
4745
}
4846

4947
let function_coverage_map = match cx.coverage_context() {
@@ -276,68 +274,3 @@ fn save_function_record(
276274
is_used,
277275
);
278276
}
279-
280-
/// When finalizing the coverage map, `FunctionCoverage` only has the `CodeRegion`s and counters for
281-
/// the functions that went through codegen; such as public functions and "used" functions
282-
/// (functions referenced by other "used" or public items). Any other functions considered unused,
283-
/// or "Unreachable", were still parsed and processed through the MIR stage, but were not
284-
/// codegenned. (Note that `-Clink-dead-code` can force some unused code to be codegenned, but
285-
/// that flag is known to cause other errors, when combined with `-C instrument-coverage`; and
286-
/// `-Clink-dead-code` will not generate code for unused generic functions.)
287-
///
288-
/// We can find the unused functions (including generic functions) by the set difference of all MIR
289-
/// `DefId`s (`tcx` query `mir_keys`) minus the codegenned `DefId`s (`tcx` query
290-
/// `codegened_and_inlined_items`).
291-
///
292-
/// These unused functions are then codegen'd in one of the CGUs which is marked as the
293-
/// "code coverage dead code cgu" during the partitioning process. This prevents us from generating
294-
/// code regions for the same function more than once which can lead to linker errors regarding
295-
/// duplicate symbols.
296-
fn add_unused_functions(cx: &CodegenCx<'_, '_>) {
297-
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
298-
299-
let tcx = cx.tcx;
300-
301-
let ignore_unused_generics = tcx.sess.instrument_coverage_except_unused_generics();
302-
303-
let eligible_def_ids: Vec<DefId> = tcx
304-
.mir_keys(())
305-
.iter()
306-
.filter_map(|local_def_id| {
307-
let def_id = local_def_id.to_def_id();
308-
let kind = tcx.def_kind(def_id);
309-
// `mir_keys` will give us `DefId`s for all kinds of things, not
310-
// just "functions", like consts, statics, etc. Filter those out.
311-
// If `ignore_unused_generics` was specified, filter out any
312-
// generic functions from consideration as well.
313-
if !matches!(
314-
kind,
315-
DefKind::Fn | DefKind::AssocFn | DefKind::Closure | DefKind::Generator
316-
) {
317-
return None;
318-
}
319-
if ignore_unused_generics && tcx.generics_of(def_id).requires_monomorphization(tcx) {
320-
return None;
321-
}
322-
Some(local_def_id.to_def_id())
323-
})
324-
.collect();
325-
326-
let codegenned_def_ids = tcx.codegened_and_inlined_items(());
327-
328-
for non_codegenned_def_id in
329-
eligible_def_ids.into_iter().filter(|id| !codegenned_def_ids.contains(id))
330-
{
331-
let codegen_fn_attrs = tcx.codegen_fn_attrs(non_codegenned_def_id);
332-
333-
// If a function is marked `#[no_coverage]`, then skip generating a
334-
// dead code stub for it.
335-
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_COVERAGE) {
336-
debug!("skipping unused fn marked #[no_coverage]: {:?}", non_codegenned_def_id);
337-
continue;
338-
}
339-
340-
debug!("generating unused fn: {:?}", non_codegenned_def_id);
341-
cx.define_unused_fn(non_codegenned_def_id);
342-
}
343-
}

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

Lines changed: 3 additions & 111 deletions
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,25 +11,19 @@ use rustc_codegen_ssa::traits::{
1211
StaticMethods,
1312
};
1413
use rustc_data_structures::fx::FxHashMap;
15-
use rustc_hir as hir;
16-
use rustc_hir::def_id::DefId;
1714
use rustc_llvm::RustString;
1815
use rustc_middle::bug;
19-
use rustc_middle::mir::coverage::{CounterId, CoverageKind};
16+
use rustc_middle::mir::coverage::CoverageKind;
2017
use rustc_middle::mir::Coverage;
21-
use rustc_middle::ty;
22-
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt};
23-
use rustc_middle::ty::GenericArgs;
18+
use rustc_middle::ty::layout::HasTyCtxt;
2419
use rustc_middle::ty::Instance;
25-
use rustc_middle::ty::Ty;
2620

2721
use std::cell::RefCell;
2822

2923
pub(crate) mod ffi;
3024
pub(crate) mod map_data;
3125
pub mod mapgen;
32-
33-
const UNUSED_FUNCTION_COUNTER_ID: CounterId = CounterId::START;
26+
mod unused;
3427

3528
const VAR_ALIGN_BYTES: usize = 8;
3629

@@ -76,28 +69,6 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
7669
bug!("Could not get the `coverage_context`");
7770
}
7871
}
79-
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 coverage `CodeRegion`s from the MIR and add
92-
/// them. The first `CodeRegion` is used to add a single counter, with the
93-
/// same counter ID used in the injected `instrprof.increment` intrinsic
94-
/// call. Since the function is never called, all other `CodeRegion`s can be
95-
/// added as `unreachable_region`s.
96-
fn define_unused_fn(&self, def_id: DefId) {
97-
let instance = declare_unused_fn(self, def_id);
98-
codegen_unused_fn_and_counter(self, instance);
99-
add_unused_function_coverage(self, instance, def_id);
100-
}
10172
}
10273

10374
impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
@@ -165,85 +136,6 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
165136
}
166137
}
167138

168-
fn declare_unused_fn<'tcx>(cx: &CodegenCx<'_, 'tcx>, def_id: DefId) -> Instance<'tcx> {
169-
let tcx = cx.tcx;
170-
171-
let instance = Instance::new(
172-
def_id,
173-
GenericArgs::for_item(tcx, def_id, |param, _| {
174-
if let ty::GenericParamDefKind::Lifetime = param.kind {
175-
tcx.lifetimes.re_erased.into()
176-
} else {
177-
tcx.mk_param_from_def(param)
178-
}
179-
}),
180-
);
181-
182-
let llfn = cx.declare_fn(
183-
tcx.symbol_name(instance).name,
184-
cx.fn_abi_of_fn_ptr(
185-
ty::Binder::dummy(tcx.mk_fn_sig(
186-
[Ty::new_unit(tcx)],
187-
Ty::new_unit(tcx),
188-
false,
189-
hir::Unsafety::Unsafe,
190-
Abi::Rust,
191-
)),
192-
ty::List::empty(),
193-
),
194-
None,
195-
);
196-
197-
llvm::set_linkage(llfn, llvm::Linkage::PrivateLinkage);
198-
llvm::set_visibility(llfn, llvm::Visibility::Default);
199-
200-
assert!(cx.instances.borrow_mut().insert(instance, llfn).is_none());
201-
202-
instance
203-
}
204-
205-
fn codegen_unused_fn_and_counter<'tcx>(cx: &CodegenCx<'_, 'tcx>, instance: Instance<'tcx>) {
206-
let llfn = cx.get_fn(instance);
207-
let llbb = Builder::append_block(cx, llfn, "unused_function");
208-
let mut bx = Builder::build(cx, llbb);
209-
let fn_name = bx.get_pgo_func_name_var(instance);
210-
let hash = bx.const_u64(0);
211-
let num_counters = bx.const_u32(1);
212-
let index = bx.const_u32(u32::from(UNUSED_FUNCTION_COUNTER_ID));
213-
debug!(
214-
"codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?},
215-
index={:?}) for unused function: {:?}",
216-
fn_name, hash, num_counters, index, instance
217-
);
218-
bx.instrprof_increment(fn_name, hash, num_counters, index);
219-
bx.ret_void();
220-
}
221-
222-
fn add_unused_function_coverage<'tcx>(
223-
cx: &CodegenCx<'_, 'tcx>,
224-
instance: Instance<'tcx>,
225-
def_id: DefId,
226-
) {
227-
let tcx = cx.tcx;
228-
229-
let mut function_coverage = FunctionCoverage::unused(tcx, instance);
230-
for (index, &code_region) in tcx.covered_code_regions(def_id).iter().enumerate() {
231-
if index == 0 {
232-
// Insert at least one real counter so the LLVM CoverageMappingReader will find expected
233-
// definitions.
234-
function_coverage.add_counter(UNUSED_FUNCTION_COUNTER_ID, code_region.clone());
235-
} else {
236-
function_coverage.add_unreachable_region(code_region.clone());
237-
}
238-
}
239-
240-
if let Some(coverage_context) = cx.coverage_context() {
241-
coverage_context.function_coverage_map.borrow_mut().insert(instance, function_coverage);
242-
} else {
243-
bug!("Could not get the `coverage_context`");
244-
}
245-
}
246-
247139
/// Calls llvm::createPGOFuncNameVar() with the given function instance's
248140
/// mangled function name. The LLVM API returns an llvm::GlobalVariable
249141
/// containing the function name, with the specific variable name and linkage

0 commit comments

Comments
 (0)