2828//! return.
2929
3030use crate :: MirPass ;
31+ use rustc_data_structures:: fx:: FxHashSet ;
3132use rustc_index:: vec:: { Idx , IndexVec } ;
3233use rustc_middle:: mir:: coverage:: * ;
3334use 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>) {
267268 return ;
268269 }
269270
270- let basic_blocks = body. basic_blocks_mut ( ) ;
271+ let basic_blocks = body. basic_blocks . as_mut ( ) ;
272+ let source_scopes = & body. source_scopes ;
271273 let mut replacements: Vec < _ > = ( 0 ..num_blocks) . map ( BasicBlock :: new) . collect ( ) ;
272274 let mut used_blocks = 0 ;
273275 for alive_index in reachable. iter ( ) {
@@ -282,7 +284,7 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
282284 }
283285
284286 if tcx. sess . instrument_coverage ( ) {
285- save_unreachable_coverage ( basic_blocks, used_blocks) ;
287+ save_unreachable_coverage ( basic_blocks, source_scopes , used_blocks) ;
286288 }
287289
288290 basic_blocks. raw . truncate ( used_blocks) ;
@@ -311,56 +313,62 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
311313/// `Unreachable` coverage statements. These are non-executable statements whose
312314/// code regions are still recorded in the coverage map, representing regions
313315/// 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.
314329fn save_unreachable_coverage (
315330 basic_blocks : & mut IndexVec < BasicBlock , BasicBlockData < ' _ > > ,
331+ source_scopes : & IndexVec < SourceScope , SourceScopeData < ' _ > > ,
316332 first_dead_block : usize ,
317333) {
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 ( ) {
338346 return ;
339347 }
340348
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 ( ) ) ) ;
350358 }
351359 }
352360 }
353361
354362 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 {
357365 source_info,
358366 kind : StatementKind :: Coverage ( Box :: new ( Coverage {
359367 kind : CoverageKind :: Unreachable ,
360368 code_region : Some ( code_region) ,
361369 } ) ) ,
362- } )
363- }
370+ } ,
371+ ) ) ;
364372}
365373
366374pub struct SimplifyLocals ;
0 commit comments