Skip to content

Commit dc762ce

Browse files
authored
Rollup merge of rust-lang#73684 - richkadel:llvm-coverage-map-gen-2, r=wesleywiser
add spans to injected coverage counters, extract with CoverageData query This is the next iteration on the Rust Coverage implementation, and follows PR rust-lang#73488 @tmandry @wesleywiser I came up with an approach for coverage spans, pushing them through the Call terminator as additional args so they can be extracted by the CoverageData query. I'm using an IndexVec to store them in CoverageData such that there can be only one per index (even if parts of the MIR get duplicated during optimization). If this approach works for you, I can quickly expand on this to build a separate IndexVec for counter expressions, using a separate call that will be ignored during code generation, but from which I can extract the counter expression values. Let me know your thoughts. Thanks! r? @tmandry Rust compiler MCP rust-lang/compiler-team#278 Relevant issue: rust-lang#34701 - Implement support for LLVMs code coverage instrumentation
2 parents 500634b + 5239a68 commit dc762ce

File tree

27 files changed

+585
-93
lines changed

27 files changed

+585
-93
lines changed

src/libcore/intrinsics.rs

+34-1
Original file line numberDiff line numberDiff line change
@@ -1958,7 +1958,40 @@ extern "rust-intrinsic" {
19581958
/// generation.
19591959
#[cfg(not(bootstrap))]
19601960
#[lang = "count_code_region"]
1961-
pub fn count_code_region(index: u32);
1961+
pub fn count_code_region(index: u32, start_byte_pos: u32, end_byte_pos: u32);
1962+
1963+
/// Internal marker for code coverage expressions, injected into the MIR when the
1964+
/// "instrument-coverage" option is enabled. This intrinsic is not converted into a
1965+
/// backend intrinsic call, but its arguments are extracted during the production of a
1966+
/// "coverage map", which is injected into the generated code, as additional data.
1967+
/// This marker identifies a code region and two other counters or counter expressions
1968+
/// whose sum is the number of times the code region was executed.
1969+
#[cfg(not(bootstrap))]
1970+
pub fn coverage_counter_add(
1971+
index: u32,
1972+
left_index: u32,
1973+
right_index: u32,
1974+
start_byte_pos: u32,
1975+
end_byte_pos: u32,
1976+
);
1977+
1978+
/// This marker identifies a code region and two other counters or counter expressions
1979+
/// whose difference is the number of times the code region was executed.
1980+
/// (See `coverage_counter_add` for more information.)
1981+
#[cfg(not(bootstrap))]
1982+
pub fn coverage_counter_subtract(
1983+
index: u32,
1984+
left_index: u32,
1985+
right_index: u32,
1986+
start_byte_pos: u32,
1987+
end_byte_pos: u32,
1988+
);
1989+
1990+
/// This marker identifies a code region to be added to the "coverage map" to indicate source
1991+
/// code that can never be reached.
1992+
/// (See `coverage_counter_add` for more information.)
1993+
#[cfg(not(bootstrap))]
1994+
pub fn coverage_unreachable(start_byte_pos: u32, end_byte_pos: u32);
19621995

19631996
/// See documentation of `<*const T>::guaranteed_eq` for details.
19641997
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]

src/librustc_codegen_llvm/base.rs

+5
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ pub fn compile_codegen_unit(
150150
cx.create_used_variable()
151151
}
152152

153+
// Finalize code coverage by injecting the coverage map
154+
if cx.sess().opts.debugging_opts.instrument_coverage {
155+
cx.coverageinfo_finalize();
156+
}
157+
153158
// Finalize debuginfo
154159
if cx.sess().opts.debuginfo != DebugInfo::None {
155160
cx.debuginfo_finalize();

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);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
use crate::builder::Builder;
2+
use crate::common::CodegenCx;
3+
use log::debug;
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;
8+
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+
}
22+
23+
/// 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.
26+
pub fn finalize(cx: &CodegenCx<'_, '_>) {
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+
),
51+
}
52+
}
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+
}
59+
}
60+
}
61+
62+
impl CoverageInfoMethods for CodegenCx<'ll, 'tcx> {
63+
fn coverageinfo_finalize(&self) {
64+
finalize(self)
65+
}
66+
}
67+
68+
impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
69+
fn add_counter_region(
70+
&mut self,
71+
instance: Instance<'tcx>,
72+
index: u32,
73+
start_byte_pos: u32,
74+
end_byte_pos: u32,
75+
) {
76+
debug!(
77+
"adding counter to coverage map: instance={:?}, index={}, byte range {}..{}",
78+
instance, index, start_byte_pos, end_byte_pos,
79+
);
80+
let mut coverage_regions = self.coverage_context().coverage_regions.borrow_mut();
81+
coverage_regions.entry(instance).or_default().add_counter(
82+
index,
83+
start_byte_pos,
84+
end_byte_pos,
85+
);
86+
}
87+
88+
fn add_counter_expression_region(
89+
&mut self,
90+
instance: Instance<'tcx>,
91+
index: u32,
92+
lhs: u32,
93+
op: CounterOp,
94+
rhs: u32,
95+
start_byte_pos: u32,
96+
end_byte_pos: u32,
97+
) {
98+
debug!(
99+
"adding counter expression to coverage map: instance={:?}, index={}, {} {:?} {}, byte range {}..{}",
100+
instance, index, lhs, op, rhs, start_byte_pos, end_byte_pos,
101+
);
102+
let mut coverage_regions = self.coverage_context().coverage_regions.borrow_mut();
103+
coverage_regions.entry(instance).or_default().add_counter_expression(
104+
index,
105+
lhs,
106+
op,
107+
rhs,
108+
start_byte_pos,
109+
end_byte_pos,
110+
);
111+
}
112+
113+
fn add_unreachable_region(
114+
&mut self,
115+
instance: Instance<'tcx>,
116+
start_byte_pos: u32,
117+
end_byte_pos: u32,
118+
) {
119+
debug!(
120+
"adding unreachable code to coverage map: instance={:?}, byte range {}..{}",
121+
instance, start_byte_pos, end_byte_pos,
122+
);
123+
let mut coverage_regions = self.coverage_context().coverage_regions.borrow_mut();
124+
coverage_regions.entry(instance).or_default().add_unreachable(start_byte_pos, end_byte_pos);
125+
}
126+
}

src/librustc_codegen_llvm/intrinsic.rs

+60-5
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ use rustc_ast::ast;
1313
use rustc_codegen_ssa::base::{compare_simd_types, to_immediate, wants_msvc_seh};
1414
use rustc_codegen_ssa::common::span_invalid_monomorphization_error;
1515
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
16+
use rustc_codegen_ssa::coverageinfo::CounterOp;
1617
use rustc_codegen_ssa::glue;
1718
use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
1819
use rustc_codegen_ssa::mir::place::PlaceRef;
1920
use rustc_codegen_ssa::traits::*;
2021
use rustc_codegen_ssa::MemFlags;
2122
use rustc_hir as hir;
23+
use rustc_middle::mir::coverage;
24+
use rustc_middle::mir::Operand;
2225
use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt};
2326
use rustc_middle::ty::{self, Ty};
2427
use rustc_middle::{bug, span_bug};
@@ -81,6 +84,53 @@ fn get_simple_intrinsic(cx: &CodegenCx<'ll, '_>, name: &str) -> Option<&'ll Valu
8184
}
8285

8386
impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
87+
fn is_codegen_intrinsic(
88+
&mut self,
89+
intrinsic: &str,
90+
args: &Vec<Operand<'tcx>>,
91+
caller_instance: ty::Instance<'tcx>,
92+
) -> bool {
93+
match intrinsic {
94+
"count_code_region" => {
95+
use coverage::count_code_region_args::*;
96+
self.add_counter_region(
97+
caller_instance,
98+
op_to_u32(&args[COUNTER_INDEX]),
99+
op_to_u32(&args[START_BYTE_POS]),
100+
op_to_u32(&args[END_BYTE_POS]),
101+
);
102+
true // Also inject the counter increment in the backend
103+
}
104+
"coverage_counter_add" | "coverage_counter_subtract" => {
105+
use coverage::coverage_counter_expression_args::*;
106+
self.add_counter_expression_region(
107+
caller_instance,
108+
op_to_u32(&args[COUNTER_EXPRESSION_INDEX]),
109+
op_to_u32(&args[LEFT_INDEX]),
110+
if intrinsic == "coverage_counter_add" {
111+
CounterOp::Add
112+
} else {
113+
CounterOp::Subtract
114+
},
115+
op_to_u32(&args[RIGHT_INDEX]),
116+
op_to_u32(&args[START_BYTE_POS]),
117+
op_to_u32(&args[END_BYTE_POS]),
118+
);
119+
false // Does not inject backend code
120+
}
121+
"coverage_unreachable" => {
122+
use coverage::coverage_unreachable_args::*;
123+
self.add_unreachable_region(
124+
caller_instance,
125+
op_to_u32(&args[START_BYTE_POS]),
126+
op_to_u32(&args[END_BYTE_POS]),
127+
);
128+
false // Does not inject backend code
129+
}
130+
_ => true, // Unhandled intrinsics should be passed to `codegen_intrinsic_call()`
131+
}
132+
}
133+
84134
fn codegen_intrinsic_call(
85135
&mut self,
86136
instance: ty::Instance<'tcx>,
@@ -143,15 +193,16 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
143193
// FIXME(richkadel): The current implementation assumes the MIR for the given
144194
// caller_instance represents a single function. Validate and/or correct if inlining
145195
// and/or monomorphization invalidates these assumptions.
146-
let coverage_data = tcx.coverage_data(caller_instance.def_id());
196+
let coverageinfo = tcx.coverageinfo(caller_instance.def_id());
147197
let mangled_fn = tcx.symbol_name(caller_instance);
148198
let (mangled_fn_name, _len_val) = self.const_str(mangled_fn.name);
149-
let hash = self.const_u64(coverage_data.hash);
150-
let num_counters = self.const_u32(coverage_data.num_counters);
151-
let index = args[0].immediate();
199+
let hash = self.const_u64(coverageinfo.hash);
200+
let num_counters = self.const_u32(coverageinfo.num_counters);
201+
use coverage::count_code_region_args::*;
202+
let index = args[COUNTER_INDEX].immediate();
152203
debug!(
153204
"count_code_region to LLVM intrinsic instrprof.increment(fn_name={}, hash={:?}, num_counters={:?}, index={:?})",
154-
mangled_fn.name, hash, num_counters, index
205+
mangled_fn.name, hash, num_counters, index,
155206
);
156207
self.instrprof_increment(mangled_fn_name, hash, num_counters, index)
157208
}
@@ -2131,3 +2182,7 @@ fn float_type_width(ty: Ty<'_>) -> Option<u64> {
21312182
_ => None,
21322183
}
21332184
}
2185+
2186+
fn op_to_u32<'tcx>(op: &Operand<'tcx>) -> u32 {
2187+
Operand::scalar_from_const(op).to_u32().expect("Scalar is u32")
2188+
}

src/librustc_codegen_llvm/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ mod callee;
5555
mod common;
5656
mod consts;
5757
mod context;
58+
mod coverageinfo;
5859
mod debuginfo;
5960
mod declare;
6061
mod intrinsic;

0 commit comments

Comments
 (0)