Report coverage 0 of dead blocks
Fixes: #84018 With `-Z instrument-coverage`, coverage reporting of dead blocks (for example, blocks dropped because a conditional branch is dropped, based on const evaluation) is now supported. If `instrument-coverage` is enabled, `simplify::remove_dead_blocks()` finds all dropped coverage `Statement`s and adds their `code_region`s as `Unreachable` coverage `Statement`s to the `START_BLOCK`, so they are still included in the coverage map. Check out the resulting changes in the test coverage reports in this PR.
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
|
||||
use crate::transform::MirPass;
|
||||
use rustc_index::vec::{Idx, IndexVec};
|
||||
use rustc_middle::mir::coverage::*;
|
||||
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
@@ -46,9 +47,9 @@ impl SimplifyCfg {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn simplify_cfg(body: &mut Body<'_>) {
|
||||
pub fn simplify_cfg(tcx: TyCtxt<'tcx>, body: &mut Body<'_>) {
|
||||
CfgSimplifier::new(body).simplify();
|
||||
remove_dead_blocks(body);
|
||||
remove_dead_blocks(tcx, body);
|
||||
|
||||
// FIXME: Should probably be moved into some kind of pass manager
|
||||
body.basic_blocks_mut().raw.shrink_to_fit();
|
||||
@@ -59,9 +60,9 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg {
|
||||
Cow::Borrowed(&self.label)
|
||||
}
|
||||
|
||||
fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
debug!("SimplifyCfg({:?}) - simplifying {:?}", self.label, body.source);
|
||||
simplify_cfg(body);
|
||||
simplify_cfg(tcx, body);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,7 +287,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_dead_blocks(body: &mut Body<'_>) {
|
||||
pub fn remove_dead_blocks(tcx: TyCtxt<'tcx>, body: &mut Body<'_>) {
|
||||
let reachable = traversal::reachable_as_bitset(body);
|
||||
let num_blocks = body.basic_blocks().len();
|
||||
if num_blocks == reachable.count() {
|
||||
@@ -306,6 +307,11 @@ pub fn remove_dead_blocks(body: &mut Body<'_>) {
|
||||
}
|
||||
used_blocks += 1;
|
||||
}
|
||||
|
||||
if tcx.sess.instrument_coverage() {
|
||||
save_unreachable_coverage(basic_blocks, used_blocks);
|
||||
}
|
||||
|
||||
basic_blocks.raw.truncate(used_blocks);
|
||||
|
||||
for block in basic_blocks {
|
||||
@@ -315,6 +321,32 @@ pub fn remove_dead_blocks(body: &mut Body<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn save_unreachable_coverage(
|
||||
basic_blocks: &mut IndexVec<BasicBlock, BasicBlockData<'_>>,
|
||||
first_dead_block: usize,
|
||||
) {
|
||||
// retain coverage info for dead blocks, so coverage reports will still
|
||||
// report `0` executions for the uncovered code regions.
|
||||
let mut dropped_coverage = Vec::new();
|
||||
for dead_block in first_dead_block..basic_blocks.len() {
|
||||
for statement in basic_blocks[BasicBlock::new(dead_block)].statements.iter() {
|
||||
if let StatementKind::Coverage(coverage) = &statement.kind {
|
||||
if let Some(code_region) = &coverage.code_region {
|
||||
dropped_coverage.push((statement.source_info, code_region.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (source_info, code_region) in dropped_coverage {
|
||||
basic_blocks[START_BLOCK].statements.push(Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Coverage(box Coverage {
|
||||
kind: CoverageKind::Unreachable,
|
||||
code_region: Some(code_region),
|
||||
}),
|
||||
})
|
||||
}
|
||||
}
|
||||
pub struct SimplifyLocals;
|
||||
|
||||
impl<'tcx> MirPass<'tcx> for SimplifyLocals {
|
||||
|
||||
Reference in New Issue
Block a user