Skip to content

Commit d38359d

Browse files
committed
[WIP] Added spans to count_code_region() Call terminator
and added an index of CoverageRegion (start/end byte pos from the spans) to CoverageData. This should give me what I need to build coverage maps for basic counters. I also added FIXME notes on how I plan to include regions with counter expressions instead of counters.
1 parent 3e18ddc commit d38359d

File tree

12 files changed

+198
-1752
lines changed

12 files changed

+198
-1752
lines changed

src/libcore/intrinsics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1956,7 +1956,7 @@ extern "rust-intrinsic" {
19561956
/// generation.
19571957
#[cfg(not(bootstrap))]
19581958
#[lang = "count_code_region"]
1959-
pub fn count_code_region(index: u32);
1959+
pub fn count_code_region(index: u32, start_byte_pos: u32, end_byte_pos: u32);
19601960

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

src/librustc_codegen_llvm/intrinsic.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
140140
self.call(llfn, &[], None)
141141
}
142142
"count_code_region" => {
143+
use rustc_middle::mir::count_code_region_args::{
144+
COUNTER_INDEX, END_BYTE_POS, START_BYTE_POS,
145+
};
143146
// FIXME(richkadel): The current implementation assumes the MIR for the given
144147
// caller_instance represents a single function. Validate and/or correct if inlining
145148
// and/or monomorphization invalidates these assumptions.
@@ -148,10 +151,16 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
148151
let (mangled_fn_name, _len_val) = self.const_str(mangled_fn.name);
149152
let hash = self.const_u64(coverage_data.hash);
150153
let num_counters = self.const_u32(coverage_data.num_counters);
151-
let index = args[0].immediate();
154+
let index = args[COUNTER_INDEX].immediate();
152155
debug!(
153-
"count_code_region to LLVM intrinsic instrprof.increment(fn_name={}, hash={:?}, num_counters={:?}, index={:?})",
154-
mangled_fn.name, hash, num_counters, index
156+
"count_code_region to LLVM intrinsic instrprof.increment(fn_name={}, hash={:?}, num_counters={:?}, index={:?}), byte range {:?}..{:?}, coverage_regions: {:?}",
157+
mangled_fn.name,
158+
hash,
159+
num_counters,
160+
index,
161+
args[START_BYTE_POS].immediate(),
162+
args[END_BYTE_POS].immediate(),
163+
coverage_data.coverage_regions,
155164
);
156165
self.instrprof_increment(mangled_fn_name, hash, num_counters, index)
157166
}

src/librustc_llvm/build.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,6 @@ fn main() {
169169
cfg.file("../rustllvm/PassWrapper.cpp")
170170
.file("../rustllvm/RustWrapper.cpp")
171171
.file("../rustllvm/ArchiveWrapper.cpp")
172-
.file("../rustllvm/CoverageMappingWrapper.cpp")
173172
.file("../rustllvm/Linker.cpp")
174173
.cpp(true)
175174
.cpp_link_stdlib(None) // we handle this below

src/librustc_middle/arena.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ macro_rules! arena_types {
9494
[] upvars_mentioned: rustc_data_structures::fx::FxIndexMap<rustc_hir::HirId, rustc_hir::Upvar>, rustc_data_structures::fx::FxIndexMap<rustc_hir::HirId, rustc_hir::Upvar>;
9595
[] object_safety_violations: rustc_middle::traits::ObjectSafetyViolation, rustc_middle::traits::ObjectSafetyViolation;
9696
[] codegen_unit: rustc_middle::mir::mono::CodegenUnit<$tcx>, rustc_middle::mir::mono::CodegenUnit<'_x>;
97+
[] coverage_data: rustc_middle::mir::CoverageData, rustc_middle::mir::CoverageData;
9798
[] attribute: rustc_ast::ast::Attribute, rustc_ast::ast::Attribute;
9899
[] name_set: rustc_data_structures::fx::FxHashSet<rustc_span::symbol::Symbol>, rustc_data_structures::fx::FxHashSet<rustc_span::symbol::Symbol>;
99100
[] hir_id_set: rustc_hir::HirIdSet, rustc_hir::HirIdSet;

src/librustc_middle/mir/mod.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2994,6 +2994,22 @@ impl Location {
29942994
}
29952995
}
29962996

2997+
///////////////////////////////////////////////////////////////////////////
2998+
// Source code coverage
2999+
3000+
/// Coverage data associated with each function (MIR) instrumented with coverage counters, when
3001+
/// compiled with `-Zinstrument_coverage`. The query `tcx.coverage_data(DefId)` computes these
3002+
/// values on demand (during code generation). This query is only valid after executing the MIR pass
3003+
/// `InstrumentCoverage`.
3004+
#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable)]
3005+
pub struct CoverageRegion {
3006+
/// The code region's starting position within the source code file.
3007+
pub start_byte_pos: u32,
3008+
3009+
/// The code region's ending position within the source code file.
3010+
pub end_byte_pos: u32,
3011+
}
3012+
29973013
/// Coverage data associated with each function (MIR) instrumented with coverage counters, when
29983014
/// compiled with `-Zinstrument_coverage`. The query `tcx.coverage_data(DefId)` computes these
29993015
/// values on demand (during code generation). This query is only valid after executing the MIR pass
@@ -3007,4 +3023,14 @@ pub struct CoverageData {
30073023

30083024
/// The total number of coverage region counters added to the MIR `Body`.
30093025
pub num_counters: u32,
3026+
3027+
/// The start and end positions within a source file for the region of source code counted by
3028+
/// the given counter index.
3029+
pub coverage_regions: IndexVec<u32, CoverageRegion>,
3030+
}
3031+
3032+
pub mod count_code_region_args {
3033+
pub const COUNTER_INDEX: usize = 0;
3034+
pub const START_BYTE_POS: usize = 1;
3035+
pub const END_BYTE_POS: usize = 2;
30103036
}

src/librustc_middle/query/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ rustc_queries! {
231231
cache_on_disk_if { key.is_local() }
232232
}
233233

234-
query coverage_data(key: DefId) -> mir::CoverageData {
234+
query coverage_data(key: DefId) -> &'tcx mir::CoverageData {
235235
desc { |tcx| "retrieving coverage data from MIR for `{}`", tcx.def_path_str(key) }
236236
storage(ArenaCacheSelector<'tcx>)
237237
cache_on_disk_if { key.is_local() }

src/librustc_mir/transform/inline.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,14 @@ struct CallSite<'tcx> {
3939
impl<'tcx> MirPass<'tcx> for Inline {
4040
fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
4141
if tcx.sess.opts.debugging_opts.mir_opt_level >= 2 {
42-
Inliner { tcx, source }.run_pass(body);
42+
if tcx.sess.opts.debugging_opts.instrument_coverage {
43+
debug!("function inlining is disabled when compiling with `instrument_coverage`");
44+
// The current implementation of source code coverage injects code region counters
45+
// into the MIR, and assumes a 1-to-1 correspondence between MIR and source-code-
46+
// based function.
47+
} else {
48+
Inliner { tcx, source }.run_pass(body);
49+
}
4350
}
4451
}
4552
}

src/librustc_mir/transform/instrument_coverage.rs

Lines changed: 116 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,22 @@ use crate::util::patch::MirPatch;
33
use rustc_data_structures::fingerprint::Fingerprint;
44
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
55
use rustc_hir::lang_items;
6+
use rustc_index::vec::IndexVec;
67
use rustc_middle::hir;
78
use rustc_middle::ich::StableHashingContext;
8-
use rustc_middle::mir::interpret::{ConstValue, Scalar};
9+
use rustc_middle::mir::interpret::{ConstValue, InterpResult, Scalar};
910
use rustc_middle::mir::{
10-
self, traversal, BasicBlock, BasicBlockData, CoverageData, Operand, Place, SourceInfo,
11-
StatementKind, Terminator, TerminatorKind, START_BLOCK,
11+
self, traversal, BasicBlock, BasicBlockData, CoverageData, CoverageRegion, Operand, Place,
12+
SourceInfo, StatementKind, Terminator, TerminatorKind, START_BLOCK,
1213
};
1314
use rustc_middle::ty;
1415
use rustc_middle::ty::query::Providers;
1516
use rustc_middle::ty::TyCtxt;
1617
use rustc_middle::ty::{ConstKind, FnDef};
1718
use rustc_span::def_id::DefId;
18-
use rustc_span::Span;
19+
use rustc_span::{Pos, Span};
20+
21+
use rustc_middle::mir::count_code_region_args::{COUNTER_INDEX, END_BYTE_POS, START_BYTE_POS};
1922

2023
/// Inserts call to count_code_region() as a placeholder to be replaced during code generation with
2124
/// the intrinsic llvm.instrprof.increment.
@@ -24,46 +27,96 @@ pub struct InstrumentCoverage;
2427
/// The `query` provider for `CoverageData`, requested by `codegen_intrinsic_call()` when
2528
/// constructing the arguments for `llvm.instrprof.increment`.
2629
pub(crate) fn provide(providers: &mut Providers<'_>) {
27-
providers.coverage_data = |tcx, def_id| {
28-
let mir_body = tcx.optimized_mir(def_id);
29-
// FIXME(richkadel): The current implementation assumes the MIR for the given DefId
30-
// represents a single function. Validate and/or correct if inlining and/or monomorphization
31-
// invalidates these assumptions.
32-
let count_code_region_fn =
33-
tcx.require_lang_item(lang_items::CountCodeRegionFnLangItem, None);
34-
let mut num_counters: u32 = 0;
35-
// The `num_counters` argument to `llvm.instrprof.increment` is the number of injected
36-
// counters, with each counter having an index from `0..num_counters-1`. MIR optimization
37-
// may split and duplicate some BasicBlock sequences. Simply counting the calls may not
38-
// not work; but computing the num_counters by adding `1` to the highest index (for a given
39-
// instrumented function) is valid.
40-
for (_, data) in traversal::preorder(mir_body) {
41-
if let Some(terminator) = &data.terminator {
42-
if let TerminatorKind::Call { func: Operand::Constant(func), args, .. } =
43-
&terminator.kind
44-
{
45-
if let FnDef(called_fn_def_id, _) = func.literal.ty.kind {
46-
if called_fn_def_id == count_code_region_fn {
47-
if let Operand::Constant(constant) =
48-
args.get(0).expect("count_code_region has at least one arg")
49-
{
50-
if let ConstKind::Value(ConstValue::Scalar(value)) =
51-
constant.literal.val
52-
{
53-
let index = value
54-
.to_u32()
55-
.expect("count_code_region index at arg0 is u32");
56-
num_counters = std::cmp::max(num_counters, index + 1);
57-
}
58-
}
59-
}
60-
}
30+
providers.coverage_data = |tcx, def_id| coverage_data_from_mir(tcx, def_id);
31+
}
32+
33+
fn coverage_data_from_mir<'tcx>(tcx: TyCtxt<'tcx>, mir_def_id: DefId) -> &'tcx CoverageData {
34+
let mir_body = tcx.optimized_mir(mir_def_id);
35+
// FIXME(richkadel): The current implementation assumes the MIR for the given DefId
36+
// represents a single function. Validate and/or correct if inlining and/or monomorphization
37+
// invalidates these assumptions.
38+
let count_code_region_fn = tcx.require_lang_item(lang_items::CountCodeRegionFnLangItem, None);
39+
40+
// The `num_counters` argument to `llvm.instrprof.increment` is the number of injected
41+
// counters, with each counter having an index from `0..num_counters-1`. MIR optimization
42+
// may split and duplicate some BasicBlock sequences. Simply counting the calls may not
43+
// not work; but computing the num_counters by adding `1` to the highest index (for a given
44+
// instrumented function) is valid.
45+
let mut indexed_regions = vec![];
46+
let mut num_counters: u32 = 0;
47+
//for terminator in count_code_region_terminators(tcx, mir_body) {
48+
for terminator in traversal::preorder(mir_body)
49+
.map(|(_, data)| (data, count_code_region_fn))
50+
.filter_map(count_code_region_terminator_filter)
51+
{
52+
// FIXME(richkadel): When analyzing the MIR and identifying CodeRegions to count by LLVM
53+
// counter_expressions, inject (and here, filter for) an additional call, such as:
54+
//
55+
// code_region_counter_expression(
56+
// left_counter: i32, signed_right_counter: i64,
57+
// start_byte_pos, end_byte_pos
58+
// )
59+
//
60+
// `abs(signed_right_counter)` (perhaps there's a better name?) is the index of the counter's
61+
// count to be added to or subtracted from (depending on the sign) the `left_counter`
62+
// index.
63+
//
64+
// Then store the expressions in a different IndexVec.
65+
// We may also want to consider "GapRegions" (clang had them... do we need them too?)
66+
if let TerminatorKind::Call { args, .. } = &terminator.kind {
67+
let (index, start_byte_pos, end_byte_pos) = count_code_region_args(&args);
68+
indexed_regions.push((index, CoverageRegion { start_byte_pos, end_byte_pos }));
69+
num_counters = std::cmp::max(num_counters, index + 1);
70+
}
71+
}
72+
let hash = if num_counters > 0 { hash_mir_source(tcx, mir_def_id) } else { 0 };
73+
let mut coverage_regions = IndexVec::<u32, CoverageRegion>::from_elem_n(
74+
CoverageRegion { start_byte_pos: 0, end_byte_pos: 0 },
75+
indexed_regions.len(),
76+
);
77+
for (index, region) in indexed_regions {
78+
coverage_regions[index] = region;
79+
}
80+
tcx.arena.alloc(CoverageData { num_counters, hash, coverage_regions })
81+
}
82+
83+
fn count_code_region_terminator_filter(
84+
(data, count_code_region_fn): (&'tcx BasicBlockData<'tcx>, DefId),
85+
) -> Option<&'tcx Terminator<'tcx>> {
86+
if let Some(terminator) = &data.terminator {
87+
if let TerminatorKind::Call { func: Operand::Constant(func), .. } = &terminator.kind {
88+
if let FnDef(called_fn_def_id, _) = func.literal.ty.kind {
89+
if called_fn_def_id == count_code_region_fn {
90+
return Some(&terminator);
6191
}
6292
}
6393
}
64-
let hash = if num_counters > 0 { hash_mir_source(tcx, def_id) } else { 0 };
65-
CoverageData { num_counters, hash }
66-
};
94+
}
95+
None
96+
}
97+
98+
fn arg<T, F>(to_val: F, args: &Vec<Operand<'tcx>>, pos: usize) -> T
99+
where
100+
F: FnOnce(Scalar) -> InterpResult<'static, T>,
101+
{
102+
match args.get(pos).unwrap_or_else(|| bug!("count_code_region arg{} not found", pos)) {
103+
Operand::Constant(constant) => match constant.literal.val {
104+
ConstKind::Value(ConstValue::Scalar(scalar)) => to_val(scalar).unwrap_or_else(|err| {
105+
bug!("count_code_region arg{}, {:?}: {:?}", pos, scalar, err);
106+
}),
107+
_ => bug!("count_code_region arg{}: ConstKind::Value expected", pos),
108+
},
109+
_ => bug!("count_code_region arg{}: Operand::Constant expected", pos),
110+
}
111+
}
112+
113+
fn count_code_region_args(args: &Vec<Operand<'tcx>>) -> (u32, u32, u32) {
114+
let to_u32 = |scalar: Scalar| scalar.to_u32();
115+
(
116+
arg(to_u32, args, COUNTER_INDEX),
117+
arg(to_u32, args, START_BYTE_POS),
118+
arg(to_u32, args, END_BYTE_POS),
119+
)
67120
}
68121

69122
struct Instrumentor<'tcx> {
@@ -102,17 +155,16 @@ impl<'tcx> Instrumentor<'tcx> {
102155
fn inject_counters(&mut self, mir_body: &mut mir::Body<'tcx>) {
103156
// FIXME(richkadel): As a first step, counters are only injected at the top of each
104157
// function. The complete solution will inject counters at each conditional code branch.
105-
let top_of_function = START_BLOCK;
106-
let entire_function = mir_body.span;
107-
108-
self.inject_counter(mir_body, top_of_function, entire_function);
158+
let code_region = mir_body.span;
159+
let next_block = START_BLOCK;
160+
self.inject_counter(mir_body, code_region, next_block);
109161
}
110162

111163
fn inject_counter(
112164
&mut self,
113165
mir_body: &mut mir::Body<'tcx>,
114-
next_block: BasicBlock,
115166
code_region: Span,
167+
next_block: BasicBlock,
116168
) {
117169
let injection_point = code_region.shrink_to_lo();
118170

@@ -121,12 +173,19 @@ impl<'tcx> Instrumentor<'tcx> {
121173
self.tcx.require_lang_item(lang_items::CountCodeRegionFnLangItem, None),
122174
injection_point,
123175
);
124-
let counter_index = Operand::const_from_scalar(
125-
self.tcx,
126-
self.tcx.types.u32,
127-
Scalar::from_u32(self.next_counter()),
128-
injection_point,
129-
);
176+
177+
let index = self.next_counter();
178+
179+
let mut args = Vec::new();
180+
181+
assert_eq!(COUNTER_INDEX, args.len());
182+
args.push(self.const_u32(index, injection_point));
183+
184+
assert_eq!(START_BYTE_POS, args.len());
185+
args.push(self.const_u32(code_region.lo().to_u32(), injection_point));
186+
187+
assert_eq!(END_BYTE_POS, args.len());
188+
args.push(self.const_u32(code_region.hi().to_u32(), injection_point));
130189

131190
let mut patch = MirPatch::new(mir_body);
132191

@@ -136,7 +195,7 @@ impl<'tcx> Instrumentor<'tcx> {
136195
new_block,
137196
TerminatorKind::Call {
138197
func: count_code_region_fn,
139-
args: vec![counter_index],
198+
args,
140199
// new_block will swapped with the next_block, after applying patch
141200
destination: Some((Place::from(temp), new_block)),
142201
cleanup: None,
@@ -154,6 +213,10 @@ impl<'tcx> Instrumentor<'tcx> {
154213
// `next_block`), just swap the indexes, leaving the rest of the graph unchanged.
155214
mir_body.basic_blocks_mut().swap(next_block, new_block);
156215
}
216+
217+
fn const_u32(&self, value: u32, span: Span) -> Operand<'tcx> {
218+
Operand::const_from_scalar(self.tcx, self.tcx.types.u32, Scalar::from_u32(value), span)
219+
}
157220
}
158221

159222
fn function_handle<'tcx>(tcx: TyCtxt<'tcx>, fn_def_id: DefId, span: Span) -> Operand<'tcx> {

src/librustc_typeck/check/intrinsic.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,9 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
352352
return;
353353
}
354354

355-
"count_code_region" => (0, vec![tcx.types.u32], tcx.mk_unit()),
355+
"count_code_region" => {
356+
(0, vec![tcx.types.u32, tcx.types.u32, tcx.types.u32], tcx.mk_unit())
357+
}
356358

357359
ref other => {
358360
struct_span_err!(

0 commit comments

Comments
 (0)