29
29
30
30
use crate :: transform:: MirPass ;
31
31
use rustc_index:: vec:: { Idx , IndexVec } ;
32
+ use rustc_middle:: mir:: coverage:: * ;
32
33
use rustc_middle:: mir:: visit:: { MutVisitor , MutatingUseContext , PlaceContext , Visitor } ;
33
34
use rustc_middle:: mir:: * ;
34
35
use rustc_middle:: ty:: TyCtxt ;
@@ -46,9 +47,17 @@ impl SimplifyCfg {
46
47
}
47
48
}
48
49
49
- pub fn simplify_cfg ( body : & mut Body < ' _ > ) {
50
+ pub fn simplify_cfg ( tcx : TyCtxt < ' tcx > , body : & mut Body < ' _ > ) {
51
+ simplify_cfg_with_coverage ( tcx, body, DroppedCoverage :: AssertNone ) ;
52
+ }
53
+
54
+ pub fn simplify_cfg_with_coverage (
55
+ tcx : TyCtxt < ' tcx > ,
56
+ body : & mut Body < ' _ > ,
57
+ dropped_coverage : DroppedCoverage ,
58
+ ) {
50
59
CfgSimplifier :: new ( body) . simplify ( ) ;
51
- remove_dead_blocks ( body) ;
60
+ remove_dead_blocks_with_coverage ( tcx , body, dropped_coverage ) ;
52
61
53
62
// FIXME: Should probably be moved into some kind of pass manager
54
63
body. basic_blocks_mut ( ) . raw . shrink_to_fit ( ) ;
@@ -59,12 +68,31 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg {
59
68
Cow :: Borrowed ( & self . label )
60
69
}
61
70
62
- fn run_pass ( & self , _tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
71
+ fn run_pass ( & self , tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
63
72
debug ! ( "SimplifyCfg({:?}) - simplifying {:?}" , self . label, body. source) ;
64
- simplify_cfg ( body) ;
73
+ simplify_cfg_with_coverage ( tcx , body, DroppedCoverage :: SaveUnreachable ) ;
65
74
}
66
75
}
67
76
77
+ /// How to handle `Coverage` statements in dropped `BasicBlocks`.
78
+ pub enum DroppedCoverage {
79
+ /// Allow the `Coverage` statement(s) in a dropped `BasicBlock` to be
80
+ /// dropped as well. (This may be because the `Coverage` statement was
81
+ /// already cloned.)
82
+ Allow ,
83
+
84
+ /// Assert that there are none. The current or most recent MIR pass(es)
85
+ /// should not have done anything that would have resulted in an
86
+ /// unreachable block with a `Coverage` statement.
87
+ AssertNone ,
88
+
89
+ /// Assume the `Coverage` statement(s) in a dropped `BacicBlock` are part
90
+ /// of a dead code region. Save them in the `START_BLOCK` but mark `Counter`
91
+ /// coverage as `is_dead` so they are not incremented. (Expressions from
92
+ /// dead blocks should already add up to zero.)
93
+ SaveUnreachable ,
94
+ }
95
+
68
96
pub struct CfgSimplifier < ' a , ' tcx > {
69
97
basic_blocks : & ' a mut IndexVec < BasicBlock , BasicBlockData < ' tcx > > ,
70
98
pred_count : IndexVec < BasicBlock , u32 > ,
@@ -286,7 +314,15 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
286
314
}
287
315
}
288
316
289
- pub fn remove_dead_blocks ( body : & mut Body < ' _ > ) {
317
+ pub fn remove_dead_blocks ( tcx : TyCtxt < ' tcx > , body : & mut Body < ' _ > ) {
318
+ remove_dead_blocks_with_coverage ( tcx, body, DroppedCoverage :: AssertNone ) ;
319
+ }
320
+
321
+ pub fn remove_dead_blocks_with_coverage (
322
+ tcx : TyCtxt < ' tcx > ,
323
+ body : & mut Body < ' _ > ,
324
+ dropped_coverage : DroppedCoverage ,
325
+ ) {
290
326
let reachable = traversal:: reachable_as_bitset ( body) ;
291
327
let num_blocks = body. basic_blocks ( ) . len ( ) ;
292
328
if num_blocks == reachable. count ( ) {
@@ -306,6 +342,16 @@ pub fn remove_dead_blocks(body: &mut Body<'_>) {
306
342
}
307
343
used_blocks += 1 ;
308
344
}
345
+
346
+ if tcx. sess . instrument_coverage ( ) {
347
+ use DroppedCoverage :: * ;
348
+ match dropped_coverage {
349
+ Allow => { }
350
+ AssertNone => assert_no_unreachable_coverage ( basic_blocks, used_blocks) ,
351
+ SaveUnreachable => save_unreachable_coverage ( basic_blocks, used_blocks) ,
352
+ }
353
+ }
354
+
309
355
basic_blocks. raw . truncate ( used_blocks) ;
310
356
311
357
for block in basic_blocks {
@@ -315,6 +361,47 @@ pub fn remove_dead_blocks(body: &mut Body<'_>) {
315
361
}
316
362
}
317
363
364
+ fn assert_no_unreachable_coverage (
365
+ basic_blocks : & mut IndexVec < BasicBlock , BasicBlockData < ' _ > > ,
366
+ first_dead_block : usize ,
367
+ ) {
368
+ for dead_block in first_dead_block..basic_blocks. len ( ) {
369
+ for statement in & basic_blocks[ BasicBlock :: new ( dead_block) ] . statements {
370
+ assert ! (
371
+ !matches!( statement. kind, StatementKind :: Coverage ( ..) ) ,
372
+ "Dead blocks should not have `Coverage` statements at this stage."
373
+ ) ;
374
+ }
375
+ }
376
+ }
377
+
378
+ fn save_unreachable_coverage (
379
+ basic_blocks : & mut IndexVec < BasicBlock , BasicBlockData < ' _ > > ,
380
+ first_dead_block : usize ,
381
+ ) {
382
+ // retain coverage info for dead blocks, so coverage reports will still
383
+ // report `0` executions for the uncovered code regions.
384
+ let mut dropped_coverage = Vec :: new ( ) ;
385
+ for dead_block in first_dead_block..basic_blocks. len ( ) {
386
+ for statement in basic_blocks[ BasicBlock :: new ( dead_block) ] . statements . iter ( ) {
387
+ if let StatementKind :: Coverage ( coverage) = & statement. kind {
388
+ if let Some ( code_region) = & coverage. code_region {
389
+ dropped_coverage. push ( ( statement. source_info , code_region. clone ( ) ) ) ;
390
+ }
391
+ }
392
+ }
393
+ }
394
+ for ( source_info, code_region) in dropped_coverage {
395
+ basic_blocks[ START_BLOCK ] . statements . push ( Statement {
396
+ source_info,
397
+ kind : StatementKind :: Coverage ( box Coverage {
398
+ kind : CoverageKind :: Unreachable ,
399
+ code_region : Some ( code_region) ,
400
+ } ) ,
401
+ } )
402
+ }
403
+ }
404
+
318
405
pub struct SimplifyLocals ;
319
406
320
407
impl < ' tcx > MirPass < ' tcx > for SimplifyLocals {
0 commit comments