Skip to content

Commit 1c43f4c

Browse files
committed
Move coverage region collection to CodegenCx finalization
Moved from `query coverageinfo` (renamed from `query coverage_data`), as discussed in the PR at: rust-lang#73684 (comment)
1 parent a433777 commit 1c43f4c

File tree

19 files changed

+414
-154
lines changed

19 files changed

+414
-154
lines changed

src/libcore/intrinsics.rs

+33
Original file line numberDiff line numberDiff line change
@@ -1958,6 +1958,39 @@ extern "rust-intrinsic" {
19581958
#[lang = "count_code_region"]
19591959
pub fn count_code_region(index: u32, start_byte_pos: u32, end_byte_pos: u32);
19601960

1961+
/// Internal marker for code coverage expressions, injected into the MIR when the
1962+
/// "instrument-coverage" option is enabled. This intrinsic is not converted into a
1963+
/// backend intrinsic call, but its arguments are extracted during the production of a
1964+
/// "coverage map", which is injected into the generated code, as additional data.
1965+
/// This marker identifies a code region and two other counters or counter expressions
1966+
/// whose sum is the number of times the code region was executed.
1967+
#[cfg(not(bootstrap))]
1968+
pub fn coverage_counter_add(
1969+
index: u32,
1970+
left_index: u32,
1971+
right_index: u32,
1972+
start_byte_pos: u32,
1973+
end_byte_pos: u32,
1974+
);
1975+
1976+
/// This marker identifies a code region and two other counters or counter expressions
1977+
/// whose difference is the number of times the code region was executed.
1978+
/// (See `coverage_counter_add` for more information.)
1979+
#[cfg(not(bootstrap))]
1980+
pub fn coverage_counter_subtract(
1981+
index: u32,
1982+
left_index: u32,
1983+
right_index: u32,
1984+
start_byte_pos: u32,
1985+
end_byte_pos: u32,
1986+
);
1987+
1988+
/// This marker identifies a code region to be added to the "coverage map" to indicate source
1989+
/// code that can never be reached.
1990+
/// (See `coverage_counter_add` for more information.)
1991+
#[cfg(not(bootstrap))]
1992+
pub fn coverage_unreachable(start_byte_pos: u32, end_byte_pos: u32);
1993+
19611994
/// See documentation of `<*const T>::guaranteed_eq` for details.
19621995
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
19631996
#[cfg(not(bootstrap))]

src/librustc_codegen_llvm/context.rs

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::attributes;
22
use crate::callee::get_fn;
3+
use crate::coverageinfo;
34
use crate::debuginfo;
45
use crate::llvm;
56
use crate::llvm_util;
@@ -77,6 +78,7 @@ pub struct CodegenCx<'ll, 'tcx> {
7778
pub pointee_infos: RefCell<FxHashMap<(Ty<'tcx>, Size), Option<PointeeInfo>>>,
7879
pub isize_ty: &'ll Type,
7980

81+
pub coverage_cx: Option<coverageinfo::CrateCoverageContext<'tcx>>,
8082
pub dbg_cx: Option<debuginfo::CrateDebugContext<'ll, 'tcx>>,
8183

8284
eh_personality: Cell<Option<&'ll Value>>,
@@ -256,6 +258,13 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
256258

257259
let (llcx, llmod) = (&*llvm_module.llcx, llvm_module.llmod());
258260

261+
let coverage_cx = if tcx.sess.opts.debugging_opts.instrument_coverage {
262+
let covctx = coverageinfo::CrateCoverageContext::new();
263+
Some(covctx)
264+
} else {
265+
None
266+
};
267+
259268
let dbg_cx = if tcx.sess.opts.debuginfo != DebugInfo::None {
260269
let dctx = debuginfo::CrateDebugContext::new(llmod);
261270
debuginfo::metadata::compile_unit_metadata(tcx, &codegen_unit.name().as_str(), &dctx);
@@ -285,6 +294,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
285294
scalar_lltypes: Default::default(),
286295
pointee_infos: Default::default(),
287296
isize_ty,
297+
coverage_cx,
288298
dbg_cx,
289299
eh_personality: Cell::new(None),
290300
rust_try_fn: Cell::new(None),
@@ -296,6 +306,11 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
296306
crate fn statics_to_rauw(&self) -> &RefCell<Vec<(&'ll Value, &'ll Value)>> {
297307
&self.statics_to_rauw
298308
}
309+
310+
#[inline]
311+
pub fn coverage_context(&'a self) -> &'a coverageinfo::CrateCoverageContext<'tcx> {
312+
self.coverage_cx.as_ref().unwrap()
313+
}
299314
}
300315

301316
impl MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> {
@@ -749,8 +764,6 @@ impl CodegenCx<'b, 'tcx> {
749764
ifn!("llvm.lifetime.start.p0i8", fn(t_i64, i8p) -> void);
750765
ifn!("llvm.lifetime.end.p0i8", fn(t_i64, i8p) -> void);
751766

752-
ifn!("llvm.instrprof.increment", fn(i8p, t_i64, t_i32, t_i32) -> void);
753-
754767
ifn!("llvm.expect.i1", fn(i1, i1) -> i1);
755768
ifn!("llvm.eh.typeid.for", fn(i8p) -> t_i32);
756769
ifn!("llvm.localescape", fn(...) -> void);
@@ -765,6 +778,10 @@ impl CodegenCx<'b, 'tcx> {
765778
ifn!("llvm.va_end", fn(i8p) -> void);
766779
ifn!("llvm.va_copy", fn(i8p, i8p) -> void);
767780

781+
if self.sess().opts.debugging_opts.instrument_coverage {
782+
ifn!("llvm.instrprof.increment", fn(i8p, t_i64, t_i32, t_i32) -> void);
783+
}
784+
768785
if self.sess().opts.debuginfo != DebugInfo::None {
769786
ifn!("llvm.dbg.declare", fn(self.type_metadata(), self.type_metadata()) -> void);
770787
ifn!("llvm.dbg.value", fn(self.type_metadata(), t_i64, self.type_metadata()) -> void);
+96-44
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,61 @@
1+
use crate::builder::Builder;
12
use crate::common::CodegenCx;
23
use log::debug;
3-
use rustc_codegen_ssa::traits::CoverageInfoMethods;
4-
use rustc_hir::def_id::LOCAL_CRATE;
4+
use rustc_codegen_ssa::coverageinfo::map::*;
5+
use rustc_codegen_ssa::traits::{CoverageInfoBuilderMethods, CoverageInfoMethods};
6+
use rustc_data_structures::fx::FxHashMap;
7+
use rustc_middle::ty::Instance;
58

6-
// FIXME(richkadel): This might not be where I put the coverage map generation, but it's a
7-
// placeholder for now.
8-
//
9-
// I need to inject the coverage map into the LLVM IR. This is one possible place to do it.
10-
//
11-
// The code below is here, at least temporarily, to verify that the `CoverageRegion`s are
12-
// available, to produce the coverage map.
9+
use std::cell::RefCell;
10+
11+
/// A context object for maintaining all state needed by the coverageinfo module.
12+
pub struct CrateCoverageContext<'tcx> {
13+
// Coverage region data for each instrumented function identified by DefId.
14+
pub(crate) coverage_regions: RefCell<FxHashMap<Instance<'tcx>, FunctionCoverageRegions>>,
15+
}
16+
17+
impl<'tcx> CrateCoverageContext<'tcx> {
18+
pub fn new() -> Self {
19+
Self { coverage_regions: Default::default() }
20+
}
21+
}
1322

1423
/// Generates and exports the Coverage Map.
24+
// FIXME(richkadel): Actually generate and export the coverage map to LLVM.
25+
// The current implementation is actually just debug messages to show the data is available.
1526
pub fn finalize(cx: &CodegenCx<'_, '_>) {
16-
let instances = &*cx.instances.borrow();
17-
for instance in instances.keys() {
18-
if instance.def_id().krate == LOCAL_CRATE {
19-
// FIXME(richkadel): Is this check `krate == LOCAL_CRATE` right?
20-
//
21-
// NOTE: I'm considering at least one alternative to this loop on `instances`,
22-
// But if I do go with this, make sure the check on LOCAL_CRATE works for all cases.
23-
//
24-
//
25-
// Without it, I was crashing on some of the instances, with:
26-
//
27-
// src/librustc_metadata/rmeta/decoder.rs:1127:17: get_optimized_mir: missing MIR for `DefId(1:2867 ~ std[70cc]::io[0]::stdio[0]::_print[0])`
28-
//
29-
// This check avoids it, but still works for the very limited testing I've done so far.
30-
let coverageinfo = cx.tcx.coverageinfo(instance.def_id());
31-
if coverageinfo.num_counters > 0 {
32-
debug!(
33-
"Generate coverage map for: {}, hash: {:?}, num_counters: {}\n{}",
34-
instance,
35-
coverageinfo.hash,
36-
coverageinfo.num_counters,
37-
{
38-
let regions = &coverageinfo.coverage_regions;
39-
(0..regions.len() as u32)
40-
.map(|counter_index| {
41-
let region = &regions[counter_index];
42-
format!(
43-
" counter_index {} byte range: {}..{}",
44-
counter_index, region.start_byte_pos, region.end_byte_pos
45-
)
46-
})
47-
.collect::<Vec<String>>()
48-
.join("\n")
49-
}
50-
);
27+
let coverage_regions = &*cx.coverage_context().coverage_regions.borrow();
28+
for instance in coverage_regions.keys() {
29+
let coverageinfo = cx.tcx.coverageinfo(instance.def_id());
30+
debug_assert!(coverageinfo.num_counters > 0);
31+
debug!(
32+
"Generate coverage map for: {:?}, hash: {}, num_counters: {}",
33+
instance, coverageinfo.hash, coverageinfo.num_counters
34+
);
35+
let function_coverage_regions = &coverage_regions[instance];
36+
for (index, region) in function_coverage_regions.indexed_regions() {
37+
match region.kind {
38+
CoverageKind::Counter => debug!(
39+
" Counter {}, for {}..{}",
40+
index, region.coverage_span.start_byte_pos, region.coverage_span.end_byte_pos
41+
),
42+
CoverageKind::CounterExpression(lhs, op, rhs) => debug!(
43+
" CounterExpression {} = {} {:?} {}, for {}..{}",
44+
index,
45+
lhs,
46+
op,
47+
rhs,
48+
region.coverage_span.start_byte_pos,
49+
region.coverage_span.end_byte_pos
50+
),
5151
}
5252
}
53+
for unreachable in function_coverage_regions.unreachable_regions() {
54+
debug!(
55+
" Unreachable code region: {}..{}",
56+
unreachable.start_byte_pos, unreachable.end_byte_pos
57+
);
58+
}
5359
}
5460
}
5561

@@ -58,3 +64,49 @@ impl CoverageInfoMethods for CodegenCx<'ll, 'tcx> {
5864
finalize(self)
5965
}
6066
}
67+
68+
impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
69+
fn new_counter_region(
70+
&mut self,
71+
instance: Instance<'tcx>,
72+
index: u32,
73+
start_byte_pos: u32,
74+
end_byte_pos: u32,
75+
) {
76+
let mut coverage_regions = self.coverage_context().coverage_regions.borrow_mut();
77+
coverage_regions
78+
.entry(instance)
79+
.or_insert_with(|| FunctionCoverageRegions::new())
80+
.new_counter(index, start_byte_pos, end_byte_pos);
81+
}
82+
83+
fn new_counter_expression_region(
84+
&mut self,
85+
instance: Instance<'tcx>,
86+
index: u32,
87+
lhs: u32,
88+
op: CounterOp,
89+
rhs: u32,
90+
start_byte_pos: u32,
91+
end_byte_pos: u32,
92+
) {
93+
let mut coverage_regions = self.coverage_context().coverage_regions.borrow_mut();
94+
coverage_regions
95+
.entry(instance)
96+
.or_insert_with(|| FunctionCoverageRegions::new())
97+
.new_counter_expression(index, lhs, op, rhs, start_byte_pos, end_byte_pos);
98+
}
99+
100+
fn new_unreachable_region(
101+
&mut self,
102+
instance: Instance<'tcx>,
103+
start_byte_pos: u32,
104+
end_byte_pos: u32,
105+
) {
106+
let mut coverage_regions = self.coverage_context().coverage_regions.borrow_mut();
107+
coverage_regions
108+
.entry(instance)
109+
.or_insert_with(|| FunctionCoverageRegions::new())
110+
.new_unreachable(start_byte_pos, end_byte_pos);
111+
}
112+
}

src/librustc_codegen_llvm/intrinsic.rs

+3-8
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,6 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
141141
self.call(llfn, &[], None)
142142
}
143143
"count_code_region" => {
144-
use coverage::count_code_region_args::*;
145144
// FIXME(richkadel): The current implementation assumes the MIR for the given
146145
// caller_instance represents a single function. Validate and/or correct if inlining
147146
// and/or monomorphization invalidates these assumptions.
@@ -150,15 +149,11 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
150149
let (mangled_fn_name, _len_val) = self.const_str(mangled_fn.name);
151150
let hash = self.const_u64(coverageinfo.hash);
152151
let num_counters = self.const_u32(coverageinfo.num_counters);
152+
use coverage::count_code_region_args::*;
153153
let index = args[COUNTER_INDEX].immediate();
154154
debug!(
155-
"count_code_region to LLVM intrinsic instrprof.increment(fn_name={}, hash={:?}, num_counters={:?}, index={:?}), byte range {:?}..{:?}",
156-
mangled_fn.name,
157-
hash,
158-
num_counters,
159-
index,
160-
args[START_BYTE_POS].immediate(),
161-
args[END_BYTE_POS].immediate(),
155+
"count_code_region to LLVM intrinsic instrprof.increment(fn_name={}, hash={:?}, num_counters={:?}, index={:?})",
156+
mangled_fn.name, hash, num_counters, index,
162157
);
163158
self.instrprof_increment(mangled_fn_name, hash, num_counters, index)
164159
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use rustc_data_structures::fx::FxHashMap;
2+
use std::collections::hash_map;
3+
use std::slice;
4+
5+
#[derive(Copy, Clone, Debug)]
6+
pub enum CounterOp {
7+
Add,
8+
Subtract,
9+
}
10+
11+
pub enum CoverageKind {
12+
Counter,
13+
CounterExpression(u32, CounterOp, u32),
14+
}
15+
16+
pub struct CoverageSpan {
17+
pub start_byte_pos: u32,
18+
pub end_byte_pos: u32,
19+
}
20+
21+
pub struct CoverageRegion {
22+
pub kind: CoverageKind,
23+
pub coverage_span: CoverageSpan,
24+
}
25+
26+
/// Collects all of the coverage regions associated with (a) injected counters, (b) counter
27+
/// expressions (additions or subtraction), and (c) unreachable regions (always counted as zero),
28+
/// for a given Function. Counters and counter expressions are indexed because they can be operands
29+
/// in an expression.
30+
///
31+
/// Note, it's important to distinguish the `unreachable` region type from what LLVM's refers to as
32+
/// a "gap region" (or "gap area"). A gap region is a code region within a counted region (either
33+
/// counter or expression), but the line or lines in the gap region are not executable (such as
34+
/// lines with only whitespace or comments). According to LLVM Code Coverage Mapping documentation,
35+
/// "A count for a gap area is only used as the line execution count if there are no other regions
36+
/// on a line."
37+
pub struct FunctionCoverageRegions {
38+
indexed: FxHashMap<u32, CoverageRegion>,
39+
unreachable: Vec<CoverageSpan>,
40+
}
41+
42+
impl FunctionCoverageRegions {
43+
pub fn new() -> Self {
44+
Self { indexed: FxHashMap::default(), unreachable: Default::default() }
45+
}
46+
47+
pub fn new_counter(&mut self, index: u32, start_byte_pos: u32, end_byte_pos: u32) {
48+
self.indexed.insert(
49+
index,
50+
CoverageRegion {
51+
kind: CoverageKind::Counter,
52+
coverage_span: CoverageSpan { start_byte_pos, end_byte_pos },
53+
},
54+
);
55+
}
56+
57+
pub fn new_counter_expression(
58+
&mut self,
59+
index: u32,
60+
lhs: u32,
61+
op: CounterOp,
62+
rhs: u32,
63+
start_byte_pos: u32,
64+
end_byte_pos: u32,
65+
) {
66+
self.indexed.insert(
67+
index,
68+
CoverageRegion {
69+
kind: CoverageKind::CounterExpression(lhs, op, rhs),
70+
coverage_span: CoverageSpan { start_byte_pos, end_byte_pos },
71+
},
72+
);
73+
}
74+
75+
pub fn new_unreachable(&mut self, start_byte_pos: u32, end_byte_pos: u32) {
76+
self.unreachable.push(CoverageSpan { start_byte_pos, end_byte_pos });
77+
}
78+
79+
pub fn indexed_regions(&self) -> hash_map::Iter<'_, u32, CoverageRegion> {
80+
self.indexed.iter()
81+
}
82+
83+
pub fn unreachable_regions(&self) -> slice::Iter<'_, CoverageSpan> {
84+
self.unreachable.iter()
85+
}
86+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub mod map;
2+
3+
pub use map::CounterOp;

src/librustc_codegen_ssa/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use std::path::{Path, PathBuf};
3434
pub mod back;
3535
pub mod base;
3636
pub mod common;
37+
pub mod coverageinfo;
3738
pub mod debuginfo;
3839
pub mod glue;
3940
pub mod meth;

0 commit comments

Comments
 (0)