Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP - Changes to try to fix and reland cover unreachable statements #85210

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions compiler/rustc_codegen_ssa/src/coverageinfo/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub struct Expression {
/// only whitespace or comments). According to LLVM Code Coverage Mapping documentation, "A count
/// for a gap area is only used as the line execution count if there are no other regions on a
/// line."
#[derive(Debug)]
pub struct FunctionCoverage<'tcx> {
instance: Instance<'tcx>,
source_hash: u64,
Expand All @@ -49,9 +50,9 @@ impl<'tcx> FunctionCoverage<'tcx> {
}

fn create(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, is_used: bool) -> Self {
let coverageinfo = tcx.coverageinfo(instance.def_id());
let coverageinfo = tcx.coverageinfo(instance.def);
debug!(
"FunctionCoverage::new(instance={:?}) has coverageinfo={:?}. is_used={}",
"FunctionCoverage::create(instance={:?}) has coverageinfo={:?}. is_used={}",
instance, coverageinfo, is_used
);
Self {
Expand Down Expand Up @@ -113,6 +114,14 @@ impl<'tcx> FunctionCoverage<'tcx> {
expression_id, lhs, op, rhs, region
);
let expression_index = self.expression_index(u32::from(expression_id));
debug_assert!(
expression_index.as_usize() < self.expressions.len(),
"expression_index {} is out of range for expressions.len() = {}
for {:?}",
expression_index.as_usize(),
self.expressions.len(),
self,
);
if let Some(previous_expression) = self.expressions[expression_index].replace(Expression {
lhs,
op,
Expand Down
36 changes: 23 additions & 13 deletions compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,38 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {

let Coverage { kind, code_region } = coverage;
match kind {
CoverageKind::Counter { function_source_hash, id } => {
CoverageKind::Counter { function_source_hash, id, is_dead } => {
if bx.set_function_source_hash(instance, function_source_hash) {
// If `set_function_source_hash()` returned true, the coverage map is enabled,
// so continue adding the counter.
if let Some(code_region) = code_region {
// Note: Some counters do not have code regions, but may still be referenced
// from expressions. In that case, don't add the counter to the coverage map,
// but do inject the counter intrinsic.
// from expressions. In that case, don't add the counter to the coverage
// map, but do inject the counter intrinsic.
if is_dead {
debug!(
"Adding coverage counter for dead code region: instance={:?},
function_source_hash={:?}, id={:?}, code_region={:?}",
instance, function_source_hash, id, code_region
);
}
bx.add_coverage_counter(instance, id, code_region);
}

let coverageinfo = bx.tcx().coverageinfo(instance.def_id());
if !is_dead {
let coverageinfo = bx.tcx().coverageinfo(instance.def);

let fn_name = bx.get_pgo_func_name_var(instance);
let hash = bx.const_u64(function_source_hash);
let num_counters = bx.const_u32(coverageinfo.num_counters);
let index = bx.const_u32(id.zero_based_index());
debug!(
"codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})",
fn_name, hash, num_counters, index,
);
bx.instrprof_increment(fn_name, hash, num_counters, index);
let fn_name = bx.get_pgo_func_name_var(instance);
let hash = bx.const_u64(function_source_hash);
let num_counters = bx.const_u32(coverageinfo.num_counters);
let index = bx.const_u32(id.zero_based_index());
debug!(
"codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?},
num_counters={:?}, index={:?})",
fn_name, hash, num_counters, index,
);
bx.instrprof_increment(fn_name, hash, num_counters, index);
}
}
}
CoverageKind::Expression { id, lhs, op, rhs } => {
Expand Down
25 changes: 20 additions & 5 deletions compiler/rustc_middle/src/mir/coverage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,25 @@ impl From<InjectedExpressionId> for ExpressionOperandId {

#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)]
pub enum CoverageKind {
Counter {
function_source_hash: u64,
id: CounterValueReference,
},
/// A live `Counter` will codegen statements to increment the counter when
/// its block is executed at runtime. A dead counter will not codegen
/// anything, but will still be added to the coverage map, so it can still
/// be referenced by expressions, and its code region will show up as
/// uncovered.
Counter { function_source_hash: u64, id: CounterValueReference, is_dead: bool },

/// A counter whose value can be computed when generating a coverage report.
/// This statement does not codegen anything, but the expression and code
/// region are added to the coverage map.
Expression {
id: InjectedExpressionId,
lhs: ExpressionOperandId,
op: Op,
rhs: ExpressionOperandId,
},

/// A code region that is never executed, and is never part of an
/// expression.
Unreachable,
}

Expand All @@ -134,7 +143,13 @@ impl Debug for CoverageKind {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
use CoverageKind::*;
match self {
Counter { id, .. } => write!(fmt, "Counter({:?})", id.index()),
Counter { id, is_dead, .. } => {
if *is_dead {
write!(fmt, "Counter({:?}/dead)", id.index())
} else {
write!(fmt, "Counter({:?})", id.index())
}
}
Expression { id, lhs, op, rhs } => write!(
fmt,
"Expression({:?}) = {} {} {}",
Expand Down
5 changes: 2 additions & 3 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,10 +335,9 @@ rustc_queries! {

/// Returns coverage summary info for a function, after executing the `InstrumentCoverage`
/// MIR pass (assuming the -Zinstrument-coverage option is enabled).
query coverageinfo(key: DefId) -> mir::CoverageInfo {
desc { |tcx| "retrieving coverage info from MIR for `{}`", tcx.def_path_str(key) }
query coverageinfo(key: ty::InstanceDef<'tcx>) -> mir::CoverageInfo {
desc { |tcx| "retrieving coverage info from MIR for `{}`", tcx.def_path_str(key.def_id()) }
storage(ArenaCacheSelector<'tcx>)
cache_on_disk_if { key.is_local() }
}

/// Returns the name of the file that contains the function body, if instrumented for coverage.
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/transform/const_goto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir/src/transform/coverage/counters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ impl CoverageCounters {
let counter = CoverageKind::Counter {
function_source_hash: self.function_source_hash,
id: self.next_counter(),
is_dead: false,
};
if self.debug_counters.is_enabled() {
self.debug_counters.add_counter(&counter, (debug_block_label_fn)());
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_mir/src/transform/coverage/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ impl CoverageVisitor {
}
}

fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo {
let mir_body = mir_body(tcx, def_id);
fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, instance_def: ty::InstanceDef<'tcx>) -> CoverageInfo {
let mir_body = tcx.instance_mir(instance_def);

let mut coverage_visitor = CoverageVisitor {
// num_counters always has at least the `ZERO` counter.
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/transform/deduplicate_blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/transform/early_otherwise_branch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_mir/src/transform/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(()));

Expand Down Expand Up @@ -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(()));
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/transform/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/transform/match_branches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
}

if should_cleanup {
simplify_cfg(body);
simplify_cfg(tcx, body);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ impl<'tcx> MirPass<'tcx> for MultipleReturnTerminators {
}
}

simplify::remove_dead_blocks(body)
simplify::remove_dead_blocks(tcx, body)
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/transform/remove_unneeded_drops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
40 changes: 35 additions & 5 deletions compiler/rustc_mir/src/transform/simplify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -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() {
Expand All @@ -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 {
Expand All @@ -315,6 +321,30 @@ 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.drain(..) {
if let StatementKind::Coverage(box coverage) = statement.kind {
dropped_coverage.push((statement.source_info, coverage));
}
}
}
for (source_info, mut coverage) in dropped_coverage {
if let CoverageKind::Counter { ref mut is_dead, .. } = coverage.kind {
*is_dead = true;
}
basic_blocks[START_BLOCK]
.statements
.push(Statement { source_info, kind: StatementKind::Coverage(box coverage) })
}
}

pub struct SimplifyLocals;

impl<'tcx> MirPass<'tcx> for SimplifyLocals {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/transform/simplify_try.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/transform/unreachable_prop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl MirPass<'_> for UnreachablePropagation {
}

if replaced {
simplify::remove_dead_blocks(body);
simplify::remove_dead_blocks(tcx, body);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
12| 1| if b {
13| 1| println!("non_async_func println in block");
14| 1| }
^0
15| 1|}
16| |
17| |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
5| 1| if true {
6| 1| countdown = 10;
7| 1| }
^0
8| |
9| | const B: u32 = 100;
10| 1| let x = if countdown > 7 {
Expand All @@ -24,6 +25,7 @@
24| 1| if true {
25| 1| countdown = 10;
26| 1| }
^0
27| |
28| 1| if countdown > 7 {
29| 1| countdown -= 4;
Expand All @@ -42,6 +44,7 @@
41| 1| if true {
42| 1| countdown = 10;
43| 1| }
^0
44| |
45| 1| if countdown > 7 {
46| 1| countdown -= 4;
Expand All @@ -54,13 +57,14 @@
53| | } else {
54| 0| return;
55| | }
56| | } // Note: closing brace shows uncovered (vs. `0` for implicit else) because condition literal
57| | // `true` was const-evaluated. The compiler knows the `if` block will be executed.
56| 0| }
57| |
58| |
59| 1| let mut countdown = 0;
60| 1| if true {
61| 1| countdown = 1;
62| 1| }
^0
63| |
64| 1| let z = if countdown > 7 {
^0
Expand Down
Loading