Skip to content

Commit fa86fc4

Browse files
committed
Refactor MIR coverage instrumentation
Lays a better foundation for injecting more counters in each function.
1 parent 12ddd60 commit fa86fc4

File tree

2 files changed

+118
-88
lines changed

2 files changed

+118
-88
lines changed

src/librustc_mir/transform/instrument_coverage.rs

+105-79
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@ use rustc_middle::hir;
77
use rustc_middle::ich::StableHashingContext;
88
use rustc_middle::mir::coverage::*;
99
use rustc_middle::mir::interpret::Scalar;
10-
use rustc_middle::mir::CoverageInfo;
1110
use rustc_middle::mir::{
12-
self, traversal, BasicBlock, BasicBlockData, Operand, Place, SourceInfo, StatementKind,
13-
Terminator, TerminatorKind, START_BLOCK,
11+
self, traversal, BasicBlock, BasicBlockData, CoverageInfo, Operand, Place, SourceInfo,
12+
SourceScope, StatementKind, Terminator, TerminatorKind,
1413
};
1514
use rustc_middle::ty;
1615
use rustc_middle::ty::query::Providers;
@@ -41,14 +40,14 @@ fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, mir_def_id: DefId) -> Coverage
4140
tcx.require_lang_item(lang_items::CoverageCounterSubtractFnLangItem, None);
4241

4342
// The `num_counters` argument to `llvm.instrprof.increment` is the number of injected
44-
// counters, with each counter having an index from `0..num_counters-1`. MIR optimization
43+
// counters, with each counter having a counter ID from `0..num_counters-1`. MIR optimization
4544
// may split and duplicate some BasicBlock sequences. Simply counting the calls may not
46-
// not work; but computing the num_counters by adding `1` to the highest index (for a given
45+
// work; but computing the num_counters by adding `1` to the highest counter_id (for a given
4746
// instrumented function) is valid.
4847
//
4948
// `num_expressions` is the number of counter expressions added to the MIR body. Both
5049
// `num_counters` and `num_expressions` are used to initialize new vectors, during backend
51-
// code generate, to lookup counters and expressions by their simple u32 indexes.
50+
// code generate, to lookup counters and expressions by simple u32 indexes.
5251
let mut num_counters: u32 = 0;
5352
let mut num_expressions: u32 = 0;
5453
for terminator in
@@ -57,27 +56,26 @@ fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, mir_def_id: DefId) -> Coverage
5756
if let TerminatorKind::Call { func: Operand::Constant(func), args, .. } = &terminator.kind {
5857
match func.literal.ty.kind {
5958
FnDef(id, _) if id == count_code_region_fn => {
60-
let index_arg =
59+
let counter_id_arg =
6160
args.get(count_code_region_args::COUNTER_ID).expect("arg found");
62-
let counter_index = mir::Operand::scalar_from_const(index_arg)
61+
let counter_id = mir::Operand::scalar_from_const(counter_id_arg)
6362
.to_u32()
64-
.expect("index arg is u32");
65-
num_counters = std::cmp::max(num_counters, counter_index + 1);
63+
.expect("counter_id arg is u32");
64+
num_counters = std::cmp::max(num_counters, counter_id + 1);
6665
}
6766
FnDef(id, _)
6867
if id == coverage_counter_add_fn || id == coverage_counter_subtract_fn =>
6968
{
70-
let index_arg = args
69+
let expression_id_arg = args
7170
.get(coverage_counter_expression_args::EXPRESSION_ID)
7271
.expect("arg found");
73-
let translated_index = mir::Operand::scalar_from_const(index_arg)
72+
let id_descending_from_max = mir::Operand::scalar_from_const(expression_id_arg)
7473
.to_u32()
75-
.expect("index arg is u32");
76-
// Counter expressions start with "translated indexes", descending from
77-
// `u32::MAX`, so the range of expression indexes is disjoint from the range of
78-
// counter indexes. This way, both counters and expressions can be operands in
79-
// other expressions.
80-
let expression_index = u32::MAX - translated_index;
74+
.expect("expression_id arg is u32");
75+
// Counter expressions are initially assigned IDs descending from `u32::MAX`, so
76+
// the range of expression IDs is disjoint from the range of counter IDs. This
77+
// way, both counters and expressions can be operands in other expressions.
78+
let expression_index = u32::MAX - id_descending_from_max;
8179
num_expressions = std::cmp::max(num_expressions, expression_index + 1);
8280
}
8381
_ => {}
@@ -97,12 +95,10 @@ fn call_terminators(data: &'tcx BasicBlockData<'tcx>) -> Option<&'tcx Terminator
9795

9896
impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
9997
fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, mir_body: &mut mir::Body<'tcx>) {
100-
if tcx.sess.opts.debugging_opts.instrument_coverage {
101-
// If the InstrumentCoverage pass is called on promoted MIRs, skip them.
102-
// See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601
103-
if src.promoted.is_none() {
104-
Instrumentor::new(tcx, src, mir_body).inject_counters();
105-
}
98+
// If the InstrumentCoverage pass is called on promoted MIRs, skip them.
99+
// See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601
100+
if src.promoted.is_none() {
101+
Instrumentor::new(tcx, src, mir_body).inject_counters();
106102
}
107103
}
108104
}
@@ -113,6 +109,12 @@ enum Op {
113109
Subtract,
114110
}
115111

112+
struct InjectedCall<'tcx> {
113+
func: Operand<'tcx>,
114+
args: Vec<Operand<'tcx>>,
115+
inject_at: Span,
116+
}
117+
116118
struct Instrumentor<'a, 'tcx> {
117119
tcx: TyCtxt<'tcx>,
118120
mir_def_id: DefId,
@@ -147,11 +149,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
147149
}
148150

149151
/// Expression IDs start from u32::MAX and go down because a CounterExpression can reference
150-
/// (add or subtract counts) of both Counter regions and CounterExpression regions. The indexes
151-
/// of each type of region must be contiguous, but also must be unique across both sets.
152-
/// The expression IDs are eventually translated into region indexes (starting after the last
153-
/// counter index, for the given function), during backend code generation, by the helper method
154-
/// `rustc_codegen_ssa::coverageinfo::map::FunctionCoverage::translate_expressions()`.
152+
/// (add or subtract counts) of both Counter regions and CounterExpression regions. The counter
153+
/// expression operand IDs must be unique across both types.
155154
fn next_expression(&mut self) -> u32 {
156155
assert!(self.num_counters < u32::MAX - self.num_expressions);
157156
let next = u32::MAX - self.num_expressions;
@@ -171,17 +170,25 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
171170
}
172171

173172
fn inject_counters(&mut self) {
173+
let mir_body = &self.mir_body;
174174
let body_span = self.hir_body.value.span;
175-
debug!(
176-
"instrumenting {:?}, span: {}",
177-
self.mir_def_id,
178-
self.tcx.sess.source_map().span_to_string(body_span)
179-
);
175+
debug!("instrumenting {:?}, span: {:?}", self.mir_def_id, body_span);
180176

181177
// FIXME(richkadel): As a first step, counters are only injected at the top of each
182178
// function. The complete solution will inject counters at each conditional code branch.
183-
let next_block = START_BLOCK;
184-
self.inject_counter(body_span, next_block);
179+
let _ignore = mir_body;
180+
let id = self.next_counter();
181+
let function_source_hash = self.function_source_hash();
182+
let code_region = body_span;
183+
let scope = rustc_middle::mir::OUTERMOST_SOURCE_SCOPE;
184+
let is_cleanup = false;
185+
let next_block = rustc_middle::mir::START_BLOCK;
186+
self.inject_call(
187+
self.make_counter(id, function_source_hash, code_region),
188+
scope,
189+
is_cleanup,
190+
next_block,
191+
);
185192

186193
// FIXME(richkadel): The next step to implement source based coverage analysis will be
187194
// instrumenting branches within functions, and some regions will be counted by "counter
@@ -190,57 +197,68 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
190197
let fake_use = false;
191198
if fake_use {
192199
let add = false;
193-
if add {
194-
self.inject_counter_expression(body_span, next_block, 1, Op::Add, 2);
195-
} else {
196-
self.inject_counter_expression(body_span, next_block, 1, Op::Subtract, 2);
197-
}
200+
let lhs = 1;
201+
let op = if add { Op::Add } else { Op::Subtract };
202+
let rhs = 2;
203+
204+
let code_region = body_span;
205+
let scope = rustc_middle::mir::OUTERMOST_SOURCE_SCOPE;
206+
let is_cleanup = false;
207+
let next_block = rustc_middle::mir::START_BLOCK;
208+
209+
let id = self.next_expression();
210+
self.inject_call(
211+
self.make_expression(id, code_region, lhs, op, rhs),
212+
scope,
213+
is_cleanup,
214+
next_block,
215+
);
198216
}
199217
}
200218

201-
fn inject_counter(&mut self, code_region: Span, next_block: BasicBlock) -> u32 {
202-
let counter_id = self.next_counter();
203-
let function_source_hash = self.function_source_hash();
204-
let injection_point = code_region.shrink_to_lo();
219+
fn make_counter(
220+
&self,
221+
id: u32,
222+
function_source_hash: u64,
223+
code_region: Span,
224+
) -> InjectedCall<'tcx> {
225+
let inject_at = code_region.shrink_to_lo();
205226

206-
let count_code_region_fn = function_handle(
227+
let func = function_handle(
207228
self.tcx,
208229
self.tcx.require_lang_item(lang_items::CountCodeRegionFnLangItem, None),
209-
injection_point,
230+
inject_at,
210231
);
211232

212233
let mut args = Vec::new();
213234

214235
use count_code_region_args::*;
215236
debug_assert_eq!(FUNCTION_SOURCE_HASH, args.len());
216-
args.push(self.const_u64(function_source_hash, injection_point));
237+
args.push(self.const_u64(function_source_hash, inject_at));
217238

218239
debug_assert_eq!(COUNTER_ID, args.len());
219-
args.push(self.const_u32(counter_id, injection_point));
240+
args.push(self.const_u32(id, inject_at));
220241

221242
debug_assert_eq!(START_BYTE_POS, args.len());
222-
args.push(self.const_u32(code_region.lo().to_u32(), injection_point));
243+
args.push(self.const_u32(code_region.lo().to_u32(), inject_at));
223244

224245
debug_assert_eq!(END_BYTE_POS, args.len());
225-
args.push(self.const_u32(code_region.hi().to_u32(), injection_point));
226-
227-
self.inject_call(count_code_region_fn, args, injection_point, next_block);
246+
args.push(self.const_u32(code_region.hi().to_u32(), inject_at));
228247

229-
counter_id
248+
InjectedCall { func, args, inject_at }
230249
}
231250

232-
fn inject_counter_expression(
233-
&mut self,
251+
fn make_expression(
252+
&self,
253+
id: u32,
234254
code_region: Span,
235-
next_block: BasicBlock,
236255
lhs: u32,
237256
op: Op,
238257
rhs: u32,
239-
) -> u32 {
240-
let expression_id = self.next_expression();
241-
let injection_point = code_region.shrink_to_lo();
258+
) -> InjectedCall<'tcx> {
259+
let inject_at = code_region.shrink_to_lo();
242260

243-
let count_code_region_fn = function_handle(
261+
let func = function_handle(
244262
self.tcx,
245263
self.tcx.require_lang_item(
246264
match op {
@@ -249,43 +267,51 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
249267
},
250268
None,
251269
),
252-
injection_point,
270+
inject_at,
253271
);
254272

255273
let mut args = Vec::new();
256274

257275
use coverage_counter_expression_args::*;
258276
debug_assert_eq!(EXPRESSION_ID, args.len());
259-
args.push(self.const_u32(expression_id, injection_point));
277+
args.push(self.const_u32(id, inject_at));
260278

261279
debug_assert_eq!(LEFT_ID, args.len());
262-
args.push(self.const_u32(lhs, injection_point));
280+
args.push(self.const_u32(lhs, inject_at));
263281

264282
debug_assert_eq!(RIGHT_ID, args.len());
265-
args.push(self.const_u32(rhs, injection_point));
283+
args.push(self.const_u32(rhs, inject_at));
266284

267285
debug_assert_eq!(START_BYTE_POS, args.len());
268-
args.push(self.const_u32(code_region.lo().to_u32(), injection_point));
286+
args.push(self.const_u32(code_region.lo().to_u32(), inject_at));
269287

270288
debug_assert_eq!(END_BYTE_POS, args.len());
271-
args.push(self.const_u32(code_region.hi().to_u32(), injection_point));
289+
args.push(self.const_u32(code_region.hi().to_u32(), inject_at));
272290

273-
self.inject_call(count_code_region_fn, args, injection_point, next_block);
274-
275-
expression_id
291+
InjectedCall { func, args, inject_at }
276292
}
277293

278294
fn inject_call(
279295
&mut self,
280-
func: Operand<'tcx>,
281-
args: Vec<Operand<'tcx>>,
282-
fn_span: Span,
296+
call: InjectedCall<'tcx>,
297+
scope: SourceScope,
298+
is_cleanup: bool,
283299
next_block: BasicBlock,
284300
) {
301+
let InjectedCall { func, args, inject_at } = call;
302+
debug!(
303+
" injecting {}call to {:?}({:?}) at: {:?}, scope: {:?}",
304+
if is_cleanup { "cleanup " } else { "" },
305+
func,
306+
args,
307+
inject_at,
308+
scope,
309+
);
310+
285311
let mut patch = MirPatch::new(self.mir_body);
286312

287-
let temp = patch.new_temp(self.tcx.mk_unit(), fn_span);
288-
let new_block = patch.new_block(placeholder_block(fn_span));
313+
let temp = patch.new_temp(self.tcx.mk_unit(), inject_at);
314+
let new_block = patch.new_block(placeholder_block(inject_at, scope, is_cleanup));
289315
patch.patch_terminator(
290316
new_block,
291317
TerminatorKind::Call {
@@ -295,7 +321,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
295321
destination: Some((Place::from(temp), new_block)),
296322
cleanup: None,
297323
from_hir_call: false,
298-
fn_span,
324+
fn_span: inject_at,
299325
},
300326
);
301327

@@ -325,15 +351,15 @@ fn function_handle<'tcx>(tcx: TyCtxt<'tcx>, fn_def_id: DefId, span: Span) -> Ope
325351
Operand::function_handle(tcx, fn_def_id, substs, span)
326352
}
327353

328-
fn placeholder_block(span: Span) -> BasicBlockData<'tcx> {
354+
fn placeholder_block(span: Span, scope: SourceScope, is_cleanup: bool) -> BasicBlockData<'tcx> {
329355
BasicBlockData {
330356
statements: vec![],
331357
terminator: Some(Terminator {
332-
source_info: SourceInfo::outermost(span),
358+
source_info: SourceInfo { span, scope },
333359
// this gets overwritten by the counter Call
334360
kind: TerminatorKind::Unreachable,
335361
}),
336-
is_cleanup: false,
362+
is_cleanup,
337363
}
338364
}
339365

src/librustc_mir/transform/mod.rs

+13-9
Original file line numberDiff line numberDiff line change
@@ -332,21 +332,25 @@ fn mir_validated(
332332
body.required_consts = required_consts;
333333

334334
let promote_pass = promote_consts::PromoteTemps::default();
335+
let promote: &[&dyn MirPass<'tcx>] = &[
336+
// What we need to run borrowck etc.
337+
&promote_pass,
338+
&simplify::SimplifyCfg::new("qualify-consts"),
339+
];
340+
341+
let opt_coverage: &[&dyn MirPass<'tcx>] = if tcx.sess.opts.debugging_opts.instrument_coverage {
342+
&[&instrument_coverage::InstrumentCoverage]
343+
} else {
344+
&[]
345+
};
346+
335347
run_passes(
336348
tcx,
337349
&mut body,
338350
InstanceDef::Item(def.to_global()),
339351
None,
340352
MirPhase::Validated,
341-
&[&[
342-
// What we need to run borrowck etc.
343-
&promote_pass,
344-
&simplify::SimplifyCfg::new("qualify-consts"),
345-
// If the `instrument-coverage` option is enabled, analyze the CFG, identify each
346-
// conditional branch, construct a coverage map to be passed to LLVM, and inject
347-
// counters where needed.
348-
&instrument_coverage::InstrumentCoverage,
349-
]],
353+
&[promote, opt_coverage],
350354
);
351355

352356
let promoted = promote_pass.promoted_fragments.into_inner();

0 commit comments

Comments
 (0)