2929
3030use crate :: transform:: MirPass ;
3131use rustc_index:: vec:: { Idx , IndexVec } ;
32+ use rustc_middle:: mir:: coverage:: * ;
3233use rustc_middle:: mir:: visit:: { MutVisitor , MutatingUseContext , PlaceContext , Visitor } ;
3334use rustc_middle:: mir:: * ;
3435use rustc_middle:: ty:: TyCtxt ;
@@ -46,9 +47,9 @@ impl SimplifyCfg {
4647 }
4748}
4849
49- pub fn simplify_cfg ( body : & mut Body < ' _ > ) {
50+ pub fn simplify_cfg ( tcx : TyCtxt < ' tcx > , body : & mut Body < ' _ > ) {
5051 CfgSimplifier :: new ( body) . simplify ( ) ;
51- remove_dead_blocks ( body) ;
52+ remove_dead_blocks ( tcx , body) ;
5253
5354 // FIXME: Should probably be moved into some kind of pass manager
5455 body. basic_blocks_mut ( ) . raw . shrink_to_fit ( ) ;
@@ -59,9 +60,9 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg {
5960 Cow :: Borrowed ( & self . label )
6061 }
6162
62- fn run_pass ( & self , _tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
63+ fn run_pass ( & self , tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
6364 debug ! ( "SimplifyCfg({:?}) - simplifying {:?}" , self . label, body. source) ;
64- simplify_cfg ( body) ;
65+ simplify_cfg ( tcx , body) ;
6566 }
6667}
6768
@@ -286,7 +287,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
286287 }
287288}
288289
289- pub fn remove_dead_blocks ( body : & mut Body < ' _ > ) {
290+ pub fn remove_dead_blocks ( tcx : TyCtxt < ' tcx > , body : & mut Body < ' _ > ) {
290291 let reachable = traversal:: reachable_as_bitset ( body) ;
291292 let num_blocks = body. basic_blocks ( ) . len ( ) ;
292293 if num_blocks == reachable. count ( ) {
@@ -306,6 +307,11 @@ pub fn remove_dead_blocks(body: &mut Body<'_>) {
306307 }
307308 used_blocks += 1 ;
308309 }
310+
311+ if tcx. sess . instrument_coverage ( ) {
312+ save_unreachable_coverage ( basic_blocks, used_blocks) ;
313+ }
314+
309315 basic_blocks. raw . truncate ( used_blocks) ;
310316
311317 for block in basic_blocks {
@@ -315,6 +321,75 @@ pub fn remove_dead_blocks(body: &mut Body<'_>) {
315321 }
316322}
317323
324+ /// Some MIR transforms can determine at compile time that a sequences of
325+ /// statements will never be executed, so they can be dropped from the MIR.
326+ /// For example, an `if` or `else` block that is guaranteed to never be executed
327+ /// because its condition can be evaluated at compile time, such as by const
328+ /// evaluation: `if false { ... }`.
329+ ///
330+ /// Those statements are bypassed by redirecting paths in the CFG around the
331+ /// `dead blocks`; but with `-Z instrument-coverage`, the dead blocks usually
332+ /// include `Coverage` statements representing the Rust source code regions to
333+ /// be counted at runtime. Without these `Coverage` statements, the regions are
334+ /// lost, and the Rust source code will show no coverage information.
335+ ///
336+ /// What we want to show in a coverage report is the dead code with coverage
337+ /// counts of `0`. To do this, we need to save the code regions, by injecting
338+ /// `Unreachable` coverage statements. These are non-executable statements whose
339+ /// code regions are still recorded in the coverage map, representing regions
340+ /// with `0` executions.
341+ fn save_unreachable_coverage (
342+ basic_blocks : & mut IndexVec < BasicBlock , BasicBlockData < ' _ > > ,
343+ first_dead_block : usize ,
344+ ) {
345+ let has_live_counters = basic_blocks. raw [ 0 ..first_dead_block] . iter ( ) . any ( |live_block| {
346+ live_block. statements . iter ( ) . any ( |statement| {
347+ if let StatementKind :: Coverage ( coverage) = & statement. kind {
348+ matches ! ( coverage. kind, CoverageKind :: Counter { .. } )
349+ } else {
350+ false
351+ }
352+ } )
353+ } ) ;
354+ if !has_live_counters {
355+ // If there are no live `Counter` `Coverage` statements anymore, don't
356+ // move dead coverage to the `START_BLOCK`. Just allow the dead
357+ // `Coverage` statements to be dropped with the dead blocks.
358+ //
359+ // The `generator::StateTransform` MIR pass can create atypical
360+ // conditions, where all live `Counter`s are dropped from the MIR.
361+ //
362+ // At least one Counter per function is required by LLVM (and necessary,
363+ // to add the `function_hash` to the counter's call to the LLVM
364+ // intrinsic `instrprof.increment()`).
365+ return ;
366+ }
367+
368+ // Retain coverage info for dead blocks, so coverage reports will still
369+ // report `0` executions for the uncovered code regions.
370+ let mut dropped_coverage = Vec :: new ( ) ;
371+ for dead_block in basic_blocks. raw [ first_dead_block..] . iter ( ) {
372+ for statement in dead_block. statements . iter ( ) {
373+ if let StatementKind :: Coverage ( coverage) = & statement. kind {
374+ if let Some ( code_region) = & coverage. code_region {
375+ dropped_coverage. push ( ( statement. source_info , code_region. clone ( ) ) ) ;
376+ }
377+ }
378+ }
379+ }
380+
381+ let start_block = & mut basic_blocks[ START_BLOCK ] ;
382+ for ( source_info, code_region) in dropped_coverage {
383+ start_block. statements . push ( Statement {
384+ source_info,
385+ kind : StatementKind :: Coverage ( box Coverage {
386+ kind : CoverageKind :: Unreachable ,
387+ code_region : Some ( code_region) ,
388+ } ) ,
389+ } )
390+ }
391+ }
392+
318393pub struct SimplifyLocals ;
319394
320395impl < ' tcx > MirPass < ' tcx > for SimplifyLocals {
0 commit comments