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:
commit
e5f83d24ae
21 changed files with 102 additions and 65 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(()));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -167,7 +167,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
|
|||
}
|
||||
|
||||
if should_cleanup {
|
||||
simplify_cfg(body);
|
||||
simplify_cfg(tcx, body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,6 @@ impl<'tcx> MirPass<'tcx> for MultipleReturnTerminators {
|
|||
}
|
||||
}
|
||||
|
||||
simplify::remove_dead_blocks(body)
|
||||
simplify::remove_dead_blocks(tcx, body)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ impl MirPass<'_> for UnreachablePropagation {
|
|||
}
|
||||
|
||||
if replaced {
|
||||
simplify::remove_dead_blocks(body);
|
||||
simplify::remove_dead_blocks(tcx, body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue