28
28
//! return.
29
29
30
30
use crate :: MirPass ;
31
+ use rustc_data_structures:: fx:: FxHashSet ;
31
32
use rustc_index:: vec:: { Idx , IndexVec } ;
32
33
use rustc_middle:: mir:: coverage:: * ;
33
34
use rustc_middle:: mir:: visit:: { MutVisitor , MutatingUseContext , PlaceContext , Visitor } ;
@@ -267,7 +268,8 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
267
268
return ;
268
269
}
269
270
270
- let basic_blocks = body. basic_blocks_mut ( ) ;
271
+ let basic_blocks = body. basic_blocks . as_mut ( ) ;
272
+ let source_scopes = & body. source_scopes ;
271
273
let mut replacements: Vec < _ > = ( 0 ..num_blocks) . map ( BasicBlock :: new) . collect ( ) ;
272
274
let mut used_blocks = 0 ;
273
275
for alive_index in reachable. iter ( ) {
@@ -282,7 +284,7 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
282
284
}
283
285
284
286
if tcx. sess . instrument_coverage ( ) {
285
- save_unreachable_coverage ( basic_blocks, used_blocks) ;
287
+ save_unreachable_coverage ( basic_blocks, source_scopes , used_blocks) ;
286
288
}
287
289
288
290
basic_blocks. raw . truncate ( used_blocks) ;
@@ -311,56 +313,62 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
311
313
/// `Unreachable` coverage statements. These are non-executable statements whose
312
314
/// code regions are still recorded in the coverage map, representing regions
313
315
/// with `0` executions.
316
+ ///
317
+ /// If there are no live `Counter` `Coverage` statements remaining, we remove
318
+ /// dead `Coverage` statements along with the dead blocks. Since at least one
319
+ /// counter per function is required by LLVM (and necessary, to add the
320
+ /// `function_hash` to the counter's call to the LLVM intrinsic
321
+ /// `instrprof.increment()`).
322
+ ///
323
+ /// The `generator::StateTransform` MIR pass and MIR inlining can create
324
+ /// atypical conditions, where all live `Counter`s are dropped from the MIR.
325
+ ///
326
+ /// With MIR inlining we can have coverage counters belonging to different
327
+ /// instances in a single body, so the strategy described above is applied to
328
+ /// coverage counters from each instance individually.
314
329
fn save_unreachable_coverage (
315
330
basic_blocks : & mut IndexVec < BasicBlock , BasicBlockData < ' _ > > ,
331
+ source_scopes : & IndexVec < SourceScope , SourceScopeData < ' _ > > ,
316
332
first_dead_block : usize ,
317
333
) {
318
- let has_live_counters = basic_blocks. raw [ 0 ..first_dead_block] . iter ( ) . any ( |live_block| {
319
- live_block. statements . iter ( ) . any ( |statement| {
320
- if let StatementKind :: Coverage ( coverage) = & statement. kind {
321
- matches ! ( coverage. kind, CoverageKind :: Counter { .. } )
322
- } else {
323
- false
324
- }
325
- } )
326
- } ) ;
327
- if !has_live_counters {
328
- // If there are no live `Counter` `Coverage` statements anymore, don't
329
- // move dead coverage to the `START_BLOCK`. Just allow the dead
330
- // `Coverage` statements to be dropped with the dead blocks.
331
- //
332
- // The `generator::StateTransform` MIR pass can create atypical
333
- // conditions, where all live `Counter`s are dropped from the MIR.
334
- //
335
- // At least one Counter per function is required by LLVM (and necessary,
336
- // to add the `function_hash` to the counter's call to the LLVM
337
- // intrinsic `instrprof.increment()`).
334
+ // Identify instances that still have some live coverage counters left.
335
+ let mut live = FxHashSet :: default ( ) ;
336
+ for basic_block in & basic_blocks. raw [ 0 ..first_dead_block] {
337
+ for statement in & basic_block. statements {
338
+ let StatementKind :: Coverage ( coverage) = & statement. kind else { continue } ;
339
+ let CoverageKind :: Counter { .. } = coverage. kind else { continue } ;
340
+ let instance = statement. source_info . scope . inlined_instance ( source_scopes) ;
341
+ live. insert ( instance) ;
342
+ }
343
+ }
344
+
345
+ if live. is_empty ( ) {
338
346
return ;
339
347
}
340
348
341
- // Retain coverage info for dead blocks, so coverage reports will still
342
- // report `0` executions for the uncovered code regions.
343
- let mut dropped_coverage = Vec :: new ( ) ;
344
- for dead_block in basic_blocks . raw [ first_dead_block.. ] . iter ( ) {
345
- for statement in dead_block . statements . iter ( ) {
346
- if let StatementKind :: Coverage ( coverage ) = & statement . kind {
347
- if let Some ( code_region ) = & coverage . code_region {
348
- dropped_coverage . push ( ( statement . source_info , code_region . clone ( ) ) ) ;
349
- }
349
+ // Retain coverage for instances that still have some live counters left.
350
+ let mut retained_coverage = Vec :: new ( ) ;
351
+ for dead_block in & basic_blocks . raw [ first_dead_block.. ] {
352
+ for statement in & dead_block . statements {
353
+ let StatementKind :: Coverage ( coverage ) = & statement . kind else { continue } ;
354
+ let Some ( code_region ) = & coverage . code_region else { continue } ;
355
+ let instance = statement . source_info . scope . inlined_instance ( source_scopes ) ;
356
+ if live . contains ( & instance ) {
357
+ retained_coverage . push ( ( statement . source_info , code_region . clone ( ) ) ) ;
350
358
}
351
359
}
352
360
}
353
361
354
362
let start_block = & mut basic_blocks[ START_BLOCK ] ;
355
- for ( source_info , code_region ) in dropped_coverage {
356
- start_block . statements . push ( Statement {
363
+ start_block . statements . extend ( retained_coverage . into_iter ( ) . map (
364
+ | ( source_info , code_region ) | Statement {
357
365
source_info,
358
366
kind : StatementKind :: Coverage ( Box :: new ( Coverage {
359
367
kind : CoverageKind :: Unreachable ,
360
368
code_region : Some ( code_region) ,
361
369
} ) ) ,
362
- } )
363
- }
370
+ } ,
371
+ ) ) ;
364
372
}
365
373
366
374
pub struct SimplifyLocals ;
0 commit comments