Skip to content

Commit 36cb289

Browse files
committed
Used functions with only unreachable coverage is now possible
Fixes: rust-lang#85081 An `assert!()` in `FunctionCoverage` failed, because PR rust-lang#84797 replaces unreachable `Counter`s with `Unreachable` code regions to be added to the function's coverage map. To fix it, and still generate valid LLVM coverage IR, convert one unreachable to a `Counter`.
1 parent 467253f commit 36cb289

File tree

3 files changed

+47
-28
lines changed

3 files changed

+47
-28
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
6161

6262
// Encode coverage mappings and generate function records
6363
let mut function_data = Vec::new();
64-
for (instance, function_coverage) in function_coverage_map {
64+
for (instance, mut function_coverage) in function_coverage_map {
6565
debug!("Generate function coverage for {}, {:?}", cx.codegen_unit.name(), instance);
6666
let mangled_function_name = tcx.symbol_name(instance).to_string();
6767
let source_hash = function_coverage.source_hash();

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -244,15 +244,9 @@ fn add_unused_function_coverage(
244244
) {
245245
let tcx = cx.tcx;
246246

247-
let mut function_coverage = FunctionCoverage::unused(tcx, instance);
248-
for (index, &code_region) in tcx.covered_code_regions(def_id).iter().enumerate() {
249-
if index == 0 {
250-
// Insert at least one real counter so the LLVM CoverageMappingReader will find expected
251-
// definitions.
252-
function_coverage.add_counter(UNUSED_FUNCTION_COUNTER_ID, code_region.clone());
253-
} else {
254-
function_coverage.add_unreachable_region(code_region.clone());
255-
}
247+
let mut function_coverage = FunctionCoverage::unused(instance);
248+
for &code_region in tcx.covered_code_regions(def_id).iter() {
249+
function_coverage.add_unreachable_region(code_region.clone());
256250
}
257251

258252
if let Some(coverage_context) = cx.coverage_context() {

compiler/rustc_codegen_ssa/src/coverageinfo/map.rs

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub struct Expression {
2828
/// only whitespace or comments). According to LLVM Code Coverage Mapping documentation, "A count
2929
/// for a gap area is only used as the line execution count if there are no other regions on a
3030
/// line."
31+
#[derive(Debug)]
3132
pub struct FunctionCoverage<'tcx> {
3233
instance: Instance<'tcx>,
3334
source_hash: u64,
@@ -40,30 +41,34 @@ pub struct FunctionCoverage<'tcx> {
4041
impl<'tcx> FunctionCoverage<'tcx> {
4142
/// Creates a new set of coverage data for a used (called) function.
4243
pub fn new(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self {
43-
Self::create(tcx, instance, true)
44-
}
45-
46-
/// Creates a new set of coverage data for an unused (never called) function.
47-
pub fn unused(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self {
48-
Self::create(tcx, instance, false)
49-
}
50-
51-
fn create(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, is_used: bool) -> Self {
5244
let coverageinfo = tcx.coverageinfo(instance.def_id());
5345
debug!(
54-
"FunctionCoverage::new(instance={:?}) has coverageinfo={:?}. is_used={}",
55-
instance, coverageinfo, is_used
46+
"FunctionCoverage::new(instance={:?}) has coverageinfo={:?}",
47+
instance, coverageinfo
5648
);
5749
Self {
5850
instance,
5951
source_hash: 0, // will be set with the first `add_counter()`
60-
is_used,
52+
is_used: true,
6153
counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize),
6254
expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize),
6355
unreachable_regions: Vec::new(),
6456
}
6557
}
6658

59+
/// Creates a new set of coverage data for an unused (never called) function.
60+
pub fn unused(instance: Instance<'tcx>) -> Self {
61+
debug!("FunctionCoverage::unused(instance={:?})", instance);
62+
Self {
63+
instance,
64+
source_hash: 0, // will be set with the first `add_counter()`
65+
is_used: false,
66+
counters: IndexVec::from_elem_n(None, CounterValueReference::START.as_usize()),
67+
expressions: IndexVec::new(),
68+
unreachable_regions: Vec::new(),
69+
}
70+
}
71+
6772
/// Returns true for a used (called) function, and false for an unused function.
6873
pub fn is_used(&self) -> bool {
6974
self.is_used
@@ -142,13 +147,33 @@ impl<'tcx> FunctionCoverage<'tcx> {
142147
/// associated `Regions` (from which the LLVM-specific `CoverageMapGenerator` will create
143148
/// `CounterMappingRegion`s.
144149
pub fn get_expressions_and_counter_regions<'a>(
145-
&'a self,
150+
&'a mut self,
146151
) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &'a CodeRegion)>) {
147-
assert!(
148-
self.source_hash != 0 || !self.is_used,
149-
"No counters provided the source_hash for used function: {:?}",
150-
self.instance
151-
);
152+
if self.source_hash == 0 {
153+
// Either this `FunctionCoverage` is _not_ used (created via
154+
// `unused()`, or the function had all statements removed, including
155+
// all `Counter`s.
156+
//
157+
// Dead blocks (removed during MIR transform, typically because
158+
// const evaluation can determine that the block will never be
159+
// called) are removed before codegen, but any coverage statements
160+
// are replaced by `Coverage::unreachable` statements at the top of
161+
// the MIR CFG.
162+
debug!("No counters provided the source_hash for used function:\n{:?}", self);
163+
assert_eq!(
164+
self.expressions.len(),
165+
0,
166+
"Expressions (from MIR) without counters: {:?}",
167+
self.instance
168+
);
169+
// If there are `Unreachable` code regions, but no `Counter`s, we
170+
// need to add at least one Counter, because LLVM seems to require
171+
// it.
172+
if let Some(unreachable_code_region) = self.unreachable_regions.pop() {
173+
// Replace any one of the `Unreachable`s with a counter.
174+
self.counters.push(Some(unreachable_code_region));
175+
}
176+
}
152177

153178
let counter_regions = self.counter_regions();
154179
let (counter_expressions, expression_regions) = self.expressions_with_regions();

0 commit comments

Comments
 (0)