1
Fork 0

Auto merge of #84797 - richkadel:cover-unreachable-statements, r=tmandry

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 (in [commit 1](https://github.com/rust-lang/rust/pull/84797/commits/0b0d293c7c46bdadf80e5304a667e34c53c0cf7e)).

r? `@tmandry`
cc: `@wesleywiser`
This commit is contained in:
bors 2021-05-07 10:06:40 +00:00
commit e5f83d24ae
21 changed files with 102 additions and 65 deletions

View file

@ -47,7 +47,7 @@ impl<'tcx> MirPass<'tcx> for ConstGoto {
// if we applied optimizations, we potentially have some cfg to cleanup to
// make it easier for further passes
if should_simplify {
simplify_cfg(body);
simplify_cfg(tcx, body);
simplify_locals(body, tcx);
}
}

View file

@ -26,7 +26,7 @@ impl<'tcx> MirPass<'tcx> for DeduplicateBlocks {
if has_opts_to_apply {
let mut opt_applier = OptApplier { tcx, duplicates };
opt_applier.visit_body(body);
simplify_cfg(body);
simplify_cfg(tcx, body);
}
}
}

View file

@ -164,7 +164,7 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
// Since this optimization adds new basic blocks and invalidates others,
// clean up the cfg to make it nicer for other passes
if should_cleanup {
simplify_cfg(body);
simplify_cfg(tcx, body);
}
}
}

View file

@ -964,7 +964,7 @@ fn create_generator_drop_shim<'tcx>(
// Make sure we remove dead blocks to remove
// unrelated code from the resume part of the function
simplify::remove_dead_blocks(&mut body);
simplify::remove_dead_blocks(tcx, &mut body);
dump_mir(tcx, None, "generator_drop", &0, &body, |_, _| Ok(()));
@ -1137,7 +1137,7 @@ fn create_generator_resume_function<'tcx>(
// Make sure we remove dead blocks to remove
// unrelated code from the drop part of the function
simplify::remove_dead_blocks(body);
simplify::remove_dead_blocks(tcx, body);
dump_mir(tcx, None, "generator_resume", &0, body, |_, _| Ok(()));
}

View file

@ -57,7 +57,7 @@ impl<'tcx> MirPass<'tcx> for Inline {
if inline(tcx, body) {
debug!("running simplify cfg on {:?}", body.source);
CfgSimplifier::new(body).simplify();
remove_dead_blocks(body);
remove_dead_blocks(tcx, body);
}
}
}

View file

@ -167,7 +167,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
}
if should_cleanup {
simplify_cfg(body);
simplify_cfg(tcx, body);
}
}
}

View file

@ -38,6 +38,6 @@ impl<'tcx> MirPass<'tcx> for MultipleReturnTerminators {
}
}
simplify::remove_dead_blocks(body)
simplify::remove_dead_blocks(tcx, body)
}
}

View file

@ -36,7 +36,7 @@ impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops {
// if we applied optimizations, we potentially have some cfg to cleanup to
// make it easier for further passes
if should_simplify {
simplify_cfg(body);
simplify_cfg(tcx, body);
}
}
}

View file

@ -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 {

View file

@ -558,7 +558,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyBranchSame {
if did_remove_blocks {
// We have dead blocks now, so remove those.
simplify::remove_dead_blocks(body);
simplify::remove_dead_blocks(tcx, body);
}
}
}

View file

@ -60,7 +60,7 @@ impl MirPass<'_> for UnreachablePropagation {
}
if replaced {
simplify::remove_dead_blocks(body);
simplify::remove_dead_blocks(tcx, body);
}
}
}