Skip to content

Commit ebcb1b9

Browse files
committed
coverage: Move unused-function synthesis into its own submodule
1 parent abfc6c4 commit ebcb1b9

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
@@ -1,15 +1,13 @@
11
use crate::common::CodegenCx;
22
use crate::coverageinfo;
33
use crate::coverageinfo::ffi::{Counter, CounterExpression, CounterMappingRegion};
4+
use crate::coverageinfo::unused;
45
use crate::llvm;
56

67
use rustc_codegen_ssa::traits::ConstMethods;
78
use rustc_data_structures::fx::FxIndexSet;
8-
use rustc_hir::def::DefKind;
9-
use rustc_hir::def_id::DefId;
109
use rustc_llvm::RustString;
1110
use rustc_middle::bug;
12-
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
1311
use rustc_middle::mir::coverage::CodeRegion;
1412
use rustc_middle::ty::TyCtxt;
1513
use rustc_span::Symbol;
@@ -42,7 +40,7 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
4240
// MIR `Coverage` code regions to the `function_coverage_map`, before calling
4341
// `ctx.take_function_coverage_map()`.
4442
if cx.codegen_unit.is_code_coverage_dead_code_cgu() {
45-
add_unused_functions(cx);
43+
unused::add_unused_functions(cx);
4644
}
4745

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

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)