From 51e07b8afec4c4502f35d72937b86d8dce5859ee Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Mon, 5 Oct 2020 16:36:10 -0700 Subject: [PATCH 01/18] Injecting expressions in place of counters where helpful Starting with implementing the Graph traits for the BasicCoverageBlock graph. --- .../src/transform/instrument_coverage.rs | 502 +++++++++++++----- 1 file changed, 379 insertions(+), 123 deletions(-) diff --git a/compiler/rustc_mir/src/transform/instrument_coverage.rs b/compiler/rustc_mir/src/transform/instrument_coverage.rs index 6824c73ab60a0..a757aa3eeee9c 100644 --- a/compiler/rustc_mir/src/transform/instrument_coverage.rs +++ b/compiler/rustc_mir/src/transform/instrument_coverage.rs @@ -3,8 +3,13 @@ use crate::util::pretty; use crate::util::spanview::{self, SpanViewable}; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::graph::dominators::Dominators; +use rustc_data_structures::graph::dominators::{self, Dominators}; +// TODO(richkadel): remove? +//use rustc_data_structures::graph::{self, GraphSuccessors, WithSuccessors}; +use rustc_data_structures::graph::{self, GraphSuccessors}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +// TODO(richkadel): remove? +//use rustc_data_structures::sync::{Lrc, OnceCell}; use rustc_data_structures::sync::Lrc; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; @@ -24,7 +29,11 @@ use rustc_span::def_id::DefId; use rustc_span::source_map::original_sp; use rustc_span::{BytePos, CharPos, Pos, SourceFile, Span, Symbol, SyntaxContext}; +// TODO(richkadel): remove? +use smallvec::SmallVec; + use std::cmp::Ordering; +use std::ops::{Index, IndexMut}; const ID_SEPARATOR: &str = ","; @@ -86,41 +95,44 @@ fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo } impl<'tcx> MirPass<'tcx> for InstrumentCoverage { - fn run_pass(&self, tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) { + fn run_pass( + &self, + tcx: TyCtxt<'tcx>, + mir_body: &mut mir::Body<'tcx>, + ) { + let mir_source = mir_body.source; + // If the InstrumentCoverage pass is called on promoted MIRs, skip them. // See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601 - if mir_body.source.promoted.is_some() { + if mir_source.promoted.is_some() { trace!( "InstrumentCoverage skipped for {:?} (already promoted for Miri evaluation)", - mir_body.source.def_id() + mir_source.def_id() ); return; } - let hir_id = tcx.hir().local_def_id_to_hir_id(mir_body.source.def_id().expect_local()); + let hir_id = tcx.hir().local_def_id_to_hir_id(mir_source.def_id().expect_local()); let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some(); // Only instrument functions, methods, and closures (not constants since they are evaluated // at compile time by Miri). // FIXME(#73156): Handle source code coverage in const eval if !is_fn_like { - trace!( - "InstrumentCoverage skipped for {:?} (not an FnLikeNode)", - mir_body.source.def_id(), - ); + trace!("InstrumentCoverage skipped for {:?} (not an FnLikeNode)", mir_source.def_id()); return; } // FIXME(richkadel): By comparison, the MIR pass `ConstProp` includes associated constants, // with functions, methods, and closures. I assume Miri is used for associated constants as // well. If not, we may need to include them here too. - trace!("InstrumentCoverage starting for {:?}", mir_body.source.def_id()); + trace!("InstrumentCoverage starting for {:?}", mir_source.def_id()); Instrumentor::new(&self.name(), tcx, mir_body).inject_counters(); - trace!("InstrumentCoverage starting for {:?}", mir_body.source.def_id()); + trace!("InstrumentCoverage starting for {:?}", mir_source.def_id()); } } -/// A BasicCoverageBlock (BCB) represents the maximal-length sequence of CFG (MIR) BasicBlocks +/// A BasicCoverageBlockData (BCB) represents the maximal-length sequence of CFG (MIR) BasicBlocks /// without conditional branches. /// /// The BCB allows coverage analysis to be performed on a simplified projection of the underlying @@ -144,25 +156,40 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage { /// Dominator/dominated relationships (which are fundamental to the coverage analysis algorithm) /// between two BCBs can be computed using the `mir::Body` `dominators()` with any `BasicBlock` /// member of each BCB. (For consistency, BCB's use the first `BasicBlock`, also referred to as the -/// `bcb_leader_bb`.) +/// `bcb` `leader_bb`.) /// /// The BCB CFG projection is critical to simplifying the coverage analysis by ensuring graph /// path-based queries (`is_dominated_by()`, `predecessors`, `successors`, etc.) have branch /// (control flow) significance. #[derive(Debug, Clone)] -struct BasicCoverageBlock { - pub blocks: Vec, +struct BasicCoverageBlockData { + basic_blocks: Vec, } -impl BasicCoverageBlock { +impl BasicCoverageBlockData { + pub fn from(basic_blocks: Vec) -> Self { + assert!(basic_blocks.len() > 0); + Self { + basic_blocks, + } + } + pub fn leader_bb(&self) -> BasicBlock { - self.blocks[0] + self.basic_blocks[0] + } + + pub fn last_bb(&self) -> BasicBlock { + *self.basic_blocks.last().unwrap() + } + + pub fn basic_blocks(&self) -> std::slice::Iter<'_, BasicBlock> { + self.basic_blocks.iter() } pub fn id(&self) -> String { format!( "@{}", - self.blocks + self.basic_blocks .iter() .map(|bb| bb.index().to_string()) .collect::>() @@ -171,23 +198,44 @@ impl BasicCoverageBlock { } } +// TODO(richkadel): Remove? +// impl std::ops::Deref for BasicCoverageBlockData { +// type Target = [BasicBlock]; + +// fn deref(&self) -> &[BasicBlock] { +// self.basic_blocks.deref() +// } +// } + +rustc_index::newtype_index! { + /// A node in the [control-flow graph][CFG] of BasicCoverageBlocks. + pub struct BasicCoverageBlock { +// TODO(richkadel): remove if not needed +// derive [HashStable] + DEBUG_FORMAT = "bcb{}", +// TODO(richkadel): remove if not needed +// const START_BLOCK = 0, + } +} + struct BasicCoverageBlocks { - vec: IndexVec>, +// TODO(richkadel): remove if not needed +// struct BasicCoverageBlocks<'a, 'tcx> { +// mir_body: &'a mut mir::Body<'tcx>, + bcbs: IndexVec, + bb_to_bcb: IndexVec>, + predecessors: IndexVec, + successors: IndexVec>, } impl BasicCoverageBlocks { +// TODO(richkadel): remove if not needed +//impl<'a, 'tcx> BasicCoverageBlocks<'a, 'tcx> { +// pub fn from_mir(mir_body: &'a mut mir::Body<'tcx>) -> Self { pub fn from_mir(mir_body: &mir::Body<'tcx>) -> Self { - let mut basic_coverage_blocks = - BasicCoverageBlocks { vec: IndexVec::from_elem_n(None, mir_body.basic_blocks().len()) }; - basic_coverage_blocks.extract_from_mir(mir_body); - basic_coverage_blocks - } - - pub fn iter(&self) -> impl Iterator { - self.vec.iter().filter_map(|option| option.as_ref()) - } - - fn extract_from_mir(&mut self, mir_body: &mir::Body<'tcx>) { + let len = mir_body.basic_blocks().len(); + let mut bcbs = IndexVec::with_capacity(len); + let mut bb_to_bcb = IndexVec::from_elem_n(None, len); // Traverse the CFG but ignore anything following an `unwind` let cfg_without_unwind = ShortCircuitPreorder::new(mir_body, |term_kind| { let mut successors = term_kind.successors(); @@ -248,17 +296,17 @@ impl BasicCoverageBlocks { // each block terminator's `successors()`. Coverage spans must map to actual source code, // so compiler generated blocks and paths can be ignored. To that end the CFG traversal // intentionally omits unwind paths. - let mut blocks = Vec::new(); + let mut basic_blocks = Vec::new(); for (bb, data) in cfg_without_unwind { - if let Some(last) = blocks.last() { + if let Some(last) = basic_blocks.last() { let predecessors = &mir_body.predecessors()[bb]; if predecessors.len() > 1 || !predecessors.contains(last) { // The `bb` has more than one _incoming_ edge, and should start its own - // `BasicCoverageBlock`. (Note, the `blocks` vector does not yet include `bb`; - // it contains a sequence of one or more sequential blocks with no intermediate - // branches in or out. Save these as a new `BasicCoverageBlock` before starting - // the new one.) - self.add_basic_coverage_block(blocks.split_off(0)); + // `BasicCoverageBlockData`. (Note, the `basic_blocks` vector does not yet include + // `bb`; it contains a sequence of one or more sequential basic_blocks with no + // intermediate branches in or out. Save these as a new `BasicCoverageBlockData` + // before starting the new one.) + Self::add_basic_coverage_block(&mut bcbs, &mut bb_to_bcb, basic_blocks.split_off(0)); debug!( " because {}", if predecessors.len() > 1 { @@ -269,7 +317,7 @@ impl BasicCoverageBlocks { ); } } - blocks.push(bb); + basic_blocks.push(bb); let term = data.terminator(); @@ -280,9 +328,9 @@ impl BasicCoverageBlocks { | TerminatorKind::Yield { .. } | TerminatorKind::SwitchInt { .. } => { // The `bb` has more than one _outgoing_ edge, or exits the function. Save the - // current sequence of `blocks` gathered to this point, as a new - // `BasicCoverageBlock`. - self.add_basic_coverage_block(blocks.split_off(0)); + // current sequence of `basic_blocks` gathered to this point, as a new + // `BasicCoverageBlockData`. + Self::add_basic_coverage_block(&mut bcbs, &mut bb_to_bcb, basic_blocks.split_off(0)); debug!(" because term.kind = {:?}", term.kind); // Note that this condition is based on `TerminatorKind`, even though it // theoretically boils down to `successors().len() != 1`; that is, either zero @@ -304,29 +352,237 @@ impl BasicCoverageBlocks { } } - if !blocks.is_empty() { - // process any remaining blocks into a final `BasicCoverageBlock` - self.add_basic_coverage_block(blocks.split_off(0)); + if !basic_blocks.is_empty() { + // process any remaining basic_blocks into a final `BasicCoverageBlockData` + Self::add_basic_coverage_block(&mut bcbs, &mut bb_to_bcb, basic_blocks.split_off(0)); debug!(" because the end of the CFG was reached while traversing"); } + + let basic_blocks = &mir_body.basic_blocks(); + let bb_predecessors = &mir_body.predecessors(); + + // Pre-transform MIR `BasicBlock` successors and predecessors into the BasicCoverageBlock + // equivalents. Note that since the BasicCoverageBlock graph has been fully simplified, the + // each predecessor of a BCB leader_bb should be in a unique BCB, and each successor of a + // BCB last_bb should bin in its own unique BCB. Therefore, collecting the BCBs using + // `bb_to_bcb` should work without requiring a deduplication step. + + let successors = IndexVec::from_fn_n(|bcb| { + let bcb_data = &bcbs[bcb]; + let bb_data = &basic_blocks[bcb_data.last_bb()]; + let bcb_successors = bb_data.terminator().successors().filter_map(|&successor_bb| bb_to_bcb[successor_bb]).collect::>(); + debug_assert!({ + let mut sorted = bcb_successors.clone(); + sorted.sort_unstable(); + let initial_len = sorted.len(); + sorted.dedup(); + sorted.len() == initial_len + }); + bcb_successors + }, bcbs.len()); + + let predecessors = IndexVec::from_fn_n(|bcb| { + let bcb_data = &bcbs[bcb]; + let bcb_predecessors = bb_predecessors[bcb_data.leader_bb()].iter().filter_map(|&predecessor_bb| bb_to_bcb[predecessor_bb]).collect::(); + debug_assert!({ + let mut sorted = bcb_predecessors.clone(); + sorted.sort_unstable(); + let initial_len = sorted.len(); + sorted.dedup(); + sorted.len() == initial_len + }); + bcb_predecessors + }, bcbs.len()); + + Self { +// mir_body, + bcbs, + bb_to_bcb, + predecessors, + successors, + } } - fn add_basic_coverage_block(&mut self, blocks: Vec) { - let leader_bb = blocks[0]; - let bcb = BasicCoverageBlock { blocks }; - debug!("adding BCB: {:?}", bcb); - self.vec[leader_bb] = Some(bcb); +// TODO(richkadel): remove if not needed + // pub fn iter(&self) -> impl Iterator { + // self.bcbs.iter() + // } + + pub fn iter_enumerated(&self) -> impl Iterator { + self.bcbs.iter_enumerated() + } + + fn bcb_from_bb(&self, bb: BasicBlock) -> BasicCoverageBlock { + self.bb_to_bcb[bb].expect("bb is not in any bcb (pre-filtered, such as unwind paths perhaps?)") + } + +// TODO(richkadel): remove if not needed + // fn bcb_data_from_bb(&self, bb: BasicBlock) -> &BasicCoverageBlockData { + // &self.bcbs[self.bcb_from_bb(bb)] + // } + +// TODO(richkadel): remove if not needed + // fn mir_body(&self) -> &mir::Body<'tcx> { + // self.mir_body + // } + +// TODO(richkadel): remove if not needed + // fn mir_body_mut(&mut self) -> &mut mir::Body<'tcx> { + // self.mir_body + // } + + fn add_basic_coverage_block(bcbs: &mut IndexVec, bb_to_bcb: &mut IndexVec>, basic_blocks: Vec) { + let bcb = BasicCoverageBlock::from_usize(bcbs.len()); + for &bb in basic_blocks.iter() { + bb_to_bcb[bb] = Some(bcb); + } + let bcb_data = BasicCoverageBlockData::from(basic_blocks); + debug!("adding bcb{}: {:?}", bcb.index(), bcb_data); + bcbs.push(bcb_data); } + + // TODO(richkadel): remove? + // #[inline] + // pub fn predecessors(&self) -> impl std::ops::Deref + '_ { + // self.predecessors + // } + + #[inline] + pub fn compute_bcb_dominators(&self) -> Dominators { + dominators::dominators(self) + } +} + + // TODO(richkadel): remove? +//impl<'a, 'tcx> graph::DirectedGraph for BasicCoverageBlocks<'a, 'tcx> { +impl graph::DirectedGraph for BasicCoverageBlocks { + type Node = BasicCoverageBlock; } -impl std::ops::Index for BasicCoverageBlocks { - type Output = BasicCoverageBlock; + // TODO(richkadel): remove? +//impl<'a, 'tcx> graph::WithNumNodes for BasicCoverageBlocks<'a, 'tcx> { +impl graph::WithNumNodes for BasicCoverageBlocks { + #[inline] + fn num_nodes(&self) -> usize { + self.bcbs.len() + } +} + + // TODO(richkadel): remove? +//impl<'a, 'tcx> graph::WithStartNode for BasicCoverageBlocks<'a, 'tcx> { +impl graph::WithStartNode for BasicCoverageBlocks { + #[inline] + fn start_node(&self) -> Self::Node { + self.bcb_from_bb(mir::START_BLOCK) + } +} - fn index(&self, index: BasicBlock) -> &Self::Output { - self.vec[index].as_ref().expect("is_some if BasicBlock is a BasicCoverageBlock leader") +// `BasicBlock` `Predecessors` uses a `SmallVec` of length 4 because, "Typically 95%+ of basic +// blocks have 4 or fewer predecessors." BasicCoverageBlocks should have the same or less. +type BcbPredecessors = SmallVec<[BasicCoverageBlock; 4]>; +// TODO(richkadel): remove? +//type BcbPredecessors = IndexVec>; + +pub type BcbSuccessors<'a> = std::slice::Iter<'a, BasicCoverageBlock>; + // TODO(richkadel): remove? +//pub type BcbSuccessors = std::slice::Iter<'_, BasicCoverageBlock>; + + // TODO(richkadel): remove? +//impl<'a, 'tcx> graph::WithSuccessors for BasicCoverageBlocks<'a, 'tcx> { +impl graph::WithSuccessors for BasicCoverageBlocks { + #[inline] + fn successors(&self, node: Self::Node) -> >::Iter { + self.successors[node].iter().cloned() } } + // TODO(richkadel): remove? +//impl<'a, 'tcx, 'b> graph::GraphSuccessors<'b> for BasicCoverageBlocks<'a, 'tcx> { +//impl<'b> graph::GraphSuccessors<'b> for BasicCoverageBlocks { +impl<'a> graph::GraphSuccessors<'a> for BasicCoverageBlocks { + type Item = BasicCoverageBlock; + // TODO(richkadel): remove? + //type Iter = std::iter::Cloned>; + //type Iter = std::iter::Cloned; + type Iter = std::iter::Cloned>; +} + + // TODO(richkadel): remove? +//impl graph::GraphPredecessors<'graph> for BasicCoverageBlocks<'a, 'tcx> { +impl graph::GraphPredecessors<'graph> for BasicCoverageBlocks { + type Item = BasicCoverageBlock; + // TODO(richkadel): remove? + //type Iter = smallvec::IntoIter; + type Iter = smallvec::IntoIter<[BasicCoverageBlock; 4]>; +} + + // TODO(richkadel): remove? +//impl graph::WithPredecessors for BasicCoverageBlocks<'a, 'tcx> { +impl graph::WithPredecessors for BasicCoverageBlocks { + #[inline] + fn predecessors(&self, node: Self::Node) -> >::Iter { + self.predecessors[node].clone().into_iter() + } +} + + // TODO(richkadel): remove? +//impl<'a, 'tcx> Index for BasicCoverageBlocks<'a, 'tcx> { +impl Index for BasicCoverageBlocks { + type Output = BasicCoverageBlockData; + + #[inline] + fn index(&self, index: BasicCoverageBlock) -> &BasicCoverageBlockData { + &self.bcbs[index] + } +} + + // TODO(richkadel): remove? +//impl<'a, 'tcx> IndexMut for BasicCoverageBlocks<'a, 'tcx> { +impl IndexMut for BasicCoverageBlocks { + #[inline] + fn index_mut(&mut self, index: BasicCoverageBlock) -> &mut BasicCoverageBlockData { + &mut self.bcbs[index] + } +} + + + + // TODO(richkadel): For any node, N, and one of its successors, H (so N -> H), if (_also_) + // N is_dominated_by H, then N -> H is a backedge. That is, we've identified that N -> H is + // at least _one_ of possibly multiple arcs that loop back to the start of the loop with + // "header" H, and this also means we've identified a loop, that has "header" H. + // + // H dominates everything inside the loop. + // + // So a SwitchInt target in a BasicBlock that is_dominated_by H and has a branch target to a + // BasicBlock that is: + // * not H, and ... (what if the SwitchInt branch target _is_ H? `continue`? is this a + // candidate for a middle or optional priority for getting a Counter?) + // * not is_dominated_by H + // is a branch that jumps outside the loop, and should get an actual Counter, most likely + // + // Or perhaps conversely, a SwitchInt dominated by H with a branch that has a target that + // ALSO is dominated by H should get a CounterExpression. + // + // + // So I need to identify all of the "H"'s, by identifying all of the backedges. + // + // If I have multiple H's (multiple loops), how do I decide which loop to compare a branch + // target (by dominator) to? + // + // Can I assume the traversal order is helpful here? I.e., the "last" encountered loop + // header is the (only?) one to compare to? (Probably not only... I don't see how that would + // work for nested loops.) + // + // What about multiple loops in sequence? + // + // + // What about nexted loops and jumping out of one or more of them at a time? + + + + + #[derive(Debug, Copy, Clone)] enum CoverageStatement { Statement(BasicBlock, Span, usize), @@ -399,7 +655,7 @@ fn term_type(kind: &TerminatorKind<'tcx>) -> &'static str { #[derive(Debug, Clone)] struct CoverageSpan { span: Span, - bcb_leader_bb: BasicBlock, + bcb: BasicCoverageBlock, coverage_statements: Vec, is_closure: bool, } @@ -408,7 +664,7 @@ impl CoverageSpan { pub fn for_statement( statement: &Statement<'tcx>, span: Span, - bcb: &BasicCoverageBlock, + bcb: BasicCoverageBlock, bb: BasicBlock, stmt_index: usize, ) -> Self { @@ -422,16 +678,16 @@ impl CoverageSpan { Self { span, - bcb_leader_bb: bcb.leader_bb(), + bcb, coverage_statements: vec![CoverageStatement::Statement(bb, span, stmt_index)], is_closure, } } - pub fn for_terminator(span: Span, bcb: &'a BasicCoverageBlock, bb: BasicBlock) -> Self { + pub fn for_terminator(span: Span, bcb: BasicCoverageBlock, bb: BasicBlock) -> Self { Self { span, - bcb_leader_bb: bcb.leader_bb(), + bcb, coverage_statements: vec![CoverageStatement::Terminator(bb, span)], is_closure: false, } @@ -458,10 +714,10 @@ impl CoverageSpan { pub fn is_dominated_by( &self, other: &CoverageSpan, - dominators: &Dominators, + bcb_dominators: &Dominators, ) -> bool { debug_assert!(!self.is_in_same_bcb(other)); - dominators.is_dominated_by(self.bcb_leader_bb, other.bcb_leader_bb) + bcb_dominators.is_dominated_by(self.bcb, other.bcb) } pub fn is_mergeable(&self, other: &Self) -> bool { @@ -469,7 +725,7 @@ impl CoverageSpan { } pub fn is_in_same_bcb(&self, other: &Self) -> bool { - self.bcb_leader_bb == other.bcb_leader_bb + self.bcb == other.bcb } } @@ -478,29 +734,45 @@ struct Instrumentor<'a, 'tcx> { tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx>, - dominators: Option>, - basic_coverage_blocks: Option, + bcb_dominators: Dominators, +//TODO(richkadel): remove? +// basic_coverage_blocks: BasicCoverageBlocks<'a, 'tcx>, + basic_coverage_blocks: BasicCoverageBlocks, function_source_hash: Option, next_counter_id: u32, num_expressions: u32, } impl<'a, 'tcx> Instrumentor<'a, 'tcx> { - fn new(pass_name: &'a str, tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self { + fn new( + pass_name: &'a str, + tcx: TyCtxt<'tcx>, + mir_body: &'a mut mir::Body<'tcx>, + ) -> Self { let hir_body = hir_body(tcx, mir_body.source.def_id()); + let basic_coverage_blocks = BasicCoverageBlocks::from_mir(mir_body); + let bcb_dominators = basic_coverage_blocks.compute_bcb_dominators(); Self { pass_name, tcx, mir_body, hir_body, - dominators: None, - basic_coverage_blocks: None, + basic_coverage_blocks, + bcb_dominators, function_source_hash: None, next_counter_id: CounterValueReference::START.as_u32(), num_expressions: 0, } } + // fn mir_body(&self) -> &mir::Body<'tcx> { + // self.basic_coverage_blocks.mir_body() + // } + + // fn mir_body_mut(&mut self) -> &mut mir::Body<'tcx> { + // self.basic_coverage_blocks.mir_body_mut() + // } + /// Counter IDs start from one and go up. fn next_counter(&mut self) -> CounterValueReference { assert!(self.next_counter_id < u32::MAX - self.num_expressions); @@ -519,16 +791,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { InjectedExpressionIndex::from(next) } - fn dominators(&self) -> &Dominators { - self.dominators.as_ref().expect("dominators must be initialized before calling") - } - - fn basic_coverage_blocks(&self) -> &BasicCoverageBlocks { - self.basic_coverage_blocks - .as_ref() - .expect("basic_coverage_blocks must be initialized before calling") - } - fn function_source_hash(&mut self) -> u64 { match self.function_source_hash { Some(hash) => hash, @@ -540,20 +802,17 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } } - fn inject_counters(&mut self) { + fn inject_counters(&'a mut self) { let tcx = self.tcx; let source_map = tcx.sess.source_map(); - let def_id = self.mir_body.source.def_id(); - let mir_body = &self.mir_body; + let mir_source = self.mir_body.source; + let def_id = mir_source.def_id(); let body_span = self.body_span(); let source_file = source_map.lookup_source_file(body_span.lo()); let file_name = Symbol::intern(&source_file.name.to_string()); debug!("instrumenting {:?}, span: {}", def_id, source_map.span_to_string(body_span)); - self.dominators.replace(mir_body.dominators()); - self.basic_coverage_blocks.replace(BasicCoverageBlocks::from_mir(mir_body)); - let coverage_spans = self.coverage_spans(); let span_viewables = if pretty::dump_enabled(tcx, self.pass_name, def_id) { @@ -564,11 +823,12 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // Inject a counter for each `CoverageSpan`. There can be multiple `CoverageSpan`s for a // given BCB, but only one actual counter needs to be incremented per BCB. `bb_counters` - // maps each `bcb_leader_bb` to its `Counter`, when injected. Subsequent `CoverageSpan`s + // maps each `bcb` to its `Counter`, when injected. Subsequent `CoverageSpan`s // for a BCB that already has a `Counter` will inject a `CounterExpression` instead, and // compute its value by adding `ZERO` to the BCB `Counter` value. - let mut bb_counters = IndexVec::from_elem_n(None, mir_body.basic_blocks().len()); - for CoverageSpan { span, bcb_leader_bb: bb, .. } in coverage_spans { + let mut bb_counters = IndexVec::from_elem_n(None, self.mir_body.basic_blocks().len()); + for CoverageSpan { span, bcb, .. } in coverage_spans { + let bb = self.basic_coverage_blocks[bcb].leader_bb(); if let Some(&counter_operand) = bb_counters[bb].as_ref() { let expression = self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO); @@ -594,15 +854,9 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } if let Some(span_viewables) = span_viewables { - let mut file = pretty::create_dump_file( - tcx, - "html", - None, - self.pass_name, - &0, - self.mir_body.source, - ) - .expect("Unexpected error creating MIR spanview HTML file"); + let mut file = + pretty::create_dump_file(tcx, "html", None, self.pass_name, &0, mir_source) + .expect("Unexpected error creating MIR spanview HTML file"); let crate_name = tcx.crate_name(def_id.krate); let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate(); let title = format!("{}.{} - Coverage Spans", crate_name, item_name); @@ -638,6 +892,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { let code_region = make_code_region(file_name, source_file, span); debug!(" injecting statement {:?} covering {:?}", coverage_kind, code_region); +// TODO(richkadel): remove? +// let data = &mut self.mir_body_mut()[block]; let data = &mut self.mir_body[block]; let source_info = data.terminator().source_info; let statement = Statement { @@ -647,15 +903,15 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { data.statements.push(statement); } - /// Converts the computed `BasicCoverageBlock`s into `SpanViewable`s. + /// Converts the computed `BasicCoverageBlockData`s into `SpanViewable`s. fn span_viewables(&self, coverage_spans: &Vec) -> Vec { let tcx = self.tcx; - let mir_body = &self.mir_body; let mut span_viewables = Vec::new(); for coverage_span in coverage_spans { - let bcb = self.bcb_from_coverage_span(coverage_span); - let CoverageSpan { span, bcb_leader_bb: bb, coverage_statements, .. } = coverage_span; - let id = bcb.id(); + let CoverageSpan { span, bcb, coverage_statements, .. } = coverage_span; + let bcb_data = &self.basic_coverage_blocks[*bcb]; + let id = bcb_data.id(); + let leader_bb = bcb_data.leader_bb(); let mut sorted_coverage_statements = coverage_statements.clone(); sorted_coverage_statements.sort_unstable_by_key(|covstmt| match *covstmt { CoverageStatement::Statement(bb, _, index) => (bb, index), @@ -663,18 +919,19 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { }); let tooltip = sorted_coverage_statements .iter() - .map(|covstmt| covstmt.format(tcx, mir_body)) + .map(|covstmt| covstmt.format(tcx, self.mir_body)) .collect::>() .join("\n"); - span_viewables.push(SpanViewable { bb: *bb, span: *span, id, tooltip }); + span_viewables.push(SpanViewable { bb: leader_bb, span: *span, id, tooltip }); } span_viewables } - #[inline(always)] - fn bcb_from_coverage_span(&self, coverage_span: &CoverageSpan) -> &BasicCoverageBlock { - &self.basic_coverage_blocks()[coverage_span.bcb_leader_bb] - } +// TODO(richkadel): remove if not needed +// #[inline(always)] +// fn bcb_from_coverage_span(&self, coverage_span: &CoverageSpan) -> &BasicCoverageBlockData { +// &self.basic_coverage_blocks[coverage_span.bcb] +// } #[inline(always)] fn body_span(&self) -> Span { @@ -682,15 +939,14 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } // Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of - // the `BasicBlock`(s) in the given `BasicCoverageBlock`. One `CoverageSpan` is generated for + // the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One `CoverageSpan` is generated for // each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will // merge some `CoverageSpan`s, at which point a `CoverageSpan` may represent multiple // `Statement`s and/or `Terminator`s.) - fn extract_spans(&self, bcb: &'a BasicCoverageBlock) -> Vec { + fn extract_spans(&self, bcb: BasicCoverageBlock, bcb_data: &'a BasicCoverageBlockData) -> Vec { let body_span = self.body_span(); let mir_basic_blocks = self.mir_body.basic_blocks(); - bcb.blocks - .iter() + bcb_data.basic_blocks() .map(|bbref| { let bb = *bbref; let data = &mir_basic_blocks[bb]; @@ -717,7 +973,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { /// The basic steps are: /// /// 1. Extract an initial set of spans from the `Statement`s and `Terminator`s of each - /// `BasicCoverageBlock`. + /// `BasicCoverageBlockData`. /// 2. Sort the spans by span.lo() (starting position). Spans that start at the same position /// are sorted with longer spans before shorter spans; and equal spans are sorted /// (deterministically) based on "dominator" relationship (if any). @@ -735,8 +991,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { fn coverage_spans(&self) -> Vec { let mut initial_spans = Vec::::with_capacity(self.mir_body.basic_blocks().len() * 2); - for bcb in self.basic_coverage_blocks().iter() { - for coverage_span in self.extract_spans(bcb) { + for (bcb, bcb_data) in self.basic_coverage_blocks.iter_enumerated() { + for coverage_span in self.extract_spans(bcb, bcb_data) { initial_spans.push(coverage_span); } } @@ -757,14 +1013,14 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // dominators always come after the dominated equal spans). When later // comparing two spans in order, the first will either dominate the second, // or they will have no dominator relationship. - self.dominators().rank_partial_cmp(b.bcb_leader_bb, a.bcb_leader_bb) + self.bcb_dominators.rank_partial_cmp(b.bcb, a.bcb) } } else { // Sort hi() in reverse order so shorter spans are attempted after longer spans. - // This guarantees that, if a `prev` span overlaps, and is not equal to, a `curr` - // span, the prev span either extends further left of the curr span, or they - // start at the same position and the prev span extends further right of the end - // of the curr span. + // This guarantees that, if a `prev` span overlaps, and is not equal to, a + // `curr` span, the prev span either extends further left of the curr span, or + // they start at the same position and the prev span extends further right of + // the end of the curr span. b.span.hi().partial_cmp(&a.span.hi()) } } else { @@ -773,14 +1029,14 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { .unwrap() }); - let refinery = CoverageSpanRefinery::from_sorted_spans(initial_spans, self.dominators()); + let refinery = CoverageSpanRefinery::from_sorted_spans(initial_spans, &self.bcb_dominators); refinery.to_refined_spans() } } struct CoverageSpanRefinery<'a> { sorted_spans_iter: std::vec::IntoIter, - dominators: &'a Dominators, + bcb_dominators: &'a Dominators, some_curr: Option, curr_original_span: Span, some_prev: Option, @@ -792,7 +1048,7 @@ struct CoverageSpanRefinery<'a> { impl<'a> CoverageSpanRefinery<'a> { fn from_sorted_spans( sorted_spans: Vec, - dominators: &'a Dominators, + bcb_dominators: &'a Dominators, ) -> Self { let refined_spans = Vec::with_capacity(sorted_spans.len()); let mut sorted_spans_iter = sorted_spans.into_iter(); @@ -800,7 +1056,7 @@ impl<'a> CoverageSpanRefinery<'a> { let prev_original_span = prev.span; Self { sorted_spans_iter, - dominators, + bcb_dominators, refined_spans, some_curr: None, curr_original_span: Span::with_root_ctxt(BytePos(0), BytePos(0)), @@ -1019,9 +1275,9 @@ impl<'a> CoverageSpanRefinery<'a> { /// `pending_dups` so the new `curr` dup can be moved to `prev` for the next iteration. fn hold_pending_dups_unless_dominated(&mut self) { // equal coverage spans are ordered by dominators before dominated (if any) - debug_assert!(!self.prev().is_dominated_by(self.curr(), self.dominators)); + debug_assert!(!self.prev().is_dominated_by(self.curr(), self.bcb_dominators)); - if self.curr().is_dominated_by(&self.prev(), self.dominators) { + if self.curr().is_dominated_by(&self.prev(), self.bcb_dominators) { // If one span dominates the other, assocate the span with the dominator only. // // For example: From ea64143a30441313268bfe6943b4b284b4f65af2 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Mon, 5 Oct 2020 16:53:54 -0700 Subject: [PATCH 02/18] Removed most TODO comments and OBE lines Most of these TODO comments were reminders to remove commented out lines that were saved but replaced with an alternative implementation. Now that the new BCB graph is implemented and tests still pass, it should be safe to remove the older attempts. --- .../src/transform/instrument_coverage.rs | 97 ------------------- 1 file changed, 97 deletions(-) diff --git a/compiler/rustc_mir/src/transform/instrument_coverage.rs b/compiler/rustc_mir/src/transform/instrument_coverage.rs index a757aa3eeee9c..a81200ceb0b1f 100644 --- a/compiler/rustc_mir/src/transform/instrument_coverage.rs +++ b/compiler/rustc_mir/src/transform/instrument_coverage.rs @@ -4,12 +4,8 @@ use crate::util::spanview::{self, SpanViewable}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::graph::dominators::{self, Dominators}; -// TODO(richkadel): remove? -//use rustc_data_structures::graph::{self, GraphSuccessors, WithSuccessors}; use rustc_data_structures::graph::{self, GraphSuccessors}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -// TODO(richkadel): remove? -//use rustc_data_structures::sync::{Lrc, OnceCell}; use rustc_data_structures::sync::Lrc; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; @@ -29,7 +25,6 @@ use rustc_span::def_id::DefId; use rustc_span::source_map::original_sp; use rustc_span::{BytePos, CharPos, Pos, SourceFile, Span, Symbol, SyntaxContext}; -// TODO(richkadel): remove? use smallvec::SmallVec; use std::cmp::Ordering; @@ -198,30 +193,14 @@ impl BasicCoverageBlockData { } } -// TODO(richkadel): Remove? -// impl std::ops::Deref for BasicCoverageBlockData { -// type Target = [BasicBlock]; - -// fn deref(&self) -> &[BasicBlock] { -// self.basic_blocks.deref() -// } -// } - rustc_index::newtype_index! { /// A node in the [control-flow graph][CFG] of BasicCoverageBlocks. pub struct BasicCoverageBlock { -// TODO(richkadel): remove if not needed -// derive [HashStable] DEBUG_FORMAT = "bcb{}", -// TODO(richkadel): remove if not needed -// const START_BLOCK = 0, } } struct BasicCoverageBlocks { -// TODO(richkadel): remove if not needed -// struct BasicCoverageBlocks<'a, 'tcx> { -// mir_body: &'a mut mir::Body<'tcx>, bcbs: IndexVec, bb_to_bcb: IndexVec>, predecessors: IndexVec, @@ -229,9 +208,6 @@ struct BasicCoverageBlocks { } impl BasicCoverageBlocks { -// TODO(richkadel): remove if not needed -//impl<'a, 'tcx> BasicCoverageBlocks<'a, 'tcx> { -// pub fn from_mir(mir_body: &'a mut mir::Body<'tcx>) -> Self { pub fn from_mir(mir_body: &mir::Body<'tcx>) -> Self { let len = mir_body.basic_blocks().len(); let mut bcbs = IndexVec::with_capacity(len); @@ -395,7 +371,6 @@ impl BasicCoverageBlocks { }, bcbs.len()); Self { -// mir_body, bcbs, bb_to_bcb, predecessors, @@ -403,11 +378,6 @@ impl BasicCoverageBlocks { } } -// TODO(richkadel): remove if not needed - // pub fn iter(&self) -> impl Iterator { - // self.bcbs.iter() - // } - pub fn iter_enumerated(&self) -> impl Iterator { self.bcbs.iter_enumerated() } @@ -416,21 +386,6 @@ impl BasicCoverageBlocks { self.bb_to_bcb[bb].expect("bb is not in any bcb (pre-filtered, such as unwind paths perhaps?)") } -// TODO(richkadel): remove if not needed - // fn bcb_data_from_bb(&self, bb: BasicBlock) -> &BasicCoverageBlockData { - // &self.bcbs[self.bcb_from_bb(bb)] - // } - -// TODO(richkadel): remove if not needed - // fn mir_body(&self) -> &mir::Body<'tcx> { - // self.mir_body - // } - -// TODO(richkadel): remove if not needed - // fn mir_body_mut(&mut self) -> &mut mir::Body<'tcx> { - // self.mir_body - // } - fn add_basic_coverage_block(bcbs: &mut IndexVec, bb_to_bcb: &mut IndexVec>, basic_blocks: Vec) { let bcb = BasicCoverageBlock::from_usize(bcbs.len()); for &bb in basic_blocks.iter() { @@ -441,26 +396,16 @@ impl BasicCoverageBlocks { bcbs.push(bcb_data); } - // TODO(richkadel): remove? - // #[inline] - // pub fn predecessors(&self) -> impl std::ops::Deref + '_ { - // self.predecessors - // } - #[inline] pub fn compute_bcb_dominators(&self) -> Dominators { dominators::dominators(self) } } - // TODO(richkadel): remove? -//impl<'a, 'tcx> graph::DirectedGraph for BasicCoverageBlocks<'a, 'tcx> { impl graph::DirectedGraph for BasicCoverageBlocks { type Node = BasicCoverageBlock; } - // TODO(richkadel): remove? -//impl<'a, 'tcx> graph::WithNumNodes for BasicCoverageBlocks<'a, 'tcx> { impl graph::WithNumNodes for BasicCoverageBlocks { #[inline] fn num_nodes(&self) -> usize { @@ -468,8 +413,6 @@ impl graph::WithNumNodes for BasicCoverageBlocks { } } - // TODO(richkadel): remove? -//impl<'a, 'tcx> graph::WithStartNode for BasicCoverageBlocks<'a, 'tcx> { impl graph::WithStartNode for BasicCoverageBlocks { #[inline] fn start_node(&self) -> Self::Node { @@ -480,15 +423,9 @@ impl graph::WithStartNode for BasicCoverageBlocks { // `BasicBlock` `Predecessors` uses a `SmallVec` of length 4 because, "Typically 95%+ of basic // blocks have 4 or fewer predecessors." BasicCoverageBlocks should have the same or less. type BcbPredecessors = SmallVec<[BasicCoverageBlock; 4]>; -// TODO(richkadel): remove? -//type BcbPredecessors = IndexVec>; pub type BcbSuccessors<'a> = std::slice::Iter<'a, BasicCoverageBlock>; - // TODO(richkadel): remove? -//pub type BcbSuccessors = std::slice::Iter<'_, BasicCoverageBlock>; - // TODO(richkadel): remove? -//impl<'a, 'tcx> graph::WithSuccessors for BasicCoverageBlocks<'a, 'tcx> { impl graph::WithSuccessors for BasicCoverageBlocks { #[inline] fn successors(&self, node: Self::Node) -> >::Iter { @@ -496,28 +433,16 @@ impl graph::WithSuccessors for BasicCoverageBlocks { } } - // TODO(richkadel): remove? -//impl<'a, 'tcx, 'b> graph::GraphSuccessors<'b> for BasicCoverageBlocks<'a, 'tcx> { -//impl<'b> graph::GraphSuccessors<'b> for BasicCoverageBlocks { impl<'a> graph::GraphSuccessors<'a> for BasicCoverageBlocks { type Item = BasicCoverageBlock; - // TODO(richkadel): remove? - //type Iter = std::iter::Cloned>; - //type Iter = std::iter::Cloned; type Iter = std::iter::Cloned>; } - // TODO(richkadel): remove? -//impl graph::GraphPredecessors<'graph> for BasicCoverageBlocks<'a, 'tcx> { impl graph::GraphPredecessors<'graph> for BasicCoverageBlocks { type Item = BasicCoverageBlock; - // TODO(richkadel): remove? - //type Iter = smallvec::IntoIter; type Iter = smallvec::IntoIter<[BasicCoverageBlock; 4]>; } - // TODO(richkadel): remove? -//impl graph::WithPredecessors for BasicCoverageBlocks<'a, 'tcx> { impl graph::WithPredecessors for BasicCoverageBlocks { #[inline] fn predecessors(&self, node: Self::Node) -> >::Iter { @@ -525,8 +450,6 @@ impl graph::WithPredecessors for BasicCoverageBlocks { } } - // TODO(richkadel): remove? -//impl<'a, 'tcx> Index for BasicCoverageBlocks<'a, 'tcx> { impl Index for BasicCoverageBlocks { type Output = BasicCoverageBlockData; @@ -536,8 +459,6 @@ impl Index for BasicCoverageBlocks { } } - // TODO(richkadel): remove? -//impl<'a, 'tcx> IndexMut for BasicCoverageBlocks<'a, 'tcx> { impl IndexMut for BasicCoverageBlocks { #[inline] fn index_mut(&mut self, index: BasicCoverageBlock) -> &mut BasicCoverageBlockData { @@ -735,8 +656,6 @@ struct Instrumentor<'a, 'tcx> { mir_body: &'a mut mir::Body<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx>, bcb_dominators: Dominators, -//TODO(richkadel): remove? -// basic_coverage_blocks: BasicCoverageBlocks<'a, 'tcx>, basic_coverage_blocks: BasicCoverageBlocks, function_source_hash: Option, next_counter_id: u32, @@ -765,14 +684,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } } - // fn mir_body(&self) -> &mir::Body<'tcx> { - // self.basic_coverage_blocks.mir_body() - // } - - // fn mir_body_mut(&mut self) -> &mut mir::Body<'tcx> { - // self.basic_coverage_blocks.mir_body_mut() - // } - /// Counter IDs start from one and go up. fn next_counter(&mut self) -> CounterValueReference { assert!(self.next_counter_id < u32::MAX - self.num_expressions); @@ -892,8 +803,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { let code_region = make_code_region(file_name, source_file, span); debug!(" injecting statement {:?} covering {:?}", coverage_kind, code_region); -// TODO(richkadel): remove? -// let data = &mut self.mir_body_mut()[block]; let data = &mut self.mir_body[block]; let source_info = data.terminator().source_info; let statement = Statement { @@ -927,12 +836,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { span_viewables } -// TODO(richkadel): remove if not needed -// #[inline(always)] -// fn bcb_from_coverage_span(&self, coverage_span: &CoverageSpan) -> &BasicCoverageBlockData { -// &self.basic_coverage_blocks[coverage_span.bcb] -// } - #[inline(always)] fn body_span(&self) -> Span { self.hir_body.value.span From dcb4b24b59beedf80fee61c254d9e11f7de1ffa9 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Wed, 7 Oct 2020 21:47:44 -0700 Subject: [PATCH 03/18] Expression optimization is nearly working I need to change how I determine if a target from a SwitchInt exits the loop or not, but this almost worked (and did successfully compile and create the right coverage). It just didn't select the right targets for the optimized expressions. --- .../src/coverageinfo/mod.rs | 33 +- .../rustc_codegen_ssa/src/coverageinfo/map.rs | 30 +- .../rustc_codegen_ssa/src/mir/coverageinfo.rs | 14 +- .../src/traits/coverageinfo.rs | 8 +- compiler/rustc_middle/src/mir/mod.rs | 2 +- .../src/transform/instrument_coverage.rs | 986 ++++++++++++------ 6 files changed, 729 insertions(+), 344 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 2bd37bf9c4f9c..7a7e8276c8763 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -27,7 +27,7 @@ const COVMAP_VAR_ALIGN_BYTES: usize = 8; /// A context object for maintaining all state needed by the coverageinfo module. pub struct CrateCoverageContext<'tcx> { - // Coverage region data for each instrumented function identified by DefId. + // Coverage data for each instrumented function identified by DefId. pub(crate) function_coverage_map: RefCell, FunctionCoverage>>, } @@ -58,7 +58,7 @@ impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { unsafe { llvm::LLVMRustCoverageCreatePGOFuncNameVar(llfn, mangled_fn_name.as_ptr()) } } - fn add_counter_region( + fn add_coverage_counter( &mut self, instance: Instance<'tcx>, function_source_hash: u64, @@ -66,45 +66,42 @@ impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { region: CodeRegion, ) { debug!( - "adding counter to coverage_regions: instance={:?}, function_source_hash={}, id={:?}, \ + "adding counter to coverage_map: instance={:?}, function_source_hash={}, id={:?}, \ at {:?}", instance, function_source_hash, id, region, ); - let mut coverage_regions = self.coverage_context().function_coverage_map.borrow_mut(); - coverage_regions + let mut coverage_map = self.coverage_context().function_coverage_map.borrow_mut(); + coverage_map .entry(instance) .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) .add_counter(function_source_hash, id, region); } - fn add_counter_expression_region( + fn add_coverage_counter_expression( &mut self, instance: Instance<'tcx>, id: InjectedExpressionIndex, lhs: ExpressionOperandId, op: Op, rhs: ExpressionOperandId, - region: CodeRegion, + region: Option, ) { debug!( - "adding counter expression to coverage_regions: instance={:?}, id={:?}, {:?} {:?} {:?}, \ - at {:?}", + "adding counter expression to coverage_map: instance={:?}, id={:?}, {:?} {:?} {:?}; \ + region: {:?}", instance, id, lhs, op, rhs, region, ); - let mut coverage_regions = self.coverage_context().function_coverage_map.borrow_mut(); - coverage_regions + let mut coverage_map = self.coverage_context().function_coverage_map.borrow_mut(); + coverage_map .entry(instance) .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) .add_counter_expression(id, lhs, op, rhs, region); } - fn add_unreachable_region(&mut self, instance: Instance<'tcx>, region: CodeRegion) { - debug!( - "adding unreachable code to coverage_regions: instance={:?}, at {:?}", - instance, region, - ); - let mut coverage_regions = self.coverage_context().function_coverage_map.borrow_mut(); - coverage_regions + fn add_coverage_unreachable(&mut self, instance: Instance<'tcx>, region: CodeRegion) { + debug!("adding unreachable code to coverage_map: instance={:?}, at {:?}", instance, region,); + let mut coverage_map = self.coverage_context().function_coverage_map.borrow_mut(); + coverage_map .entry(instance) .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) .add_unreachable_region(region); diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs index d8bde8ee70533..0ee2ecfc1e809 100644 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs @@ -9,11 +9,11 @@ use rustc_middle::ty::Instance; use rustc_middle::ty::TyCtxt; #[derive(Clone, Debug)] -pub struct ExpressionRegion { +pub struct Expression { lhs: ExpressionOperandId, op: Op, rhs: ExpressionOperandId, - region: CodeRegion, + region: Option, } /// Collects all of the coverage regions associated with (a) injected counters, (b) counter @@ -31,7 +31,7 @@ pub struct ExpressionRegion { pub struct FunctionCoverage { source_hash: u64, counters: IndexVec>, - expressions: IndexVec>, + expressions: IndexVec>, unreachable_regions: Vec, } @@ -78,11 +78,11 @@ impl FunctionCoverage { lhs: ExpressionOperandId, op: Op, rhs: ExpressionOperandId, - region: CodeRegion, + region: Option, ) { let expression_index = self.expression_index(u32::from(expression_id)); self.expressions[expression_index] - .replace(ExpressionRegion { lhs, op, rhs, region }) + .replace(Expression { lhs, op, rhs, region }) .expect_none("add_counter_expression called with duplicate `id_descending_from_max`"); } @@ -135,9 +135,9 @@ impl FunctionCoverage { // will be set, and after that, `new_indexes` will only be accessed using those same // indexes. - // Note that an `ExpressionRegion`s at any given index can include other expressions as + // Note that an `Expression`s at any given index can include other expressions as // operands, but expression operands can only come from the subset of expressions having - // `expression_index`s lower than the referencing `ExpressionRegion`. Therefore, it is + // `expression_index`s lower than the referencing `Expression`. Therefore, it is // reasonable to look up the new index of an expression operand while the `new_indexes` // vector is only complete up to the current `ExpressionIndex`. let id_to_counter = @@ -162,15 +162,15 @@ impl FunctionCoverage { } }; - for (original_index, expression_region) in + for (original_index, expression) in self.expressions.iter_enumerated().filter_map(|(original_index, entry)| { // Option::map() will return None to filter out missing expressions. This may happen // if, for example, a MIR-instrumented expression is removed during an optimization. - entry.as_ref().map(|region| (original_index, region)) + entry.as_ref().map(|expression| (original_index, expression)) }) { - let region = &expression_region.region; - let ExpressionRegion { lhs, op, rhs, .. } = *expression_region; + let optional_region = &expression.region; + let Expression { lhs, op, rhs, .. } = *expression; if let Some(Some((lhs_counter, rhs_counter))) = id_to_counter(&new_indexes, lhs).map(|lhs_counter| { @@ -190,12 +190,14 @@ impl FunctionCoverage { rhs_counter, ); debug!( - "Adding expression {:?} = {:?} at {:?}", - mapped_expression_index, expression, region + "Adding expression {:?} = {:?}, region: {:?}", + mapped_expression_index, expression, optional_region ); counter_expressions.push(expression); new_indexes[original_index] = mapped_expression_index; - expression_regions.push((Counter::expression(mapped_expression_index), region)); + if let Some(region) = optional_region { + expression_regions.push((Counter::expression(mapped_expression_index), region)); + } } } (counter_expressions, expression_regions.into_iter()) diff --git a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs index a2ad27b925c34..17fa4c8f3824b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs @@ -10,7 +10,12 @@ 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 } => { - bx.add_counter_region(self.instance, function_source_hash, id, code_region); + bx.add_coverage_counter( + self.instance, + function_source_hash, + id, + code_region.expect("counters always have code regions"), + ); let coverageinfo = bx.tcx().coverageinfo(self.instance.def_id()); @@ -25,10 +30,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.instrprof_increment(fn_name, hash, num_counters, id); } CoverageKind::Expression { id, lhs, op, rhs } => { - bx.add_counter_expression_region(self.instance, id, lhs, op, rhs, code_region); + bx.add_coverage_counter_expression(self.instance, id, lhs, op, rhs, code_region); } CoverageKind::Unreachable => { - bx.add_unreachable_region(self.instance, code_region); + bx.add_coverage_unreachable( + self.instance, + code_region.expect("unreachable regions always have code regions"), + ); } } } diff --git a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs index b74e4e459016f..414c1be98261b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs @@ -9,7 +9,7 @@ pub trait CoverageInfoMethods: BackendTypes { pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { fn create_pgo_func_name_var(&self, instance: Instance<'tcx>) -> Self::Value; - fn add_counter_region( + fn add_coverage_counter( &mut self, instance: Instance<'tcx>, function_source_hash: u64, @@ -17,15 +17,15 @@ pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { region: CodeRegion, ); - fn add_counter_expression_region( + fn add_coverage_counter_expression( &mut self, instance: Instance<'tcx>, id: InjectedExpressionIndex, lhs: ExpressionOperandId, op: Op, rhs: ExpressionOperandId, - region: CodeRegion, + region: Option, ); - fn add_unreachable_region(&mut self, instance: Instance<'tcx>, region: CodeRegion); + fn add_coverage_unreachable(&mut self, instance: Instance<'tcx>, region: CodeRegion); } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index b38efedbf607e..5de64d3b1c1fb 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1626,7 +1626,7 @@ impl Debug for Statement<'_> { #[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, HashStable, TypeFoldable)] pub struct Coverage { pub kind: CoverageKind, - pub code_region: CodeRegion, + pub code_region: Option, } /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_mir/src/transform/instrument_coverage.rs b/compiler/rustc_mir/src/transform/instrument_coverage.rs index a81200ceb0b1f..3e0dab7ece437 100644 --- a/compiler/rustc_mir/src/transform/instrument_coverage.rs +++ b/compiler/rustc_mir/src/transform/instrument_coverage.rs @@ -4,7 +4,7 @@ use crate::util::spanview::{self, SpanViewable}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::graph::dominators::{self, Dominators}; -use rustc_data_structures::graph::{self, GraphSuccessors}; +use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; use rustc_index::bit_set::BitSet; @@ -90,11 +90,7 @@ fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo } impl<'tcx> MirPass<'tcx> for InstrumentCoverage { - fn run_pass( - &self, - tcx: TyCtxt<'tcx>, - mir_body: &mut mir::Body<'tcx>, - ) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) { let mir_source = mir_body.source; // If the InstrumentCoverage pass is called on promoted MIRs, skip them. @@ -112,7 +108,11 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage { // Only instrument functions, methods, and closures (not constants since they are evaluated // at compile time by Miri). - // FIXME(#73156): Handle source code coverage in const eval + // FIXME(#73156): Handle source code coverage in const eval, but note, if and when const + // expressions get coverage spans, we will probably have to "carve out" space for const + // expressions from coverage spans in enclosing MIR's, like we do for closures. (That might + // be tricky if const expressions have no corresponding statements in the enclosing MIR. + // Closures are carved out by their initial `Assign` statement.) if !is_fn_like { trace!("InstrumentCoverage skipped for {:?} (not an FnLikeNode)", mir_source.def_id()); return; @@ -127,18 +127,17 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage { } } -/// A BasicCoverageBlockData (BCB) represents the maximal-length sequence of CFG (MIR) BasicBlocks -/// without conditional branches. +/// A BasicCoverageBlockData (BCB) represents the maximal-length sequence of MIR BasicBlocks without +/// conditional branches, and form a new, simplified, coverage-specific Control Flow Graph, without +/// altering the original MIR CFG. /// -/// The BCB allows coverage analysis to be performed on a simplified projection of the underlying -/// MIR CFG, without altering the original CFG. Note that running the MIR `SimplifyCfg` transform, -/// is not sufficient, and therefore not necessary, since the BCB-based CFG projection is a more -/// aggressive simplification. For example: +/// Note that running the MIR `SimplifyCfg` transform is not sufficient (and therefore not +/// necessary). The BCB-based CFG is a more aggressive simplification. For example: /// -/// * The BCB CFG projection ignores (trims) branches not relevant to coverage, such as unwind- -/// related code that is injected by the Rust compiler but has no physical source code to -/// count. This also means a BasicBlock with a `Call` terminator can be merged into its -/// primary successor target block, in the same BCB. +/// * The BCB CFG ignores (trims) branches not relevant to coverage, such as unwind-related code, +/// that is injected by the Rust compiler but has no physical source code to count. This also +/// means a BasicBlock with a `Call` terminator can be merged into its primary successor target +/// block, in the same BCB. /// * Some BasicBlock terminators support Rust-specific concerns--like borrow-checking--that are /// not relevant to coverage analysis. `FalseUnwind`, for example, can be treated the same as /// a `Goto`, and merged with its successor into the same BCB. @@ -148,37 +147,56 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage { /// disjoint `CoverageSpan`s in a BCB can also be counted by `CounterExpression` (by adding `ZERO` /// to the BCB's primary counter or expression). /// -/// Dominator/dominated relationships (which are fundamental to the coverage analysis algorithm) -/// between two BCBs can be computed using the `mir::Body` `dominators()` with any `BasicBlock` -/// member of each BCB. (For consistency, BCB's use the first `BasicBlock`, also referred to as the -/// `bcb` `leader_bb`.) -/// -/// The BCB CFG projection is critical to simplifying the coverage analysis by ensuring graph -/// path-based queries (`is_dominated_by()`, `predecessors`, `successors`, etc.) have branch -/// (control flow) significance. +/// The BCB CFG is critical to simplifying the coverage analysis by ensuring graph path-based +/// queries (`is_dominated_by()`, `predecessors`, `successors`, etc.) have branch (control flow) +/// significance. #[derive(Debug, Clone)] struct BasicCoverageBlockData { basic_blocks: Vec, + counter_kind: Option, } impl BasicCoverageBlockData { pub fn from(basic_blocks: Vec) -> Self { assert!(basic_blocks.len() > 0); - Self { - basic_blocks, - } + Self { basic_blocks, counter_kind: None } } + #[inline(always)] + pub fn basic_blocks(&self) -> std::slice::Iter<'_, BasicBlock> { + self.basic_blocks.iter() + } + + #[inline(always)] pub fn leader_bb(&self) -> BasicBlock { self.basic_blocks[0] } + #[inline(always)] pub fn last_bb(&self) -> BasicBlock { *self.basic_blocks.last().unwrap() } - pub fn basic_blocks(&self) -> std::slice::Iter<'_, BasicBlock> { - self.basic_blocks.iter() + #[inline(always)] + pub fn terminator<'a, 'tcx>(&self, mir_body: &'a mir::Body<'tcx>) -> &'a TerminatorKind<'tcx> { + &mir_body[self.last_bb()].terminator().kind + } + + #[inline(always)] + pub fn set_counter(&mut self, counter_kind: CoverageKind) { + self.counter_kind + .replace(counter_kind) + .expect_none("attempt to set a BasicCoverageBlock coverage counter more than once"); + } + + #[inline(always)] + pub fn counter(&self) -> Option<&CoverageKind> { + self.counter_kind.as_ref() + } + + #[inline(always)] + pub fn take_counter(&mut self) -> Option { + self.counter_kind.take() } pub fn id(&self) -> String { @@ -203,94 +221,112 @@ rustc_index::newtype_index! { struct BasicCoverageBlocks { bcbs: IndexVec, bb_to_bcb: IndexVec>, + successors: IndexVec>, predecessors: IndexVec, - successors: IndexVec>, } impl BasicCoverageBlocks { pub fn from_mir(mir_body: &mir::Body<'tcx>) -> Self { - let len = mir_body.basic_blocks().len(); + let (bcbs, bb_to_bcb) = Self::compute_basic_coverage_blocks(mir_body); + + // Pre-transform MIR `BasicBlock` successors and predecessors into the BasicCoverageBlock + // equivalents. Note that since the BasicCoverageBlock graph has been fully simplified, the + // each predecessor of a BCB leader_bb should be in a unique BCB, and each successor of a + // BCB last_bb should bin in its own unique BCB. Therefore, collecting the BCBs using + // `bb_to_bcb` should work without requiring a deduplication step. + + let successors = IndexVec::from_fn_n( + |bcb| { + let bcb_data = &bcbs[bcb]; + let bcb_successors = bcb_data + .terminator(mir_body) + .successors() + .filter_map(|&successor_bb| bb_to_bcb[successor_bb]) + .collect::>(); + debug_assert!({ + let mut sorted = bcb_successors.clone(); + sorted.sort_unstable(); + let initial_len = sorted.len(); + sorted.dedup(); + sorted.len() == initial_len + }); + bcb_successors + }, + bcbs.len(), + ); + + let predecessors = IndexVec::from_fn_n( + |bcb| { + let bcb_data = &bcbs[bcb]; + let bcb_predecessors = mir_body.predecessors()[bcb_data.leader_bb()] + .iter() + .filter_map(|&predecessor_bb| bb_to_bcb[predecessor_bb]) + .collect::(); + debug_assert!({ + let mut sorted = bcb_predecessors.clone(); + sorted.sort_unstable(); + let initial_len = sorted.len(); + sorted.dedup(); + sorted.len() == initial_len + }); + bcb_predecessors + }, + bcbs.len(), + ); + + Self { bcbs, bb_to_bcb, successors, predecessors } + } + + fn compute_basic_coverage_blocks( + mir_body: &mir::Body<'tcx>, + ) -> ( + IndexVec, + IndexVec>, + ) { + let len = mir_body.num_nodes(); let mut bcbs = IndexVec::with_capacity(len); let mut bb_to_bcb = IndexVec::from_elem_n(None, len); - // Traverse the CFG but ignore anything following an `unwind` - let cfg_without_unwind = ShortCircuitPreorder::new(mir_body, |term_kind| { + + // Walk the MIR CFG using a Preorder traversal, which starts from `START_BLOCK` and follows + // each block terminator's `successors()`. Coverage spans must map to actual source code, + // so compiler generated blocks and paths can be ignored. To that end, the CFG traversal + // intentionally omits unwind paths. + let mir_cfg_without_unwind = ShortCircuitPreorder::new(mir_body, |term_kind| { let mut successors = term_kind.successors(); match &term_kind { // SwitchInt successors are never unwind, and all of them should be traversed. - - // NOTE: TerminatorKind::FalseEdge targets from SwitchInt don't appear to be - // helpful in identifying unreachable code. I did test the theory, but the following - // changes were not beneficial. (I assumed that replacing some constants with - // non-deterministic variables might effect which blocks were targeted by a - // `FalseEdge` `imaginary_target`. It did not.) - // - // Also note that, if there is a way to identify BasicBlocks that are part of the - // MIR CFG, but not actually reachable, here are some other things to consider: - // - // Injecting unreachable code regions will probably require computing the set - // difference between the basic blocks found without filtering out unreachable - // blocks, and the basic blocks found with the filter; then computing the - // `CoverageSpans` without the filter; and then injecting `Counter`s or - // `CounterExpression`s for blocks that are not unreachable, or injecting - // `Unreachable` code regions otherwise. This seems straightforward, but not - // trivial. - // - // Alternatively, we might instead want to leave the unreachable blocks in - // (bypass the filter here), and inject the counters. This will result in counter - // values of zero (0) for unreachable code (and, notably, the code will be displayed - // with a red background by `llvm-cov show`). - // - // TerminatorKind::SwitchInt { .. } => { - // let some_imaginary_target = successors.clone().find_map(|&successor| { - // let term = mir_body.basic_blocks()[successor].terminator(); - // if let TerminatorKind::FalseEdge { imaginary_target, .. } = term.kind { - // if mir_body.predecessors()[imaginary_target].len() == 1 { - // return Some(imaginary_target); - // } - // } - // None - // }); - // if let Some(imaginary_target) = some_imaginary_target { - // box successors.filter(move |&&successor| successor != imaginary_target) - // } else { - // box successors - // } - // } - // - // Note this also required changing the closure signature for the - // `ShortCurcuitPreorder` to: - // - // F: Fn(&'tcx TerminatorKind<'tcx>) -> Box + 'a>, TerminatorKind::SwitchInt { .. } => successors, - - // For all other kinds, return only the first successor, if any, and ignore unwinds + // For all other kinds, return only the first successor, if any, and ignore unwinds. + // NOTE: `chain(&[])` is required to coerce the `option::iter` (from + // `next().into_iter()`) into the `mir::Successors` aliased type. _ => successors.next().into_iter().chain(&[]), } }); - // Walk the CFG using a Preorder traversal, which starts from `START_BLOCK` and follows - // each block terminator's `successors()`. Coverage spans must map to actual source code, - // so compiler generated blocks and paths can be ignored. To that end the CFG traversal - // intentionally omits unwind paths. let mut basic_blocks = Vec::new(); - for (bb, data) in cfg_without_unwind { + for (bb, data) in mir_cfg_without_unwind { if let Some(last) = basic_blocks.last() { let predecessors = &mir_body.predecessors()[bb]; if predecessors.len() > 1 || !predecessors.contains(last) { // The `bb` has more than one _incoming_ edge, and should start its own - // `BasicCoverageBlockData`. (Note, the `basic_blocks` vector does not yet include - // `bb`; it contains a sequence of one or more sequential basic_blocks with no - // intermediate branches in or out. Save these as a new `BasicCoverageBlockData` - // before starting the new one.) - Self::add_basic_coverage_block(&mut bcbs, &mut bb_to_bcb, basic_blocks.split_off(0)); - debug!( - " because {}", - if predecessors.len() > 1 { - "predecessors.len() > 1".to_owned() - } else { - format!("bb {} is not in precessors: {:?}", bb.index(), predecessors) - } + // `BasicCoverageBlockData`. (Note, the `basic_blocks` vector does not yet + // include `bb`; it contains a sequence of one or more sequential basic_blocks + // with no intermediate branches in or out. Save these as a new + // `BasicCoverageBlockData` before starting the new one.) + Self::add_basic_coverage_block( + &mut bcbs, + &mut bb_to_bcb, + basic_blocks.split_off(0), ); + // TODO(richkadel): uncomment debug! + // debug!( + // " because {}", + // if predecessors.len() > 1 { + // "predecessors.len() > 1".to_owned() + // } else { + // format!("bb {} is not in precessors: {:?}", bb.index(), predecessors) + // } + // ); } } basic_blocks.push(bb); @@ -299,21 +335,46 @@ impl BasicCoverageBlocks { match term.kind { TerminatorKind::Return { .. } +// TODO(richkadel): Do we handle Abort like Return? The program doesn't continue +// normally. It's like a failed assert (I assume). | TerminatorKind::Abort +// TODO(richkadel): I think Assert should be handled like falseUnwind. +// It's just a goto if we assume it does not fail the assert, and if it +// does fail, the unwind caused by failure is hidden code not covered +// in coverage??? +// +// BUT if we take it out, then we can't count code up until the assert. +// +// (That may be OK, not sure. Maybe not.). +// +// How am I handling the try "?" on functions that return Result? + +// TODO(richkadel): comment out? | TerminatorKind::Assert { .. } + +// TODO(richkadel): And I don't know what do do with Yield | TerminatorKind::Yield { .. } + // FIXME(richkadel): Add coverage test for TerminatorKind::Yield and/or `yield` + // keyword (see "generators" unstable feature). + // FIXME(richkadel): Add tests with async and threading. + | TerminatorKind::SwitchInt { .. } => { // The `bb` has more than one _outgoing_ edge, or exits the function. Save the // current sequence of `basic_blocks` gathered to this point, as a new // `BasicCoverageBlockData`. - Self::add_basic_coverage_block(&mut bcbs, &mut bb_to_bcb, basic_blocks.split_off(0)); - debug!(" because term.kind = {:?}", term.kind); + Self::add_basic_coverage_block( + &mut bcbs, + &mut bb_to_bcb, + basic_blocks.split_off(0), + ); + // TODO(richkadel): uncomment debug! + // debug!(" because term.kind = {:?}", term.kind); // Note that this condition is based on `TerminatorKind`, even though it // theoretically boils down to `successors().len() != 1`; that is, either zero // (e.g., `Return`, `Abort`) or multiple successors (e.g., `SwitchInt`), but - // since the Coverage graph (the BCB CFG projection) ignores things like unwind - // branches (which exist in the `Terminator`s `successors()` list) checking the - // number of successors won't work. + // since the BCB CFG ignores things like unwind branches (which exist in the + // `Terminator`s `successors()` list) checking the number of successors won't + // work. } TerminatorKind::Goto { .. } | TerminatorKind::Resume @@ -331,77 +392,63 @@ impl BasicCoverageBlocks { if !basic_blocks.is_empty() { // process any remaining basic_blocks into a final `BasicCoverageBlockData` Self::add_basic_coverage_block(&mut bcbs, &mut bb_to_bcb, basic_blocks.split_off(0)); - debug!(" because the end of the CFG was reached while traversing"); - } - - let basic_blocks = &mir_body.basic_blocks(); - let bb_predecessors = &mir_body.predecessors(); - - // Pre-transform MIR `BasicBlock` successors and predecessors into the BasicCoverageBlock - // equivalents. Note that since the BasicCoverageBlock graph has been fully simplified, the - // each predecessor of a BCB leader_bb should be in a unique BCB, and each successor of a - // BCB last_bb should bin in its own unique BCB. Therefore, collecting the BCBs using - // `bb_to_bcb` should work without requiring a deduplication step. - - let successors = IndexVec::from_fn_n(|bcb| { - let bcb_data = &bcbs[bcb]; - let bb_data = &basic_blocks[bcb_data.last_bb()]; - let bcb_successors = bb_data.terminator().successors().filter_map(|&successor_bb| bb_to_bcb[successor_bb]).collect::>(); - debug_assert!({ - let mut sorted = bcb_successors.clone(); - sorted.sort_unstable(); - let initial_len = sorted.len(); - sorted.dedup(); - sorted.len() == initial_len - }); - bcb_successors - }, bcbs.len()); - - let predecessors = IndexVec::from_fn_n(|bcb| { - let bcb_data = &bcbs[bcb]; - let bcb_predecessors = bb_predecessors[bcb_data.leader_bb()].iter().filter_map(|&predecessor_bb| bb_to_bcb[predecessor_bb]).collect::(); - debug_assert!({ - let mut sorted = bcb_predecessors.clone(); - sorted.sort_unstable(); - let initial_len = sorted.len(); - sorted.dedup(); - sorted.len() == initial_len - }); - bcb_predecessors - }, bcbs.len()); - - Self { - bcbs, - bb_to_bcb, - predecessors, - successors, + // TODO(richkadel): uncomment debug! + // debug!(" because the end of the MIR CFG was reached while traversing"); } - } - pub fn iter_enumerated(&self) -> impl Iterator { - self.bcbs.iter_enumerated() + (bcbs, bb_to_bcb) } - fn bcb_from_bb(&self, bb: BasicBlock) -> BasicCoverageBlock { - self.bb_to_bcb[bb].expect("bb is not in any bcb (pre-filtered, such as unwind paths perhaps?)") - } - - fn add_basic_coverage_block(bcbs: &mut IndexVec, bb_to_bcb: &mut IndexVec>, basic_blocks: Vec) { + fn add_basic_coverage_block( + bcbs: &mut IndexVec, + bb_to_bcb: &mut IndexVec>, + basic_blocks: Vec, + ) { let bcb = BasicCoverageBlock::from_usize(bcbs.len()); for &bb in basic_blocks.iter() { bb_to_bcb[bb] = Some(bcb); } let bcb_data = BasicCoverageBlockData::from(basic_blocks); - debug!("adding bcb{}: {:?}", bcb.index(), bcb_data); + // TODO(richkadel): uncomment debug! + // debug!("adding bcb{}: {:?}", bcb.index(), bcb_data); bcbs.push(bcb_data); } - #[inline] + #[inline(always)] + pub fn iter_enumerated( + &self, + ) -> impl Iterator { + self.bcbs.iter_enumerated() + } + + #[inline(always)] + pub fn bcb_from_bb(&self, bb: BasicBlock) -> BasicCoverageBlock { + self.bb_to_bcb[bb] + .expect("bb is not in any bcb (pre-filtered, such as unwind paths perhaps?)") + } + + #[inline(always)] pub fn compute_bcb_dominators(&self) -> Dominators { dominators::dominators(self) } } +impl Index for BasicCoverageBlocks { + type Output = BasicCoverageBlockData; + + #[inline] + fn index(&self, index: BasicCoverageBlock) -> &BasicCoverageBlockData { + &self.bcbs[index] + } +} + +impl IndexMut for BasicCoverageBlocks { + #[inline] + fn index_mut(&mut self, index: BasicCoverageBlock) -> &mut BasicCoverageBlockData { + &mut self.bcbs[index] + } +} + impl graph::DirectedGraph for BasicCoverageBlocks { type Node = BasicCoverageBlock; } @@ -420,11 +467,7 @@ impl graph::WithStartNode for BasicCoverageBlocks { } } -// `BasicBlock` `Predecessors` uses a `SmallVec` of length 4 because, "Typically 95%+ of basic -// blocks have 4 or fewer predecessors." BasicCoverageBlocks should have the same or less. -type BcbPredecessors = SmallVec<[BasicCoverageBlock; 4]>; - -pub type BcbSuccessors<'a> = std::slice::Iter<'a, BasicCoverageBlock>; +type BcbSuccessors<'a> = std::slice::Iter<'a, BasicCoverageBlock>; impl graph::WithSuccessors for BasicCoverageBlocks { #[inline] @@ -438,6 +481,10 @@ impl<'a> graph::GraphSuccessors<'a> for BasicCoverageBlocks { type Iter = std::iter::Cloned>; } +// `BasicBlock` `Predecessors` uses a `SmallVec` of length 4 because, "Typically 95%+ of basic +// blocks have 4 or fewer predecessors." BasicCoverageBlocks should have the same or less. +type BcbPredecessors = SmallVec<[BasicCoverageBlock; 4]>; + impl graph::GraphPredecessors<'graph> for BasicCoverageBlocks { type Item = BasicCoverageBlock; type Iter = smallvec::IntoIter<[BasicCoverageBlock; 4]>; @@ -450,60 +497,6 @@ impl graph::WithPredecessors for BasicCoverageBlocks { } } -impl Index for BasicCoverageBlocks { - type Output = BasicCoverageBlockData; - - #[inline] - fn index(&self, index: BasicCoverageBlock) -> &BasicCoverageBlockData { - &self.bcbs[index] - } -} - -impl IndexMut for BasicCoverageBlocks { - #[inline] - fn index_mut(&mut self, index: BasicCoverageBlock) -> &mut BasicCoverageBlockData { - &mut self.bcbs[index] - } -} - - - - // TODO(richkadel): For any node, N, and one of its successors, H (so N -> H), if (_also_) - // N is_dominated_by H, then N -> H is a backedge. That is, we've identified that N -> H is - // at least _one_ of possibly multiple arcs that loop back to the start of the loop with - // "header" H, and this also means we've identified a loop, that has "header" H. - // - // H dominates everything inside the loop. - // - // So a SwitchInt target in a BasicBlock that is_dominated_by H and has a branch target to a - // BasicBlock that is: - // * not H, and ... (what if the SwitchInt branch target _is_ H? `continue`? is this a - // candidate for a middle or optional priority for getting a Counter?) - // * not is_dominated_by H - // is a branch that jumps outside the loop, and should get an actual Counter, most likely - // - // Or perhaps conversely, a SwitchInt dominated by H with a branch that has a target that - // ALSO is dominated by H should get a CounterExpression. - // - // - // So I need to identify all of the "H"'s, by identifying all of the backedges. - // - // If I have multiple H's (multiple loops), how do I decide which loop to compare a branch - // target (by dominator) to? - // - // Can I assume the traversal order is helpful here? I.e., the "last" encountered loop - // header is the (only?) one to compare to? (Probably not only... I don't see how that would - // work for nested loops.) - // - // What about multiple loops in sequence? - // - // - // What about nexted loops and jumping out of one or more of them at a time? - - - - - #[derive(Debug, Copy, Clone)] enum CoverageStatement { Statement(BasicBlock, Span, usize), @@ -514,7 +507,7 @@ impl CoverageStatement { pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String { match *self { Self::Statement(bb, span, stmt_index) => { - let stmt = &mir_body.basic_blocks()[bb].statements[stmt_index]; + let stmt = &mir_body[bb].statements[stmt_index]; format!( "{}: @{}[{}]: {:?}", spanview::source_range_no_file(tcx, &span), @@ -524,7 +517,7 @@ impl CoverageStatement { ) } Self::Terminator(bb, span) => { - let term = mir_body.basic_blocks()[bb].terminator(); + let term = mir_body[bb].terminator(); format!( "{}: @{}.{}: {:?}", spanview::source_range_no_file(tcx, &span), @@ -632,6 +625,7 @@ impl CoverageSpan { } } + #[inline] pub fn is_dominated_by( &self, other: &CoverageSpan, @@ -641,15 +635,32 @@ impl CoverageSpan { bcb_dominators.is_dominated_by(self.bcb, other.bcb) } + #[inline] pub fn is_mergeable(&self, other: &Self) -> bool { self.is_in_same_bcb(other) && !(self.is_closure || other.is_closure) } + #[inline] pub fn is_in_same_bcb(&self, other: &Self) -> bool { self.bcb == other.bcb } } +/// Maintains separate worklists for each loop in the BasicCoverageBlock CFG, plus one for the +/// BasicCoverageBlocks outside all loops. This supports traversing the BCB CFG in a way that +/// ensures a loop is completely traversed before processing Blocks after the end of the loop. +#[derive(Debug)] +struct TraversalContext { + /// the start (backedge target) of a loop. If `None`, the context is all + /// BasicCoverageBlocks in the MIR that are _not_ within any loop. + loop_header: Option, + + /// worklist, to be traversed, of BasicCoverageBlocks in the loop headed by + /// `loop_header`, such that the loop is the inner inner-most loop containing these + /// BasicCoverageBlocks + worklist: Vec, +} + struct Instrumentor<'a, 'tcx> { pass_name: &'a str, tcx: TyCtxt<'tcx>, @@ -663,11 +674,7 @@ struct Instrumentor<'a, 'tcx> { } impl<'a, 'tcx> Instrumentor<'a, 'tcx> { - fn new( - pass_name: &'a str, - tcx: TyCtxt<'tcx>, - mir_body: &'a mut mir::Body<'tcx>, - ) -> Self { + fn new(pass_name: &'a str, tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self { let hir_body = hir_body(tcx, mir_body.source.def_id()); let basic_coverage_blocks = BasicCoverageBlocks::from_mir(mir_body); let bcb_dominators = basic_coverage_blocks.compute_bcb_dominators(); @@ -732,36 +739,31 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { None }; + self.make_counters(); + // Inject a counter for each `CoverageSpan`. There can be multiple `CoverageSpan`s for a // given BCB, but only one actual counter needs to be incremented per BCB. `bb_counters` // maps each `bcb` to its `Counter`, when injected. Subsequent `CoverageSpan`s // for a BCB that already has a `Counter` will inject a `CounterExpression` instead, and // compute its value by adding `ZERO` to the BCB `Counter` value. - let mut bb_counters = IndexVec::from_elem_n(None, self.mir_body.basic_blocks().len()); + let mut bcb_counters = IndexVec::from_elem_n(None, self.basic_coverage_blocks.num_nodes()); for CoverageSpan { span, bcb, .. } in coverage_spans { - let bb = self.basic_coverage_blocks[bcb].leader_bb(); - if let Some(&counter_operand) = bb_counters[bb].as_ref() { - let expression = - self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO); - debug!( - "Injecting counter expression {:?} at: {:?}:\n{}\n==========", - expression, - span, - source_map.span_to_snippet(span).expect("Error getting source for span"), - ); - self.inject_statement(file_name, &source_file, expression, span, bb); + let counter_kind = if let Some(&counter_operand) = bcb_counters[bcb].as_ref() { + self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO) + } else if let Some(counter_kind) = self.bcb_data_mut(bcb).take_counter() { + bcb_counters[bcb] = Some(counter_kind.as_operand_id()); + counter_kind } else { - let counter = self.make_counter(); - debug!( - "Injecting counter {:?} at: {:?}:\n{}\n==========", - counter, - span, - source_map.span_to_snippet(span).expect("Error getting source for span"), - ); - let counter_operand = counter.as_operand_id(); - bb_counters[bb] = Some(counter_operand); - self.inject_statement(file_name, &source_file, counter, span, bb); - } + bug!("Every BasicCoverageBlock should have a Counter or CounterExpression"); + }; + // TODO(richkadel): uncomment debug! + // debug!( + // "Injecting {:?} at: {:?}:\n{}\n==========", + // counter_kind, + // span, + // source_map.span_to_snippet(span).expect("Error getting source for span"), + // ); + self.inject_statement(file_name, &source_file, counter_kind, span, bcb); } if let Some(span_viewables) = span_viewables { @@ -776,6 +778,298 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } } + /// Traverse the BCB CFG and add either a `Counter` or `CounterExpression` to ever BCB, to be + /// injected with `CoverageSpan`s. `CounterExpressions` have no runtime overhead, so if a viable + /// expression (adding or subtracting two other counters or expressions) can compute the same + /// result as an embedded counter, a `CounterExpression` should be used. + /// + /// If two `BasicCoverageBlocks` branch from another `BasicCoverageBlock`, one of the branches + /// can be counted by `CounterExpression` by subtracting the other branch from the branching + /// block. Otherwise, the `BasicCoverageBlock` executed the least should have the `Counter`. + /// One way to predict which branch executes the least is by considering loops. A loop is exited + /// at a branch, so the branch that jumps to a `BasicCoverageBlock` outside the loop is almost + /// always executed less than the branch that does not exit the loop. + fn make_counters(&mut self) { + debug!( + "make_counters(): adding a counter or expression to each BasicCoverageBlock.\n ... First identify any loops by their backedges:" + ); + let mut loop_headers = BitSet::new_empty(self.basic_coverage_blocks.num_nodes()); + + // Identify backedges + for (bcb, _) in self.basic_coverage_blocks.iter_enumerated() { + for &successor in &self.basic_coverage_blocks.successors[bcb] { + if self.bcb_is_dominated_by(bcb, successor) { + debug!("Found BCB backedge: {:?} -> loop_header: {:?}", bcb, successor); + loop_headers.insert(successor); + } + } + } + + let start_bcb = self.basic_coverage_blocks.start_node(); + + // `context_stack` starts with a `TraversalContext` for the main function context (beginning + // with the `start` BasicCoverageBlock of the function). New worklists are pushed to the top + // of the stack as loops are entered, and popped off of the stack when a loop's worklist is + // exhausted. + let mut context_stack = Vec::new(); + context_stack.push(TraversalContext { loop_header: None, worklist: vec![start_bcb] }); + let mut visited = BitSet::new_empty(self.basic_coverage_blocks.num_nodes()); + + while let Some(bcb) = { + // Strip contexts with empty worklists from the top of the stack + while context_stack + .last() + .map_or(false, |context| context.worklist.is_empty()) + { + context_stack.pop(); + } + context_stack.last_mut().map_or(None, |context| context.worklist.pop()) + } + { + if !visited.insert(bcb) { + debug!("Already visited: {:?}", bcb); + continue; + } + debug!("Visiting {:?}", bcb); + if loop_headers.contains(bcb) { + debug!("{:?} is a loop header! Start a new TraversalContext...", bcb); + context_stack + .push(TraversalContext { loop_header: Some(bcb), worklist: Vec::new() }); + } + + debug!( + "{:?} has {} successors:", + bcb, + self.basic_coverage_blocks.successors[bcb].len() + ); + for &successor in &self.basic_coverage_blocks.successors[bcb] { + for context in context_stack.iter_mut().rev() { + if let Some(loop_header) = context.loop_header { + if self.bcb_is_dominated_by(successor, loop_header) { + debug!( + "Adding successor {:?} to worklist of loop headed by {:?}", + successor, loop_header + ); + context.worklist.push(successor); + break; + } + } else { + debug!("Adding successor {:?} to non-loop worklist", successor); + context.worklist.push(successor); + } + } + } + + let bcb_counter_operand = if let Some(counter) = self.bcb_data(bcb).counter() { + debug!("{:?} already has a counter: {:?}", bcb, counter); + counter.as_operand_id() + } else { + let counter = self.make_counter(); + debug!("{:?} needs a counter: {:?}", bcb, counter); + let operand = counter.as_operand_id(); + self.bcb_data_mut(bcb).set_counter(counter); + operand + }; + + let targets = match &self.bcb_data(bcb).terminator(self.mir_body) { + TerminatorKind::SwitchInt { targets, .. } => targets.clone(), + _ => vec![], + }; + if targets.len() > 0 { + debug!( + "{:?}'s terminator is a SwitchInt with targets: {:?}", + bcb, + targets.iter().map(|bb| self.bcb_from_bb(*bb)).collect::>() + ); + let switch_int_counter_operand = bcb_counter_operand; + + // Only one target can have an expression, but if `found_loop_exit`, any + // `in_loop_target` can get the `CounterExpression`. + let mut some_in_loop_target = None; + for context in context_stack.iter().rev() { + if let Some(loop_header) = context.loop_header { + let mut found_loop_exit = false; + for &bb in &targets { + let target_bcb = self.bcb_from_bb(bb); +// TODO(richkadel): But IF... + if self.bcb_is_dominated_by(target_bcb, loop_header) { +// the target or any non-branching BCB successor down the line +// exits or is a TerminatorKind::Return, or something similar, +// then this should be "found_loop_exit" instead of some_in_loop_target + + +// WHAT IF instead of checking target_bcb is dominated by loop header, +// we check something like, if backedge start (for all backedges leading to loop header?) +// is dominated by target_bcb? +// AND CAN THERE BE MORE THAN ONE BACKEDGE? I THINK MAYBE... like "continue loop_label;" + +// Will that work better? +// YES I THINK SO... IT SAYS, "target_bcb" leads out of the loop or it doesn't. + + some_in_loop_target = Some(target_bcb); + } else { + found_loop_exit = true; + } + if some_in_loop_target.is_some() && found_loop_exit { + break; + } + } + debug!( + "found_loop_exit={}, some_in_loop_target={:?}", + found_loop_exit, some_in_loop_target + ); + if !(found_loop_exit && some_in_loop_target.is_none()) { + break; + } + // else all branches exited this loop context, so run the same checks with + // the outer loop(s) + } + } + + // If some preferred target for a CounterExpression was not determined, pick any + // target. + let expression_target = if let Some(in_loop_target) = some_in_loop_target { + debug!("Adding expression to in_loop_target={:?}", some_in_loop_target); + in_loop_target + } else { + let bb_without_counter = *targets + .iter() + .find(|&&bb| { + let target_bcb = self.bcb_from_bb(bb); + self.bcb_data_mut(target_bcb).counter().is_none() + }) + .expect("At least one target should need a counter"); + debug!( + "No preferred expression target, so adding expression to the first target without an existing counter={:?}", + self.bcb_from_bb(bb_without_counter) + ); + self.bcb_from_bb(bb_without_counter) + }; + + // Assign a Counter or CounterExpression to each target BasicCoverageBlock, + // computing intermediate CounterExpression as needed. + let mut some_prev_counter_operand = None; + for bb in targets { + let target_bcb = self.bcb_from_bb(bb); + if target_bcb != expression_target { + // TODO(richkadel): this let if let else block is repeated above. Refactor into function. + let target_counter_operand = + if let Some(counter) = self.bcb_data(target_bcb).counter() { + debug!("{:?} already has a counter: {:?}", target_bcb, counter); + counter.as_operand_id() + } else { + let counter = self.make_counter(); + debug!("{:?} gets a counter: {:?}", target_bcb, counter); + let operand = counter.as_operand_id(); + self.bcb_data_mut(target_bcb).set_counter(counter); + operand + }; + if let Some(prev_counter_operand) = + some_prev_counter_operand.replace(target_counter_operand) + { + let expression = self.make_expression( + prev_counter_operand, + Op::Add, + target_counter_operand, + ); + debug!("new non-code expression: {:?}", expression); + let expression_operand = expression.as_operand_id(); + self.inject_non_code_expression(expression); + some_prev_counter_operand.replace(expression_operand); + } + } + } + let expression = self.make_expression( + switch_int_counter_operand, + Op::Subtract, + some_prev_counter_operand.expect("prev_counter should have a value"), + ); + debug!("{:?} gets an expression: {:?}", expression_target, expression); + self.bcb_data_mut(expression_target).set_counter(expression); + } + } + + debug_assert_eq!(visited.count(), visited.domain_size()); + } + + #[inline] + fn bcb_from_bb(&self, bb: BasicBlock) -> BasicCoverageBlock { + self.basic_coverage_blocks.bcb_from_bb(bb) + } + + #[inline] + fn bcb_data(&self, bcb: BasicCoverageBlock) -> &BasicCoverageBlockData { + &self.basic_coverage_blocks[bcb] + } + + #[inline] + fn bcb_data_mut(&mut self, bcb: BasicCoverageBlock) -> &mut BasicCoverageBlockData { + &mut self.basic_coverage_blocks[bcb] + } + + #[inline] + fn bcb_is_dominated_by(&self, node: BasicCoverageBlock, dom: BasicCoverageBlock) -> bool { + self.bcb_dominators.is_dominated_by(node, dom) + } + + // loop through backedges + + // select inner loops before their outer loops, so the first matched loop for a given target_bcb + // is it's inner-most loop + + // CASE #1: + // if a target_bcb is_dominated_by a loop bcb (target of backedge), and if any other target_bcb is NOT dominated by the loop bcb, + // add expression to the first target_bcb that dominated by the loop bcb, and counters to all others. Compute expressions from + // counter pairs as needed, to provide a single sum that can be subtracted from the SwitchInt block's counter. + + // CASE #2: + // if all target_bcb are dominated_by the loop bcb, no branch ends the loop (directly), so pick any branch to have the expression, + + // CASE #3: + // if NONE of the target_bcb are dominated_by the loop bcb, check if there's an outer loop (from stack of active loops?) + // and re-do this check again to see if one of them jumps out of the outer loop while other(s) don't, and assign the expression + // to one of the target_bcb that is dominated_by that outer loop. (Continue this if none are dominated by the outer loop either.) + + // TODO(richkadel): In the last case above, also see the next TODO below. If all targets exit the loop then can we pass that info + // to the predecessor (if only one??) so if the predecessor is a target of another SwitchInt, we know that the predecessor exits + // the loop, and should have the counter, if the predecessor is in CASE #2 (none of the other targets of the predecessor's + // SwitchInt exited the loop?) + + // TODO(richkadel): What about a target that is another SwitchInt where both branches exit the loop? + // Can I detect that somehow? + + // TODO(richkadel): For any node, N, and one of its successors, H (so N -> H), if (_also_) + // N is_dominated_by H, then N -> H is a backedge. That is, we've identified that N -> H is + // at least _one_ of possibly multiple arcs that loop back to the start of the loop with + // "header" H, and this also means we've identified a loop, that has "header" H. + // + // H dominates everything inside the loop. + // + // So a SwitchInt target in a BasicBlock that is_dominated_by H and has a branch target to a + // BasicBlock that is: + // * not H, and ... (what if the SwitchInt branch target _is_ H? `continue`? is this a + // candidate for a middle or optional priority for getting a Counter?) + // * not is_dominated_by H + // is a branch that jumps outside the loop, and should get an actual Counter, most likely + // + // Or perhaps conversely, a SwitchInt dominated by H with a branch that has a target that + // ALSO is dominated by H should get a CounterExpression. + // + // + // So I need to identify all of the "H"'s, by identifying all of the backedges. + // + // If I have multiple H's (multiple loops), how do I decide which loop to compare a branch + // target (by dominator) to? + // + // Can I assume the traversal order is helpful here? I.e., the "last" encountered loop + // header is the (only?) one to compare to? (Probably not only... I don't see how that would + // work for nested loops.) + // + // What about multiple loops in sequence? + // + // + // What about nexted loops and jumping out of one or more of them at a time? + fn make_counter(&mut self) -> CoverageKind { CoverageKind::Counter { function_source_hash: self.function_source_hash(), @@ -796,18 +1090,36 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { &mut self, file_name: Symbol, source_file: &Lrc, - coverage_kind: CoverageKind, + counter_kind: CoverageKind, span: Span, - block: BasicBlock, + bcb: BasicCoverageBlock, ) { - let code_region = make_code_region(file_name, source_file, span); - debug!(" injecting statement {:?} covering {:?}", coverage_kind, code_region); + let code_region = Some(make_code_region(file_name, source_file, span)); + // TODO(richkadel): uncomment debug! + // debug!(" injecting statement {:?} covering {:?}", counter_kind, code_region); - let data = &mut self.mir_body[block]; + let inject_in_bb = self.bcb_data(bcb).leader_bb(); + let data = &mut self.mir_body[inject_in_bb]; let source_info = data.terminator().source_info; let statement = Statement { source_info, - kind: StatementKind::Coverage(box Coverage { kind: coverage_kind, code_region }), + kind: StatementKind::Coverage(box Coverage { kind: counter_kind, code_region }), + }; + data.statements.push(statement); + } + + // Non-code expressions are injected into the coverage map, without generating executable code. + fn inject_non_code_expression(&mut self, expression: CoverageKind) { + debug_assert!(if let CoverageKind::Expression { .. } = expression { true } else { false }); + // TODO(richkadel): uncomment debug! + // debug!(" injecting non-code expression {:?}", expression); + + let inject_in_bb = mir::START_BLOCK; + let data = &mut self.mir_body[inject_in_bb]; + let source_info = data.terminator().source_info; + let statement = Statement { + source_info, + kind: StatementKind::Coverage(box Coverage { kind: expression, code_region: None }), }; data.statements.push(statement); } @@ -818,7 +1130,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { let mut span_viewables = Vec::new(); for coverage_span in coverage_spans { let CoverageSpan { span, bcb, coverage_statements, .. } = coverage_span; - let bcb_data = &self.basic_coverage_blocks[*bcb]; + let bcb_data = self.bcb_data(*bcb); let id = bcb_data.id(); let leader_bb = bcb_data.leader_bb(); let mut sorted_coverage_statements = coverage_statements.clone(); @@ -842,17 +1154,21 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } // Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of - // the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One `CoverageSpan` is generated for - // each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will + // the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One `CoverageSpan` is generated + // for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will // merge some `CoverageSpan`s, at which point a `CoverageSpan` may represent multiple // `Statement`s and/or `Terminator`s.) - fn extract_spans(&self, bcb: BasicCoverageBlock, bcb_data: &'a BasicCoverageBlockData) -> Vec { + fn extract_spans( + &self, + bcb: BasicCoverageBlock, + bcb_data: &'a BasicCoverageBlockData, + ) -> Vec { let body_span = self.body_span(); - let mir_basic_blocks = self.mir_body.basic_blocks(); - bcb_data.basic_blocks() + bcb_data + .basic_blocks() .map(|bbref| { let bb = *bbref; - let data = &mir_basic_blocks[bb]; + let data = &self.mir_body[bb]; data.statements .iter() .enumerate() @@ -892,8 +1208,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { /// Note the resulting vector of `CoverageSpan`s does may not be fully sorted (and does not need /// to be). fn coverage_spans(&self) -> Vec { - let mut initial_spans = - Vec::::with_capacity(self.mir_body.basic_blocks().len() * 2); + let mut initial_spans = Vec::::with_capacity(self.mir_body.num_nodes() * 2); for (bcb, bcb_data) in self.basic_coverage_blocks.iter_enumerated() { for coverage_span in self.extract_spans(bcb, bcb_data) { initial_spans.push(coverage_span); @@ -974,26 +1289,29 @@ impl<'a> CoverageSpanRefinery<'a> { fn to_refined_spans(mut self) -> Vec { while self.next_coverage_span() { if self.curr().is_mergeable(self.prev()) { - debug!(" same bcb (and neither is a closure), merge with prev={:?}", self.prev()); + // TODO(richkadel): uncomment debug! + // debug!(" same bcb (and neither is a closure), merge with prev={:?}", self.prev()); let prev = self.take_prev(); self.curr_mut().merge_from(prev); // Note that curr.span may now differ from curr_original_span } else if self.prev_ends_before_curr() { - debug!( - " different bcbs and disjoint spans, so keep curr for next iter, and add \ - prev={:?}", - self.prev() - ); + // TODO(richkadel): uncomment debug! + // debug!( + // " different bcbs and disjoint spans, so keep curr for next iter, and add \ + // prev={:?}", + // self.prev() + // ); let prev = self.take_prev(); self.add_refined_span(prev); } else if self.prev().is_closure { // drop any equal or overlapping span (`curr`) and keep `prev` to test again in the // next iter - debug!( - " curr overlaps a closure (prev). Drop curr and keep prev for next iter. \ - prev={:?}", - self.prev() - ); + // TODO(richkadel): uncomment debug! + // debug!( + // " curr overlaps a closure (prev). Drop curr and keep prev for next iter. \ + // prev={:?}", + // self.prev() + // ); self.discard_curr(); } else if self.curr().is_closure { self.carve_out_span_for_closure(); @@ -1003,10 +1321,12 @@ impl<'a> CoverageSpanRefinery<'a> { self.cutoff_prev_at_overlapping_curr(); } } - debug!(" AT END, adding last prev={:?}", self.prev()); + // TODO(richkadel): uncomment debug! + // debug!(" AT END, adding last prev={:?}", self.prev()); let pending_dups = self.pending_dups.split_off(0); for dup in pending_dups.into_iter() { - debug!(" ...adding at least one pending dup={:?}", dup); + // TODO(richkadel): uncomment debug! + // debug!(" ...adding at least one pending dup={:?}", dup); self.add_refined_span(dup); } let prev = self.take_prev(); @@ -1071,14 +1391,16 @@ impl<'a> CoverageSpanRefinery<'a> { fn check_pending_dups(&mut self) { if let Some(dup) = self.pending_dups.last() { if dup.span != self.prev().span { - debug!( - " SAME spans, but pending_dups are NOT THE SAME, so BCBs matched on \ - previous iteration, or prev started a new disjoint span" - ); + // TODO(richkadel): uncomment debug! + // debug!( + // " SAME spans, but pending_dups are NOT THE SAME, so BCBs matched on \ + // previous iteration, or prev started a new disjoint span" + // ); if dup.span.hi() <= self.curr().span.lo() { let pending_dups = self.pending_dups.split_off(0); for dup in pending_dups.into_iter() { - debug!(" ...adding at least one pending={:?}", dup); + // TODO(richkadel): uncomment debug! + // debug!(" ...adding at least one pending={:?}", dup); self.add_refined_span(dup); } } else { @@ -1095,13 +1417,15 @@ impl<'a> CoverageSpanRefinery<'a> { self.prev_original_span = self.curr_original_span; } while let Some(curr) = self.sorted_spans_iter.next() { - debug!("FOR curr={:?}", curr); + // TODO(richkadel): uncomment debug! + // debug!("FOR curr={:?}", curr); if self.prev_starts_after_next(&curr) { - debug!( - " prev.span starts after curr.span, so curr will be dropped (skipping past \ - closure?); prev={:?}", - self.prev() - ); + // TODO(richkadel): uncomment debug! + // debug!( + // " prev.span starts after curr.span, so curr will be dropped (skipping past \ + // closure?); prev={:?}", + // self.prev() + // ); } else { // Save a copy of the original span for `curr` in case the `CoverageSpan` is changed // by `self.curr_mut().merge_from(prev)`. @@ -1148,11 +1472,13 @@ impl<'a> CoverageSpanRefinery<'a> { if has_pre_closure_span { let mut pre_closure = self.prev().clone(); pre_closure.span = pre_closure.span.with_hi(left_cutoff); - debug!(" prev overlaps a closure. Adding span for pre_closure={:?}", pre_closure); + // TODO(richkadel): uncomment debug! + // debug!(" prev overlaps a closure. Adding span for pre_closure={:?}", pre_closure); if !pending_dups.is_empty() { for mut dup in pending_dups.iter().cloned() { dup.span = dup.span.with_hi(left_cutoff); - debug!(" ...and at least one pre_closure dup={:?}", dup); + // TODO(richkadel): uncomment debug! + // debug!(" ...and at least one pre_closure dup={:?}", dup); self.add_refined_span(dup); } } @@ -1193,21 +1519,23 @@ impl<'a> CoverageSpanRefinery<'a> { // // The dominator's (`prev`) execution count may be higher than the dominated // block's execution count, so drop `curr`. - debug!( - " different bcbs but SAME spans, and prev dominates curr. Drop curr and \ - keep prev for next iter. prev={:?}", - self.prev() - ); + // TODO(richkadel): uncomment debug! + // debug!( + // " different bcbs but SAME spans, and prev dominates curr. Drop curr and \ + // keep prev for next iter. prev={:?}", + // self.prev() + // ); self.discard_curr(); } else { // Save `prev` in `pending_dups`. (`curr` will become `prev` in the next iteration.) // If the `curr` CoverageSpan is later discarded, `pending_dups` can be discarded as // well; but if `curr` is added to refined_spans, the `pending_dups` will also be added. - debug!( - " different bcbs but SAME spans, and neither dominates, so keep curr for \ - next iter, and, pending upcoming spans (unless overlapping) add prev={:?}", - self.prev() - ); + // TODO(richkadel): uncomment debug! + // debug!( + // " different bcbs but SAME spans, and neither dominates, so keep curr for \ + // next iter, and, pending upcoming spans (unless overlapping) add prev={:?}", + // self.prev() + // ); let prev = self.take_prev(); self.pending_dups.push(prev); } @@ -1221,18 +1549,21 @@ impl<'a> CoverageSpanRefinery<'a> { /// avoids injecting multiple counters for overlapping spans, and the potential for /// double-counting. fn cutoff_prev_at_overlapping_curr(&mut self) { - debug!( - " different bcbs, overlapping spans, so ignore/drop pending and only add prev \ - if it has statements that end before curr={:?}", - self.prev() - ); + // TODO(richkadel): uncomment debug! + // debug!( + // " different bcbs, overlapping spans, so ignore/drop pending and only add prev \ + // if it has statements that end before curr={:?}", + // self.prev() + // ); if self.pending_dups.is_empty() { let curr_span = self.curr().span; self.prev_mut().cutoff_statements_at(curr_span.lo()); if self.prev().coverage_statements.is_empty() { - debug!(" ... no non-overlapping statements to add"); + // TODO(richkadel): uncomment debug! + // debug!(" ... no non-overlapping statements to add"); } else { - debug!(" ... adding modified prev={:?}", self.prev()); + // TODO(richkadel): uncomment debug! + // debug!(" ... adding modified prev={:?}", self.prev()); let prev = self.take_prev(); self.add_refined_span(prev); } @@ -1297,7 +1628,7 @@ fn filtered_terminator_span(terminator: &'a Terminator<'tcx>, body_span: Span) - // an `if condition { block }` has a span that includes the executed block, if true, // but for coverage, the code region executed, up to *and* through the SwitchInt, // actually stops before the if's block.) - TerminatorKind::Unreachable // Unreachable blocks are not connected to the CFG + TerminatorKind::Unreachable // Unreachable blocks are not connected to the MIR CFG | TerminatorKind::Assert { .. } | TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } @@ -1428,3 +1759,50 @@ impl<'a: 'tcx, 'tcx, F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>> (size, Some(size)) } } + +// NOTE: Regarding past efforts and revelations when trying to identify `Unreachable` coverage spans +// from the MIR: +// +// TerminatorKind::FalseEdge targets from SwitchInt don't appear to be helpful in identifying +// unreachable code. I did test the theory, but the following changes were not beneficial. (I +// assumed that replacing some constants with non-deterministic variables might effect which blocks +// were targeted by a `FalseEdge` `imaginary_target`. It did not.) +// +// Also note that, if there is a way to identify BasicBlocks that are part of the MIR CFG, but not +// actually reachable, here are some other things to consider: +// +// Injecting unreachable code regions will probably require computing the set difference between the +// basic blocks found without filtering out unreachable blocks, and the basic blocks found with a +// filter (similar to or as an extension of the `filter_unwind_paths` filter); then computing the +// `CoverageSpans` without the filter; and then injecting `Counter`s or `CounterExpression`s for +// blocks that are not unreachable, or injecting `Unreachable` code regions otherwise. This seems +// straightforward, but not trivial. +// +// Alternatively, we might instead want to leave the unreachable blocks in (bypass the filter here), +// and inject the counters. This will result in counter values of zero (0) for unreachable code +// (and, notably, the code will be displayed with a red background by `llvm-cov show`). +// +// ```rust +// TerminatorKind::SwitchInt { .. } => { +// let some_imaginary_target = successors.clone().find_map(|&successor| { +// let term = mir_body[successor].terminator(); +// if let TerminatorKind::FalseEdge { imaginary_target, .. } = term.kind { +// if mir_body.predecessors()[imaginary_target].len() == 1 { +// return Some(imaginary_target); +// } +// } +// None +// }); +// if let Some(imaginary_target) = some_imaginary_target { +// box successors.filter(move |&&successor| successor != imaginary_target) +// } else { +// box successors +// } +// } +// ``` +// +// Note this also required changing the closure signature for the `ShortCurcuitPreorder` to: +// +// ```rust +// F: Fn(&'tcx TerminatorKind<'tcx>) -> Box + 'a>, +// ``` From aa9747dd754dc7d272ccaa60baa367f681853e56 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Wed, 14 Oct 2020 16:05:54 -0700 Subject: [PATCH 04/18] optimized replacement of counters with expressions plus new BCB graphviz --- .../rustc_codegen_ssa/src/coverageinfo/map.rs | 38 + compiler/rustc_graphviz/src/lib.rs | 1 + compiler/rustc_middle/src/mir/coverage.rs | 59 +- compiler/rustc_middle/src/mir/mod.rs | 19 +- .../src/transform/instrument_coverage.rs | 1417 +++++++++++++---- .../rustc_mir/src/util/generic_graphviz.rs | 175 ++ compiler/rustc_mir/src/util/graphviz.rs | 4 +- compiler/rustc_mir/src/util/mod.rs | 1 + ...rage_graphviz.bar.InstrumentCoverage.0.dot | 6 + ...age_graphviz.main.InstrumentCoverage.0.dot | 11 + src/test/mir-opt/coverage_graphviz.rs | 20 + ...ment_coverage.main.InstrumentCoverage.diff | 5 +- src/test/mir-opt/instrument_coverage.rs | 8 +- .../filecheck.testprog.txt | 2 +- .../coverage-reports-base/Makefile | 1 + .../expected_export_coverage.closure.json | 16 +- .../expected_export_coverage.if.json | 12 +- .../expected_export_coverage.inner_items.json | 12 +- ...expected_export_coverage.lazy_boolean.json | 16 +- .../expected_export_coverage.simple_loop.json | 16 +- ...expected_export_coverage.simple_match.json | 24 +- ...cted_export_coverage.try_error_result.json | 28 +- ...ed_export_coverage.various_conditions.json | 16 +- ...ed_export_coverage.while_early_return.json | 24 +- .../expected_show_coverage.simple_match.txt | 12 +- ...xpected_show_coverage.try_error_result.txt | 9 +- ...ected_show_coverage.while_early_return.txt | 2 +- .../expected_export_coverage.closure.json | 16 +- .../expected_export_coverage.if.json | 12 +- .../expected_export_coverage.inner_items.json | 12 +- ...expected_export_coverage.lazy_boolean.json | 16 +- .../expected_export_coverage.simple_loop.json | 16 +- ...expected_export_coverage.simple_match.json | 24 +- ...cted_export_coverage.try_error_result.json | 28 +- ...ed_export_coverage.various_conditions.json | 16 +- ...ed_export_coverage.while_early_return.json | 24 +- .../expected_show_coverage.simple_match.txt | 12 +- ...xpected_show_coverage.try_error_result.txt | 9 +- ...ected_show_coverage.while_early_return.txt | 2 +- ...-in_func.-------.InstrumentCoverage.0.html | 3 +- ...ait_func.-------.InstrumentCoverage.0.html | 3 +- ...oop.main.-------.InstrumentCoverage.0.html | 6 +- ...tch.main.-------.InstrumentCoverage.0.html | 49 +- ...ult.main.-------.InstrumentCoverage.0.html | 60 +- ...ons.main.-------.InstrumentCoverage.0.html | 38 +- ...urn.main.-------.InstrumentCoverage.0.html | 14 +- ...-in_func.-------.InstrumentCoverage.0.html | 3 +- ...ait_func.-------.InstrumentCoverage.0.html | 3 +- ...oop.main.-------.InstrumentCoverage.0.html | 6 +- ...tch.main.-------.InstrumentCoverage.0.html | 49 +- ...ult.main.-------.InstrumentCoverage.0.html | 60 +- ...ons.main.-------.InstrumentCoverage.0.html | 38 +- ...urn.main.-------.InstrumentCoverage.0.html | 14 +- 53 files changed, 1778 insertions(+), 709 deletions(-) create mode 100644 compiler/rustc_mir/src/util/generic_graphviz.rs create mode 100644 src/test/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot create mode 100644 src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot create mode 100644 src/test/mir-opt/coverage_graphviz.rs diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs index 0ee2ecfc1e809..9f2999702d8a5 100644 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs @@ -38,6 +38,10 @@ pub struct FunctionCoverage { impl FunctionCoverage { pub fn new<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { let coverageinfo = tcx.coverageinfo(instance.def_id()); + debug!( + "FunctionCoverage::new(instance={:?}) has coverageinfo={:?}", + instance, coverageinfo + ); Self { source_hash: 0, // will be set with the first `add_counter()` counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize), @@ -150,13 +154,41 @@ impl FunctionCoverage { self.counters .get(index) .unwrap() // pre-validated + // TODO(richkadel): is it really pre-validated? + // What if I add some counters that never get added to the map, and they are + // larger than the number of counters in the MIR (as seems to happen with expressions below?) .as_ref() .map(|_| Counter::counter_value_reference(index)) } else { let index = self.expression_index(u32::from(id)); + // TODO(richkadel): remove this debug + debug!( + "id_to_counter expression id={:?}, self.expressions.get(index={:?}) = {:?}", + id, + index, + self.expressions.get(index) + ); self.expressions .get(index) + // TODO(richkadel): Now some tests generate segfault, and other tests hit this out of range error + // Some expressions reference blocks that ended up not needing counters. + // Can we assume the expression is no longer relevant? If not, then instrument_counters + // transform pass will need to figure this out earlier (MAYBE IT SHOULD ANYWAY?) + // and if the counter is needed for an expression that can no longer be resolved, + // create a new make_counter() right there? + // + // MUST FIX! + // + // It looks like the segfault is at: + // + // /usr/local/google/home/richkadel/rust/src/llvm-project/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp:93 + // AdjustedExpressionIDs[ID] = 1; + // + // I think we have expressions with operand IDs that don't exist as either counters or expressions, and that's breaking + // the LLVM code. + // TODO(richkadel): replace expect() with unwrap_or()? .expect("expression id is out of range") + // .unwrap_or(&None) .as_ref() .map(|_| Counter::expression(new_indexes[index])) } @@ -198,6 +230,12 @@ impl FunctionCoverage { if let Some(region) = optional_region { expression_regions.push((Counter::expression(mapped_expression_index), region)); } + } else { + debug!( + "Ignoring expression with one or more missing operands: \ + original_index={:?}, lhs={:?}, op={:?}, rhs={:?}, region={:?}", + original_index, lhs, op, rhs, optional_region, + ) } } (counter_expressions, expression_regions.into_iter()) diff --git a/compiler/rustc_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs index 76e33bed97f27..e9ec11ba99714 100644 --- a/compiler/rustc_graphviz/src/lib.rs +++ b/compiler/rustc_graphviz/src/lib.rs @@ -643,6 +643,7 @@ where } if options.contains(&RenderOption::DarkTheme) { graph_attrs.push(r#"bgcolor="black""#); + graph_attrs.push(r#"fontcolor="white""#); content_attrs.push(r#"color="white""#); content_attrs.push(r#"fontcolor="white""#); } diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 0421eabc2dc05..29b885136122e 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -64,6 +64,13 @@ impl From for ExpressionOperandId { } } +impl From<&mut CounterValueReference> for ExpressionOperandId { + #[inline] + fn from(v: &mut CounterValueReference) -> ExpressionOperandId { + ExpressionOperandId::from(v.as_u32()) + } +} + impl From for ExpressionOperandId { #[inline] fn from(v: InjectedExpressionIndex) -> ExpressionOperandId { @@ -71,7 +78,14 @@ impl From for ExpressionOperandId { } } -#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +impl From<&mut InjectedExpressionIndex> for ExpressionOperandId { + #[inline] + fn from(v: &mut InjectedExpressionIndex) -> ExpressionOperandId { + ExpressionOperandId::from(v.as_u32()) + } +} + +#[derive(Clone, PartialEq, TyEncodable, TyDecodable, HashStable, TypeFoldable)] pub enum CoverageKind { Counter { function_source_hash: u64, @@ -88,14 +102,51 @@ pub enum CoverageKind { impl CoverageKind { pub fn as_operand_id(&self) -> ExpressionOperandId { + use CoverageKind::*; match *self { - CoverageKind::Counter { id, .. } => ExpressionOperandId::from(id), - CoverageKind::Expression { id, .. } => ExpressionOperandId::from(id), - CoverageKind::Unreachable => { + Counter { id, .. } => ExpressionOperandId::from(id), + Expression { id, .. } => ExpressionOperandId::from(id), + Unreachable => { bug!("Unreachable coverage cannot be part of an expression") } } } + + pub fn is_counter(&self) -> bool { + match self { + Self::Counter { .. } => true, + _ => false, + } + } + + pub fn is_expression(&self) -> bool { + match self { + Self::Expression { .. } => true, + _ => false, + } + } + + pub fn is_unreachable(&self) -> bool { + *self == Self::Unreachable + } +} + +impl Debug for CoverageKind { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + use CoverageKind::*; + match self { + Counter { id, .. } => write!(fmt, "Counter({:?})", id.index()), + Expression { id, lhs, op, rhs } => write!( + fmt, + "Expression({:?}) = {} {} {}", + id.index(), + lhs.index(), + if *op == Op::Add { "+" } else { "-" }, + rhs.index(), + ), + Unreachable => write!(fmt, "Unreachable"), + } + } } #[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, PartialEq, Eq, PartialOrd, Ord)] diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 5de64d3b1c1fb..1df2bed2ae2e6 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1601,21 +1601,10 @@ impl Debug for Statement<'_> { write!(fmt, "AscribeUserType({:?}, {:?}, {:?})", place, variance, c_ty) } Coverage(box ref coverage) => { - let rgn = &coverage.code_region; - match coverage.kind { - CoverageKind::Counter { id, .. } => { - write!(fmt, "Coverage::Counter({:?}) for {:?}", id.index(), rgn) - } - CoverageKind::Expression { id, lhs, op, rhs } => write!( - fmt, - "Coverage::Expression({:?}) = {} {} {} for {:?}", - id.index(), - lhs.index(), - if op == coverage::Op::Add { "+" } else { "-" }, - rhs.index(), - rgn - ), - CoverageKind::Unreachable => write!(fmt, "Coverage::Unreachable for {:?}", rgn), + if let Some(rgn) = &coverage.code_region { + write!(fmt, "Coverage::{:?} for {:?}", coverage.kind, rgn) + } else { + write!(fmt, "Coverage::{:?}", coverage.kind) } } Nop => write!(fmt, "nop"), diff --git a/compiler/rustc_mir/src/transform/instrument_coverage.rs b/compiler/rustc_mir/src/transform/instrument_coverage.rs index 3e0dab7ece437..a06392627bfdb 100644 --- a/compiler/rustc_mir/src/transform/instrument_coverage.rs +++ b/compiler/rustc_mir/src/transform/instrument_coverage.rs @@ -1,14 +1,16 @@ use crate::transform::MirPass; +use crate::util::generic_graphviz::GraphvizWriter; use crate::util::pretty; use crate::util::spanview::{self, SpanViewable}; use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::graph::dominators::{self, Dominators}; use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; use rustc_index::bit_set::BitSet; -use rustc_index::vec::IndexVec; +use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::hir; use rustc_middle::hir::map::blocks::FnLikeNode; use rustc_middle::ich::StableHashingContext; @@ -25,8 +27,6 @@ use rustc_span::def_id::DefId; use rustc_span::source_map::original_sp; use rustc_span::{BytePos, CharPos, Pos, SourceFile, Span, Symbol, SyntaxContext}; -use smallvec::SmallVec; - use std::cmp::Ordering; use std::ops::{Index, IndexMut}; @@ -58,23 +58,72 @@ pub(crate) fn provide(providers: &mut Providers) { /// are still included in the total `num_counters` or `num_expressions`.) Simply counting the /// calls may not work; but computing the number of counters or expressions by adding `1` to the /// highest ID (for a given instrumented function) is valid. +/// +/// This visitor runs twice, first with `add_missing_operands` set to `false`, to find the maximum +/// counter ID and maximum expression ID based on their enum variant `id` fields; then, as a +/// safeguard, with `add_missing_operands` set to `true`, to find any other counter or expression +/// IDs referenced by expression operands, if not already seen. +/// +/// Ideally, every expression operand in the MIR will have a corresponding Counter or Expression, +/// but since current or future MIR optimizations can theoretically optimize out segments of a +/// MIR, it may not be possible to guarantee this, so the second pass ensures the `CoverageInfo` +/// counts include all referenced IDs. struct CoverageVisitor { info: CoverageInfo, + add_missing_operands: bool, +} + +impl CoverageVisitor { + // If an expression operand is encountered with an ID outside the range of known counters and + // expressions, the only way to determine if the ID is a counter ID or an expression ID is to + // assume a maximum possible counter ID value. + const MAX_COUNTER_GUARD: u32 = (u32::MAX / 2) + 1; + + #[inline(always)] + fn update_num_counters(&mut self, counter_id: u32) { + self.info.num_counters = std::cmp::max(self.info.num_counters, counter_id + 1); + } + + #[inline(always)] + fn update_num_expressions(&mut self, expression_id: u32) { + let expression_index = u32::MAX - expression_id; + self.info.num_expressions = std::cmp::max(self.info.num_expressions, expression_index + 1); + } + + fn update_from_expression_operand(&mut self, operand_id: u32) { + if operand_id >= self.info.num_counters { + let operand_as_expression_index = u32::MAX - operand_id; + if operand_as_expression_index >= self.info.num_expressions { + if operand_id <= Self::MAX_COUNTER_GUARD { + self.update_num_counters(operand_id) + } else { + self.update_num_expressions(operand_id) + } + } + } + } } impl Visitor<'_> for CoverageVisitor { fn visit_coverage(&mut self, coverage: &Coverage, _location: Location) { - match coverage.kind { - CoverageKind::Counter { id, .. } => { - let counter_id = u32::from(id); - self.info.num_counters = std::cmp::max(self.info.num_counters, counter_id + 1); + if self.add_missing_operands { + match coverage.kind { + CoverageKind::Expression { lhs, rhs, .. } => { + self.update_from_expression_operand(u32::from(lhs)); + self.update_from_expression_operand(u32::from(rhs)); + } + _ => {} } - CoverageKind::Expression { id, .. } => { - let expression_index = u32::MAX - u32::from(id); - self.info.num_expressions = - std::cmp::max(self.info.num_expressions, expression_index + 1); + } else { + match coverage.kind { + CoverageKind::Counter { id, .. } => { + self.update_num_counters(u32::from(id)); + } + CoverageKind::Expression { id, .. } => { + self.update_num_expressions(u32::from(id)); + } + _ => {} } - _ => {} } } } @@ -82,10 +131,16 @@ impl Visitor<'_> for CoverageVisitor { fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo { let mir_body = tcx.optimized_mir(def_id); - let mut coverage_visitor = - CoverageVisitor { info: CoverageInfo { num_counters: 0, num_expressions: 0 } }; + let mut coverage_visitor = CoverageVisitor { + info: CoverageInfo { num_counters: 0, num_expressions: 0 }, + add_missing_operands: false, + }; coverage_visitor.visit_body(mir_body); + + coverage_visitor.add_missing_operands = true; + coverage_visitor.visit_body(mir_body); + coverage_visitor.info } @@ -154,12 +209,13 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage { struct BasicCoverageBlockData { basic_blocks: Vec, counter_kind: Option, + edge_counter_from_bcbs: Option>, } impl BasicCoverageBlockData { pub fn from(basic_blocks: Vec) -> Self { assert!(basic_blocks.len() > 0); - Self { basic_blocks, counter_kind: None } + Self { basic_blocks, counter_kind: None, edge_counter_from_bcbs: None } } #[inline(always)] @@ -178,15 +234,18 @@ impl BasicCoverageBlockData { } #[inline(always)] - pub fn terminator<'a, 'tcx>(&self, mir_body: &'a mir::Body<'tcx>) -> &'a TerminatorKind<'tcx> { - &mir_body[self.last_bb()].terminator().kind + pub fn terminator<'a, 'tcx>(&self, mir_body: &'a mir::Body<'tcx>) -> &'a Terminator<'tcx> { + &mir_body[self.last_bb()].terminator() } #[inline(always)] - pub fn set_counter(&mut self, counter_kind: CoverageKind) { + pub fn set_counter(&mut self, counter_kind: CoverageKind) -> ExpressionOperandId { + debug_assert!(self.edge_counter_from_bcbs.is_none() || counter_kind.is_expression(), "attempt to add a `Counter` to a BCB target with existing incoming edge counters"); + let operand = counter_kind.as_operand_id(); self.counter_kind .replace(counter_kind) .expect_none("attempt to set a BasicCoverageBlock coverage counter more than once"); + operand } #[inline(always)] @@ -199,6 +258,31 @@ impl BasicCoverageBlockData { self.counter_kind.take() } + #[inline(always)] + pub fn set_edge_counter_from(&mut self, from_bcb: BasicCoverageBlock, counter_kind: CoverageKind) -> ExpressionOperandId { + debug_assert!(self.counter_kind.as_ref().map_or(true, |c| c.is_expression()), "attempt to add an incoming edge counter from {:?} when the target BCB already has a `Counter`", from_bcb); + let operand = counter_kind.as_operand_id(); + self.edge_counter_from_bcbs + .get_or_insert_with(|| FxHashMap::default()) + .insert(from_bcb, counter_kind) + .expect_none("attempt to set an edge counter more than once"); + operand + } + + #[inline(always)] + pub fn edge_counter_from(&self, from_bcb: BasicCoverageBlock) -> Option<&CoverageKind> { + if let Some(edge_counter_from_bcbs) = &self.edge_counter_from_bcbs { + edge_counter_from_bcbs.get(&from_bcb) + } else { + None + } + } + + #[inline(always)] + pub fn take_edge_counters(&mut self) -> Option> { + self.edge_counter_from_bcbs.take().map_or(None, |m|Some(m.into_iter())) + } + pub fn id(&self) -> String { format!( "@{}", @@ -209,6 +293,65 @@ impl BasicCoverageBlockData { .join(ID_SEPARATOR) ) } + + pub fn to_string_sections( + &self, + tcx: TyCtxt<'tcx>, + mir_body: &mir::Body<'tcx>, + debug_counters: &DebugCounters, + some_coverage_spans_with_counters: Option<&Vec<(CoverageSpan, CoverageKind)>>, + some_dependency_counters: Option<&Vec>, + some_intermediate_expressions: Option<&Vec>, + ) -> Vec { + let len = self.basic_blocks.len(); + let mut sections = Vec::new(); + if let Some(collect_intermediate_expressions) = some_intermediate_expressions { + sections.push( + collect_intermediate_expressions + .iter() + .map(|expression| format!("Intermediate {}", debug_counters.format_counter(expression))) + .collect::>() + .join("\n"), + ); + } + if let Some(coverage_spans_with_counters) = some_coverage_spans_with_counters { + sections.push( + coverage_spans_with_counters + .iter() + .map(|(covspan, counter)| format!("{} at {}", debug_counters.format_counter(counter), covspan.format(tcx, mir_body))) + .collect::>() + .join("\n"), + ); + } + if let Some(dependency_counters) = some_dependency_counters { + sections.push( + format!( + "Non-coverage counters:\n {}", + dependency_counters + .iter() + .map(|counter| debug_counters.format_counter(counter)) + .collect::>() + .join(" \n"), + ) + ); + } + if let Some(counter_kind) = &self.counter_kind { + sections.push(format!("{:?}", counter_kind)); + } + let non_term_blocks = self.basic_blocks[0..len - 1] + .iter() + .map(|&bb| format!("{:?}: {}", bb, term_type(&mir_body[bb].terminator().kind))) + .collect::>(); + if non_term_blocks.len() > 0 { + sections.push(non_term_blocks.join("\n")); + } + sections.push(format!( + "{:?}: {}", + self.basic_blocks.last().unwrap(), + term_type(&self.terminator(mir_body).kind) + )); + sections + } } rustc_index::newtype_index! { @@ -238,9 +381,12 @@ impl BasicCoverageBlocks { let successors = IndexVec::from_fn_n( |bcb| { let bcb_data = &bcbs[bcb]; - let bcb_successors = bcb_data - .terminator(mir_body) - .successors() + let bcb_successors = bcb_filtered_successors(&bcb_data.terminator(mir_body).kind) +// TODO(richkadel): +// MAKE SURE WE ONLY RETURN THE SAME SUCCESSORS USED WHEN CREATING THE BCB (THE FIRST SUCCESSOR ONLY, +// UNLESS ITS A SWITCHINT).) + +// THEN BUILD PREDECESSORS FROM THE SUCCESSORS .filter_map(|&successor_bb| bb_to_bcb[successor_bb]) .collect::>(); debug_assert!({ @@ -255,24 +401,12 @@ impl BasicCoverageBlocks { bcbs.len(), ); - let predecessors = IndexVec::from_fn_n( - |bcb| { - let bcb_data = &bcbs[bcb]; - let bcb_predecessors = mir_body.predecessors()[bcb_data.leader_bb()] - .iter() - .filter_map(|&predecessor_bb| bb_to_bcb[predecessor_bb]) - .collect::(); - debug_assert!({ - let mut sorted = bcb_predecessors.clone(); - sorted.sort_unstable(); - let initial_len = sorted.len(); - sorted.dedup(); - sorted.len() == initial_len - }); - bcb_predecessors - }, - bcbs.len(), - ); + let mut predecessors = IndexVec::from_elem_n(Vec::new(), bcbs.len()); + for (bcb, bcb_successors) in successors.iter_enumerated() { + for &successor in bcb_successors { + predecessors[successor].push(bcb); + } + } Self { bcbs, bb_to_bcb, successors, predecessors } } @@ -283,25 +417,15 @@ impl BasicCoverageBlocks { IndexVec, IndexVec>, ) { - let len = mir_body.num_nodes(); - let mut bcbs = IndexVec::with_capacity(len); - let mut bb_to_bcb = IndexVec::from_elem_n(None, len); + let num_basic_blocks = mir_body.num_nodes(); + let mut bcbs = IndexVec::with_capacity(num_basic_blocks); + let mut bb_to_bcb = IndexVec::from_elem_n(None, num_basic_blocks); // Walk the MIR CFG using a Preorder traversal, which starts from `START_BLOCK` and follows // each block terminator's `successors()`. Coverage spans must map to actual source code, // so compiler generated blocks and paths can be ignored. To that end, the CFG traversal // intentionally omits unwind paths. - let mir_cfg_without_unwind = ShortCircuitPreorder::new(mir_body, |term_kind| { - let mut successors = term_kind.successors(); - match &term_kind { - // SwitchInt successors are never unwind, and all of them should be traversed. - TerminatorKind::SwitchInt { .. } => successors, - // For all other kinds, return only the first successor, if any, and ignore unwinds. - // NOTE: `chain(&[])` is required to coerce the `option::iter` (from - // `next().into_iter()`) into the `mir::Successors` aliased type. - _ => successors.next().into_iter().chain(&[]), - } - }); + let mir_cfg_without_unwind = ShortCircuitPreorder::new(mir_body, bcb_filtered_successors); let mut basic_blocks = Vec::new(); for (bb, data) in mir_cfg_without_unwind { @@ -422,9 +546,19 @@ impl BasicCoverageBlocks { } #[inline(always)] - pub fn bcb_from_bb(&self, bb: BasicBlock) -> BasicCoverageBlock { - self.bb_to_bcb[bb] - .expect("bb is not in any bcb (pre-filtered, such as unwind paths perhaps?)") + pub fn iter_enumerated_mut( + &mut self, + ) -> impl Iterator { + self.bcbs.iter_enumerated_mut() + } + + #[inline(always)] + pub fn bcb_from_bb(&self, bb: BasicBlock) -> Option { + if bb.index() < self.bb_to_bcb.len() { + self.bb_to_bcb[bb] + } else { + None + } } #[inline(always)] @@ -463,11 +597,11 @@ impl graph::WithNumNodes for BasicCoverageBlocks { impl graph::WithStartNode for BasicCoverageBlocks { #[inline] fn start_node(&self) -> Self::Node { - self.bcb_from_bb(mir::START_BLOCK) + self.bcb_from_bb(mir::START_BLOCK).expect("mir::START_BLOCK should be in a BasicCoverageBlock") } } -type BcbSuccessors<'a> = std::slice::Iter<'a, BasicCoverageBlock>; +type BcbSuccessors<'graph> = std::slice::Iter<'graph, BasicCoverageBlock>; impl graph::WithSuccessors for BasicCoverageBlocks { #[inline] @@ -476,18 +610,16 @@ impl graph::WithSuccessors for BasicCoverageBlocks { } } -impl<'a> graph::GraphSuccessors<'a> for BasicCoverageBlocks { +impl<'graph> graph::GraphSuccessors<'graph> for BasicCoverageBlocks { type Item = BasicCoverageBlock; - type Iter = std::iter::Cloned>; + type Iter = std::iter::Cloned>; } -// `BasicBlock` `Predecessors` uses a `SmallVec` of length 4 because, "Typically 95%+ of basic -// blocks have 4 or fewer predecessors." BasicCoverageBlocks should have the same or less. -type BcbPredecessors = SmallVec<[BasicCoverageBlock; 4]>; +type BcbPredecessors = Vec; impl graph::GraphPredecessors<'graph> for BasicCoverageBlocks { type Item = BasicCoverageBlock; - type Iter = smallvec::IntoIter<[BasicCoverageBlock; 4]>; + type Iter = std::vec::IntoIter; } impl graph::WithPredecessors for BasicCoverageBlocks { @@ -644,6 +776,31 @@ impl CoverageSpan { pub fn is_in_same_bcb(&self, other: &Self) -> bool { self.bcb == other.bcb } + + pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String { + format!( + "{}\n {}", + spanview::source_range_no_file(tcx, &self.span), + self.format_coverage_statements(tcx, mir_body).replace("\n", "\n "), + ) + } + + pub fn format_coverage_statements( + &self, + tcx: TyCtxt<'tcx>, + mir_body: &'a mir::Body<'tcx>, + ) -> String { + let mut sorted_coverage_statements = self.coverage_statements.clone(); + sorted_coverage_statements.sort_unstable_by_key(|covstmt| match *covstmt { + CoverageStatement::Statement(bb, _, index) => (bb, index), + CoverageStatement::Terminator(bb, _) => (bb, usize::MAX), + }); + sorted_coverage_statements + .iter() + .map(|covstmt| covstmt.format(tcx, mir_body)) + .collect::>() + .join("\n") + } } /// Maintains separate worklists for each loop in the BasicCoverageBlock CFG, plus one for the @@ -651,16 +808,91 @@ impl CoverageSpan { /// ensures a loop is completely traversed before processing Blocks after the end of the loop. #[derive(Debug)] struct TraversalContext { - /// the start (backedge target) of a loop. If `None`, the context is all - /// BasicCoverageBlocks in the MIR that are _not_ within any loop. - loop_header: Option, + /// From one or more backedges returning to a loop header. + loop_backedges: Option<(Vec, BasicCoverageBlock)>, - /// worklist, to be traversed, of BasicCoverageBlocks in the loop headed by - /// `loop_header`, such that the loop is the inner inner-most loop containing these + /// worklist, to be traversed, of BasicCoverageBlocks in the loop with the given loop + /// backedges, such that the loop is the inner inner-most loop containing these /// BasicCoverageBlocks worklist: Vec, } +struct DebugCounters { + some_expressions: Option>, +} + +impl DebugCounters { + pub fn new() -> Self { + Self { + some_expressions: None, + } + } + + pub fn enable(&mut self) { + self.some_expressions.replace(FxHashMap::default()); + } + + pub fn is_enabled(&mut self) -> bool { + self.some_expressions.is_some() + } + + pub fn add_expression(&mut self, expression: &CoverageKind, debug_string: String) { + if let Some(expressions) = &mut self.some_expressions { + if let CoverageKind::Expression { id, .. } = *expression { + expressions.insert(id.into(), (expression.clone(), debug_string)).expect_none("attempt to add the same expression to DebugCounters more than once"); + } else { + bug!("the given `CoverageKind` is not an expression: {:?}", expression); + } + } + } + + pub fn debug_string(&self, operand: ExpressionOperandId) -> String { + if let Some(expressions) = &self.some_expressions { + if let Some((_, debug_string)) = expressions.get(&operand) { + return debug_string.clone() + } + } + String::new() + } + + pub fn format_counter(&self, counter_kind: &CoverageKind) -> String { + match *counter_kind { + CoverageKind::Counter { id, .. } => format!("Counter(#{})", id.index()), + CoverageKind::Expression { .. } => format!("Expression({})", self.format_expression(counter_kind)), + CoverageKind::Unreachable { .. } => "Unreachable".to_owned(), + } + } + + fn format_expression(&self, expression: &CoverageKind) -> String { + if let CoverageKind::Expression { lhs, op, rhs, .. } = *expression { + format!( + "{} {} {}", + self.format_operand(lhs), + if op == Op::Add { "+" } else { "-" }, + self.format_operand(rhs), + ) + } else { + bug!("format_expression called with {:?}", expression); + } + } + + fn format_operand(&self, operand: ExpressionOperandId) -> String { + if operand.index() == 0 { + return String::from("0"); + } + if let Some(expressions) = &self.some_expressions { + if let Some((expression, debug_string)) = expressions.get(&operand) { + if debug_string.is_empty() { + return format!("({})", self.format_expression(expression)); + } else { + return format!("{}:({})", debug_string, self.format_expression(expression)); + } + } + } + format!("#{}", operand.index().to_string()) + } +} + struct Instrumentor<'a, 'tcx> { pass_name: &'a str, tcx: TyCtxt<'tcx>, @@ -671,6 +903,8 @@ struct Instrumentor<'a, 'tcx> { function_source_hash: Option, next_counter_id: u32, num_expressions: u32, + expressions_cache: FxHashMap, + debug_counters: DebugCounters, } impl<'a, 'tcx> Instrumentor<'a, 'tcx> { @@ -688,6 +922,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { function_source_hash: None, next_counter_id: CounterValueReference::START.as_u32(), num_expressions: 0, + expressions_cache: FxHashMap::default(), + debug_counters: DebugCounters::new(), } } @@ -731,15 +967,44 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { debug!("instrumenting {:?}, span: {}", def_id, source_map.span_to_string(body_span)); + let dump_spanview = pretty::dump_enabled(tcx, self.pass_name, def_id); + let dump_graphviz = tcx.sess.opts.debugging_opts.dump_mir_graphviz; + + if dump_graphviz { + self.debug_counters.enable(); + } + let coverage_spans = self.coverage_spans(); - let span_viewables = if pretty::dump_enabled(tcx, self.pass_name, def_id) { - Some(self.span_viewables(&coverage_spans)) - } else { - None - }; + let span_viewables = + if dump_spanview { Some(self.span_viewables(&coverage_spans)) } else { None }; - self.make_counters(); + let mut collect_intermediate_expressions = Vec::with_capacity(self.basic_coverage_blocks.num_nodes()); + + self.make_bcb_counters(&mut collect_intermediate_expressions); + + let mut debug_bcb_to_coverage_spans_with_counters = None; + let mut debug_bcb_to_dependency_counter = None; + let mut debug_edge_to_counter = None; + if dump_graphviz { + debug_bcb_to_coverage_spans_with_counters = Some(FxHashMap::default()); + debug_bcb_to_dependency_counter = Some(FxHashMap::default()); + debug_edge_to_counter = Some(FxHashMap::default()); + } + + let mut debug_used_expression_operands = None; + if level_enabled!(tracing::Level::DEBUG) { + debug_used_expression_operands = Some(FxHashMap::default()); + } + + if let Some(used_expression_operands) = debug_used_expression_operands.as_mut() { + for intermediate_expression in &collect_intermediate_expressions { + if let CoverageKind::Expression { id, lhs, rhs, .. } = *intermediate_expression { + used_expression_operands.entry(lhs).or_insert_with(|| Vec::new()).push(id); + used_expression_operands.entry(rhs).or_insert_with(|| Vec::new()).push(id); + } + } + } // Inject a counter for each `CoverageSpan`. There can be multiple `CoverageSpan`s for a // given BCB, but only one actual counter needs to be incremented per BCB. `bb_counters` @@ -747,25 +1012,233 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // for a BCB that already has a `Counter` will inject a `CounterExpression` instead, and // compute its value by adding `ZERO` to the BCB `Counter` value. let mut bcb_counters = IndexVec::from_elem_n(None, self.basic_coverage_blocks.num_nodes()); - for CoverageSpan { span, bcb, .. } in coverage_spans { + for covspan in coverage_spans { + let bcb = covspan.bcb; + let span = covspan.span; let counter_kind = if let Some(&counter_operand) = bcb_counters[bcb].as_ref() { - self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO) + self.make_identity_counter(counter_operand) } else if let Some(counter_kind) = self.bcb_data_mut(bcb).take_counter() { bcb_counters[bcb] = Some(counter_kind.as_operand_id()); + if let Some(used_expression_operands) = debug_used_expression_operands.as_mut() { + if let CoverageKind::Expression { id, lhs, rhs, .. } = counter_kind { + used_expression_operands.entry(lhs).or_insert_with(|| Vec::new()).push(id); + used_expression_operands.entry(rhs).or_insert_with(|| Vec::new()).push(id); + } + } counter_kind } else { bug!("Every BasicCoverageBlock should have a Counter or CounterExpression"); }; // TODO(richkadel): uncomment debug! // debug!( - // "Injecting {:?} at: {:?}:\n{}\n==========", - // counter_kind, + // "Injecting {} at: {:?}:\n{}\n==========", + // self.format_counter(counter_kind), // span, // source_map.span_to_snippet(span).expect("Error getting source for span"), // ); - self.inject_statement(file_name, &source_file, counter_kind, span, bcb); + if let Some(bcb_to_coverage_spans_with_counters) = debug_bcb_to_coverage_spans_with_counters.as_mut() { + bcb_to_coverage_spans_with_counters.entry(bcb).or_insert_with(|| Vec::new()).push((covspan.clone(), counter_kind.clone())); + } + self.inject_statement(counter_kind, self.bcb_last_bb(bcb), make_code_region(file_name, &source_file, span)); + } + + // The previous step looped through the `CoverageSpan`s and injected the counter from the + // `CoverageSpan`s `BasicCoverageBlock`, removing it from the BCB in the process (via + // `take_counter()`). + // + // Any other counter associated with a `BasicCoverageBlock`, or its incoming edge, but not + // associated with a `CoverageSpan`, should only exist if the counter is a + // `CounterExpression` dependency (one of the expression operands). Collect them, and inject + // the additional counters into the MIR, without a reportable coverage span. + let mut bcb_counters_without_direct_coverage_spans = Vec::new(); + for (target_bcb, target_bcb_data) in self.basic_coverage_blocks.iter_enumerated_mut() { + if let Some(counter_kind) = target_bcb_data.take_counter() { + bcb_counters_without_direct_coverage_spans.push((None, target_bcb, counter_kind)); + } + if let Some(edge_counters) = target_bcb_data.take_edge_counters() { + for (from_bcb, counter_kind) in edge_counters { + bcb_counters_without_direct_coverage_spans.push((Some(from_bcb), target_bcb, counter_kind)); + } + } + } + + // Validate that every BCB or edge counter not directly associated with a coverage span is + // at least indirectly associated (it is a dependency of a BCB counter that _is_ associated + // with a coverage span). + if let Some(used_expression_operands) = debug_used_expression_operands.as_mut() { + let mut not_validated = bcb_counters_without_direct_coverage_spans.iter().map(|(_, _, counter_kind)| counter_kind).collect::>(); + let mut validating_count = 0; + while not_validated.len() != validating_count { + let to_validate = not_validated.split_off(0); + validating_count = to_validate.len(); + for counter_kind in to_validate { + if used_expression_operands.contains_key(&counter_kind.as_operand_id()) { + if let CoverageKind::Expression { id, lhs, rhs, .. } = *counter_kind { + used_expression_operands.entry(lhs).or_insert_with(|| Vec::new()).push(id); + used_expression_operands.entry(rhs).or_insert_with(|| Vec::new()).push(id); + } + } else { + not_validated.push(counter_kind); + } + } + } + } + + for (edge_counter_from_bcb, target_bcb, counter_kind) in bcb_counters_without_direct_coverage_spans { + if let Some(used_expression_operands) = debug_used_expression_operands.as_ref() { + if !used_expression_operands.contains_key(&counter_kind.as_operand_id()) { + // These unused counters may happen for expressions replaced during expression + // simplification. For example, expression: x + (y - x) becomes just "y", and the + // expression: y - x may no longer be a dependency. Similarly, expression: + // x + (y + 0) becomes just x + y. + if let Some(from_bcb) = edge_counter_from_bcb.as_ref() { + debug!( + "non-coverage edge counter found without a dependent expression, in {:?}->{:?}; counter={}", + from_bcb, + target_bcb, + self.format_counter(&counter_kind), + ); + } else { + debug!( + "non-coverage counter found without a dependent expression, in {:?}; counter={}", + target_bcb, + self.format_counter(&counter_kind), + ); + } + } + } + + let inject_to_bb = if let Some(from_bcb) = edge_counter_from_bcb { + // The MIR edge starts `from_bb` (the outgoing / last BasicBlock in `from_bcb`) and + // ends at `to_bb` (the incoming / first BasicBlock in the `target_bcb`; also called + // the `leader_bb`). + let from_bb = self.bcb_last_bb(from_bcb); + let to_bb = self.bcb_leader_bb(target_bcb); + + debug!( + "Edge {:?} (last {:?}) -> {:?} (leader {:?}) requires a new MIR BasicBlock, for unclaimed edge counter {}", + edge_counter_from_bcb, from_bb, target_bcb, to_bb, self.format_counter(&counter_kind), + ); + debug!( + " from_bb {:?} has successors: {:?}", + from_bb, self.mir_body[from_bb].terminator().successors(), + ); + let span = self.mir_body[from_bb].terminator().source_info.span.shrink_to_hi(); + let new_bb = self.mir_body.basic_blocks_mut().push(BasicBlockData { + statements: vec![], // counter will be injected here + terminator: Some(Terminator { + source_info: SourceInfo::outermost(span), + kind: TerminatorKind::Goto { target: to_bb }, + }), + is_cleanup: false, + }); + let edge_ref = self.mir_body[from_bb].terminator_mut().successors_mut().find(|successor| **successor == to_bb).expect("from_bb should have a successor for to_bb"); + *edge_ref = new_bb; + + if let Some(edge_to_counter) = debug_edge_to_counter.as_mut() { + debug!("from_bcb={:?} to new_bb={:?} has edge_counter={}", + from_bcb, new_bb, self.format_counter(&counter_kind)); + edge_to_counter.insert((from_bcb, new_bb), counter_kind.clone()).expect_none("invalid attempt to insert more than one edge counter for the same edge"); + } + + new_bb + } else { + if let Some(bcb_to_dependency_counter) = debug_bcb_to_dependency_counter.as_mut() { + bcb_to_dependency_counter.entry(target_bcb).or_insert_with(|| Vec::new()).push(counter_kind.clone()); + } + let target_bb = self.bcb_last_bb(target_bcb); + debug!( + "{:?} ({:?}) gets a new Coverage statement for unclaimed counter {}", + target_bcb, + target_bb, + self.format_counter(&counter_kind), + ); + target_bb + }; + self.inject_statement(counter_kind, inject_to_bb, make_non_reportable_code_region(file_name, &source_file)); + } + + if dump_graphviz { + let bcb_to_coverage_spans_with_counters = debug_bcb_to_coverage_spans_with_counters.expect("graphviz data should exist if dump_graphviz is true"); + let bcb_to_dependency_counter = debug_bcb_to_dependency_counter.expect("graphviz data should exist if dump_graphviz is true"); + let edge_to_counter = debug_edge_to_counter.expect("graphviz data should exist if dump_graphviz is true"); + let graphviz_name = format!("Cov_{}_{}", def_id.krate.index(), def_id.index.index()); + let node_content = |bcb| { + self.bcb_data(bcb).to_string_sections( + tcx, + self.mir_body, + &self.debug_counters, + bcb_to_coverage_spans_with_counters.get(&bcb), + bcb_to_dependency_counter.get(&bcb), + // collect_intermediate_expressions are injected into the mir::START_BLOCK, so include + // them in the first BCB. + if bcb.index() == 0 { Some(&collect_intermediate_expressions) } else { None } + ) + }; + let edge_labels = |from_bcb| { + let from_terminator = self.bcb_terminator(from_bcb); + let edge_labels = from_terminator.kind.fmt_successor_labels(); + let edge_counters = from_terminator.successors().map(|&successor| { + edge_to_counter.get(&(from_bcb, successor)) + }); + edge_labels.iter().zip(edge_counters).map(|(label, some_counter)| { + if let Some(counter) = some_counter { + format!("{}\n{}", label, self.format_counter(counter)) + } else { + label.to_string() + } + }).collect::>() + }; + let graphviz_writer = GraphvizWriter::new( + &self.basic_coverage_blocks, + &graphviz_name, + node_content, + edge_labels, + ); + // If there is any additional information to show, not related to a specific node or + // edge, add it with: + // graphviz_writer.set_graph_label(""); + let mut file = + pretty::create_dump_file(tcx, "dot", None, self.pass_name, &0, mir_source) + .expect("Unexpected error creating BasicCoverageBlock graphviz DOT file"); + graphviz_writer + .write_graphviz(tcx, &mut file) + .expect("Unexpected error writing BasicCoverageBlock graphviz DOT file"); + } + + for intermediate_expression in collect_intermediate_expressions { + self.inject_intermediate_expression(intermediate_expression); } + // TODO(richkadel): + // + // 1. "try" coverage isn't quite right. See `bcb14` in try_error_result.rs. + // The `?` character should have its own coverage span (for bcb14, which is otherwise + // uncovered) indicating when the error result occurred, and the `main()` function + // returned `Err`. It looks like it's because the SwitchInt block includes an Assign + // with the exact same span as the `?` span, and since it dominates the `Err` handling + // code for `?`, it got the span. This seems wrong here, but the `dominator gets equal spans` + // logic was there for the match guard handling. + // + // Maybe I really should reverse that match guard handling. I know it looks weird, but actually + // it may be right. The assignment of the value to the guard variable is (maybe?) to make + // it available to the arm code? Well, actually, not (only) in that case since I use it for + // the comparison. But then, why doesn't the comparison get it? (Or does it? Maybe it would + // get it and that's why it would be counted twice!) + // + // I need to try it again. + // 2. SwitchInt branches should exclude branches to unreachable blocks. + // 3. BCB's without CoverageSpans may (??!!??) not need counters unless they are part + // of another expression. With the current algorithm, I think the only way they can + // be part of an expression is if they are a SwitchInt or a target of a SwitchInt, + // AND I think the SwitchInt counter logic may ensure those counters are added, + // so we can probably just not add the counters during traversal if there are no + // coverage spans. + // 4. If a BCB gets a counter (because it's a SwitchInt or target of SwitchInt), and + // there is no CoverageSpan for that BCB, we still must `inject_statement` the + // counter, ... but with what coverage span??? Maybe an empty span at the terminator's + // span.lo()? + if let Some(span_viewables) = span_viewables { let mut file = pretty::create_dump_file(tcx, "html", None, self.pass_name, &0, mir_source) @@ -789,18 +1262,28 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { /// One way to predict which branch executes the least is by considering loops. A loop is exited /// at a branch, so the branch that jumps to a `BasicCoverageBlock` outside the loop is almost /// always executed less than the branch that does not exit the loop. - fn make_counters(&mut self) { - debug!( - "make_counters(): adding a counter or expression to each BasicCoverageBlock.\n ... First identify any loops by their backedges:" + /// + /// Returns non-code-span expressions created to represent intermediate values (if required), + /// such as to add two counters so the result can be subtracted from another counter. + fn make_bcb_counters(&mut self, collect_intermediate_expressions: &mut Vec) { + debug!("make_bcb_counters(): adding a counter or expression to each BasicCoverageBlock"); + let num_bcbs = self.basic_coverage_blocks.num_nodes(); + let mut backedges = IndexVec::from_elem_n( + Vec::::new(), + num_bcbs, ); - let mut loop_headers = BitSet::new_empty(self.basic_coverage_blocks.num_nodes()); - // Identify backedges + // Identify loops by their backedges for (bcb, _) in self.basic_coverage_blocks.iter_enumerated() { for &successor in &self.basic_coverage_blocks.successors[bcb] { if self.bcb_is_dominated_by(bcb, successor) { - debug!("Found BCB backedge: {:?} -> loop_header: {:?}", bcb, successor); - loop_headers.insert(successor); + let loop_header = successor; + let backedge_from_bcb = bcb; + debug!( + "Found BCB backedge: {:?} -> loop_header: {:?}", + backedge_from_bcb, loop_header + ); + backedges[loop_header].push(backedge_from_bcb); } } } @@ -812,29 +1295,28 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // of the stack as loops are entered, and popped off of the stack when a loop's worklist is // exhausted. let mut context_stack = Vec::new(); - context_stack.push(TraversalContext { loop_header: None, worklist: vec![start_bcb] }); - let mut visited = BitSet::new_empty(self.basic_coverage_blocks.num_nodes()); + context_stack.push(TraversalContext { loop_backedges: None, worklist: vec![start_bcb] }); + let mut visited = BitSet::new_empty(num_bcbs); while let Some(bcb) = { // Strip contexts with empty worklists from the top of the stack - while context_stack - .last() - .map_or(false, |context| context.worklist.is_empty()) - { + while context_stack.last().map_or(false, |context| context.worklist.is_empty()) { context_stack.pop(); } + // Pop the next bcb off of the current context_stack. If none, all BCBs were visited. context_stack.last_mut().map_or(None, |context| context.worklist.pop()) - } - { + } { if !visited.insert(bcb) { debug!("Already visited: {:?}", bcb); continue; } debug!("Visiting {:?}", bcb); - if loop_headers.contains(bcb) { + if backedges[bcb].len() > 0 { debug!("{:?} is a loop header! Start a new TraversalContext...", bcb); - context_stack - .push(TraversalContext { loop_header: Some(bcb), worklist: Vec::new() }); + context_stack.push(TraversalContext { + loop_backedges: Some((backedges[bcb].clone(), bcb)), + worklist: Vec::new(), + }); } debug!( @@ -844,81 +1326,90 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { ); for &successor in &self.basic_coverage_blocks.successors[bcb] { for context in context_stack.iter_mut().rev() { - if let Some(loop_header) = context.loop_header { + if let Some((_, loop_header)) = context.loop_backedges { if self.bcb_is_dominated_by(successor, loop_header) { - debug!( - "Adding successor {:?} to worklist of loop headed by {:?}", - successor, loop_header - ); - context.worklist.push(successor); + if self.bcb_successors(successor).len() > 1 { + debug!( + "Adding branching successor {:?} to the beginning of the worklist of loop headed by {:?}", + successor, loop_header + ); + context.worklist.insert(0, successor); + } else { + debug!( + "Adding non-branching successor {:?} to the end of the worklist of loop headed by {:?}", + successor, loop_header + ); + context.worklist.push(successor); + } break; } } else { - debug!("Adding successor {:?} to non-loop worklist", successor); - context.worklist.push(successor); + if self.bcb_successors(successor).len() > 1 { + debug!("Adding branching successor {:?} to the beginning of the non-loop worklist", successor); + context.worklist.insert(0, successor); + } else { + debug!("Adding non-branching successor {:?} to the end of the non-loop worklist", successor); + context.worklist.push(successor); + } } } } - let bcb_counter_operand = if let Some(counter) = self.bcb_data(bcb).counter() { - debug!("{:?} already has a counter: {:?}", bcb, counter); - counter.as_operand_id() - } else { - let counter = self.make_counter(); - debug!("{:?} needs a counter: {:?}", bcb, counter); - let operand = counter.as_operand_id(); - self.bcb_data_mut(bcb).set_counter(counter); - operand - }; + let bcb_counter_operand = self.get_or_make_counter_operand(bcb, collect_intermediate_expressions); - let targets = match &self.bcb_data(bcb).terminator(self.mir_body) { - TerminatorKind::SwitchInt { targets, .. } => targets.clone(), - _ => vec![], + let needs_branch_counters = { + let successors = self.bcb_successors(bcb); + successors.len() > 1 && successors.iter().any(|&successor| self.bcb_data(successor).counter().is_none()) }; - if targets.len() > 0 { + if needs_branch_counters { + let branching_bcb = bcb; + let branching_counter_operand = bcb_counter_operand; + let branches = self.bcb_successors(branching_bcb).clone(); + debug!( - "{:?}'s terminator is a SwitchInt with targets: {:?}", - bcb, - targets.iter().map(|bb| self.bcb_from_bb(*bb)).collect::>() + "{:?} is branching, with branches: {:?}", + branching_bcb, + branches ); - let switch_int_counter_operand = bcb_counter_operand; - // Only one target can have an expression, but if `found_loop_exit`, any - // `in_loop_target` can get the `CounterExpression`. - let mut some_in_loop_target = None; + // At most one of the branches (or its edge, from the branching_bcb, + // if the branch has multiple incoming edges) can have a counter computed by + // expression. + // + // If at least one of the branches leads outside of a loop (`found_loop_exit` is + // true), and at least one other branch does not exit the loop (the first of which + // is captured in `some_reloop_branch`), it's likely any reloop branch will be + // executed far more often than loop exit branch, making the reloop branch a better + // candidate for an expression. + let mut some_reloop_branch = None; for context in context_stack.iter().rev() { - if let Some(loop_header) = context.loop_header { + if let Some((backedge_from_bcbs, _)) = &context.loop_backedges { let mut found_loop_exit = false; - for &bb in &targets { - let target_bcb = self.bcb_from_bb(bb); -// TODO(richkadel): But IF... - if self.bcb_is_dominated_by(target_bcb, loop_header) { -// the target or any non-branching BCB successor down the line -// exits or is a TerminatorKind::Return, or something similar, -// then this should be "found_loop_exit" instead of some_in_loop_target - - -// WHAT IF instead of checking target_bcb is dominated by loop header, -// we check something like, if backedge start (for all backedges leading to loop header?) -// is dominated by target_bcb? -// AND CAN THERE BE MORE THAN ONE BACKEDGE? I THINK MAYBE... like "continue loop_label;" - -// Will that work better? -// YES I THINK SO... IT SAYS, "target_bcb" leads out of the loop or it doesn't. - - some_in_loop_target = Some(target_bcb); + for &branch in branches.iter() { + if backedge_from_bcbs.iter().any(|&backedge_from_bcb| { + self.bcb_is_dominated_by(backedge_from_bcb, branch) + }) { + // The path from branch leads back to the top of the loop + some_reloop_branch = Some(branch); } else { + // The path from branch leads outside this loop found_loop_exit = true; } - if some_in_loop_target.is_some() && found_loop_exit { + if some_reloop_branch.is_some() && found_loop_exit { break; } } debug!( - "found_loop_exit={}, some_in_loop_target={:?}", - found_loop_exit, some_in_loop_target + "found_loop_exit={}, some_reloop_branch={:?}", + found_loop_exit, some_reloop_branch ); - if !(found_loop_exit && some_in_loop_target.is_none()) { + if !found_loop_exit { + // No branches exit a loop, so there is no specific recommended branch for + // an `ExpressionCounter`. + break; + } + if some_reloop_branch.is_some() { + // A recommended branch for an `ExpressionCounter` was found. break; } // else all branches exited this loop context, so run the same checks with @@ -926,66 +1417,106 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } } - // If some preferred target for a CounterExpression was not determined, pick any - // target. - let expression_target = if let Some(in_loop_target) = some_in_loop_target { - debug!("Adding expression to in_loop_target={:?}", some_in_loop_target); - in_loop_target + // Select a branch for the expression, either the recommended `reloop_branch`, or + // if none was found, select any branch. + let expression_branch = if let Some(reloop_branch) = some_reloop_branch { + debug!("Adding expression to reloop_branch={:?}", reloop_branch); + reloop_branch } else { - let bb_without_counter = *targets + let &branch_without_counter = branches .iter() - .find(|&&bb| { - let target_bcb = self.bcb_from_bb(bb); - self.bcb_data_mut(target_bcb).counter().is_none() + .find(|&&branch| { + self.bcb_data(branch).counter().is_none() }) - .expect("At least one target should need a counter"); + .expect("needs_branch_counters was `true` so there should be at least one branch"); debug!( - "No preferred expression target, so adding expression to the first target without an existing counter={:?}", - self.bcb_from_bb(bb_without_counter) + "No preferred expression branch. Selected the first branch without a counter. That branch={:?}", + branch_without_counter ); - self.bcb_from_bb(bb_without_counter) + branch_without_counter }; - // Assign a Counter or CounterExpression to each target BasicCoverageBlock, - // computing intermediate CounterExpression as needed. - let mut some_prev_counter_operand = None; - for bb in targets { - let target_bcb = self.bcb_from_bb(bb); - if target_bcb != expression_target { - // TODO(richkadel): this let if let else block is repeated above. Refactor into function. - let target_counter_operand = - if let Some(counter) = self.bcb_data(target_bcb).counter() { - debug!("{:?} already has a counter: {:?}", target_bcb, counter); - counter.as_operand_id() + // Assign a Counter or CounterExpression to each branch, plus additional + // `CounterExpression`s, as needed, to sum up intermediate results. + let mut some_sumup_counter_operand = None; + for branch in branches { + if branch != expression_branch { + let branch_counter_operand = + if self.bcb_has_multiple_incoming_edges(branch) { + debug!("{:?} has multiple incoming edges, so adding an edge counter from {:?}", branch, branching_bcb); + self.get_or_make_edge_counter_operand(branching_bcb, branch, collect_intermediate_expressions) } else { - let counter = self.make_counter(); - debug!("{:?} gets a counter: {:?}", target_bcb, counter); - let operand = counter.as_operand_id(); - self.bcb_data_mut(target_bcb).set_counter(counter); - operand + debug!("{:?} has only one incoming edge (from {:?}), so adding a counter", branch, branching_bcb); +// TODO(richkadel): IS THIS FOR LOOP DUPLICATING WHAT'S IN get_or_make_counter_operand? + self.get_or_make_counter_operand(branch, collect_intermediate_expressions) }; - if let Some(prev_counter_operand) = - some_prev_counter_operand.replace(target_counter_operand) + if let Some(sumup_counter_operand) = + some_sumup_counter_operand.replace(branch_counter_operand) { - let expression = self.make_expression( - prev_counter_operand, + let intermediate_expression = self.make_expression( + branch_counter_operand, Op::Add, - target_counter_operand, + sumup_counter_operand, + || String::new(), ); - debug!("new non-code expression: {:?}", expression); - let expression_operand = expression.as_operand_id(); - self.inject_non_code_expression(expression); - some_prev_counter_operand.replace(expression_operand); + debug!(" new intermediate expression: {}", self.format_counter(&intermediate_expression)); + let intermediate_expression_operand = intermediate_expression.as_operand_id(); + collect_intermediate_expressions.push(intermediate_expression); + some_sumup_counter_operand.replace(intermediate_expression_operand); } } } + let sumup_counter_operand = some_sumup_counter_operand.expect("sumup_counter_operand should have a value"); + let multiple_incoming_edges = self.bcb_has_multiple_incoming_edges(expression_branch); + debug!("expression_branch is {:?}, multiple_incoming_edges={}, expression_branch predecessors: {:?}", + expression_branch, multiple_incoming_edges, self.bcb_predecessors(expression_branch)); let expression = self.make_expression( - switch_int_counter_operand, + branching_counter_operand, Op::Subtract, - some_prev_counter_operand.expect("prev_counter should have a value"), + sumup_counter_operand, + || { + if multiple_incoming_edges { + format!("{:?}->{:?}", branching_bcb, expression_branch) + } else { + format!("{:?}", expression_branch) + } + } ); - debug!("{:?} gets an expression: {:?}", expression_target, expression); - self.bcb_data_mut(expression_target).set_counter(expression); + if multiple_incoming_edges { + debug!("Edge {:?}->{:?} gets an expression: {}", branching_bcb, expression_branch, self.format_counter(&expression)); + self.bcb_data_mut(expression_branch).set_edge_counter_from(branching_bcb, expression); + } else { + debug!("{:?} gets an expression: {}", expression_branch, self.format_counter(&expression)); + self.bcb_data_mut(expression_branch).set_counter(expression); + } + +// TODO(richkadel): +// Who would use this edge counter? +// Since this isn't the bcb counter, what will the bcb counter be? +// (I think the bcb counter will be the sum of all edge counters) +// +// Does this mean that we should assert that a bcb ONLY has either a single bcb counter, or 2 or more edge counters, but not both? +// WELL... not exactly. +// 1. A BCB with edge counters always has an ExpressionCounter +// 2. A BCB with multiple predecessors always gets an ExpressionCounter. +// +// ??? +// 3. If NO predecessor comes from a branching BCB, then the BCB Expression can be the sum of all predecessor BCB counters +// ??? +// +// 4. If (any?) predecessor comes from a branching BCB, assume the incoming edges will +// ?? all ?? +// need edge counters, and the BCB Expression is the sum of all incoming edge counters. +// +// THIS (#4) DOESN'T SOUND RIGHT +// +// 5. An incoming edge from a source BCB (from_bcb) with only one successor (so, from_bcb is not a branching BCB) does not +// need a separate outgoing edge counter. In this case, when summing up incoming edge counts for the successor BCB, for +// each edge, use the incoming edge counter if present, or use the counter from the edge SOURCE (and assert the source has +// only one successor) + + + } } @@ -993,8 +1524,23 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } #[inline] - fn bcb_from_bb(&self, bb: BasicBlock) -> BasicCoverageBlock { - self.basic_coverage_blocks.bcb_from_bb(bb) + fn format_counter(&self, counter_kind: &CoverageKind) -> String { + self.debug_counters.format_counter(counter_kind) + } + + #[inline] + fn bcb_leader_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock { + self.bcb_data(bcb).leader_bb() + } + + #[inline] + fn bcb_last_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock { + self.bcb_data(bcb).last_bb() + } + + #[inline] + fn bcb_terminator(&self, bcb: BasicCoverageBlock) -> &Terminator<'tcx> { + self.bcb_data(bcb).terminator(self.mir_body) } #[inline] @@ -1007,35 +1553,108 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { &mut self.basic_coverage_blocks[bcb] } + #[inline] + fn bcb_predecessors(&self, bcb: BasicCoverageBlock) -> &BcbPredecessors { + &self.basic_coverage_blocks.predecessors[bcb] + } + + #[inline] + fn bcb_successors(&self, bcb: BasicCoverageBlock) -> &Vec { + &self.basic_coverage_blocks.successors[bcb] + } + + #[inline] + fn bcb_has_multiple_incoming_edges(&self, bcb: BasicCoverageBlock) -> bool { + self.bcb_predecessors(bcb).len() > 1 + } + #[inline] fn bcb_is_dominated_by(&self, node: BasicCoverageBlock, dom: BasicCoverageBlock) -> bool { self.bcb_dominators.is_dominated_by(node, dom) } + fn get_or_make_counter_operand(&mut self, bcb: BasicCoverageBlock, collect_intermediate_expressions: &mut Vec) -> ExpressionOperandId { + if let Some(counter_kind) = self.basic_coverage_blocks[bcb].counter() { + debug!(" {:?} already has a counter: {}", bcb, self.format_counter(counter_kind)); + counter_kind.as_operand_id() + } else { + if self.bcb_has_multiple_incoming_edges(bcb) { + let mut predecessors = self.bcb_predecessors(bcb).clone().into_iter(); + let first_edge_counter_operand = self.get_or_make_edge_counter_operand(predecessors.next().unwrap(), bcb, collect_intermediate_expressions); + let mut some_sumup_edge_counter_operand = None; + for predecessor in predecessors { + let edge_counter_operand = self.get_or_make_edge_counter_operand(predecessor, bcb, collect_intermediate_expressions); + if let Some(sumup_edge_counter_operand) = + some_sumup_edge_counter_operand.replace(edge_counter_operand) + { + let intermediate_expression = self.make_expression( + sumup_edge_counter_operand, + Op::Add, + edge_counter_operand, + || String::new(), + ); + debug!(" new intermediate expression: {}", self.format_counter(&intermediate_expression)); + let intermediate_expression_operand = intermediate_expression.as_operand_id(); + collect_intermediate_expressions.push(intermediate_expression); + some_sumup_edge_counter_operand.replace(intermediate_expression_operand); + } + } + let counter_kind = self.make_expression( + first_edge_counter_operand, + Op::Add, + some_sumup_edge_counter_operand.unwrap(), + || format!("{:?}", bcb) + ); + debug!(" {:?} gets a new counter (sum of predecessor counters): {}", bcb, self.format_counter(&counter_kind)); + self.basic_coverage_blocks[bcb].set_counter(counter_kind) + } else { + let counter_kind = self.make_counter(); + debug!(" {:?} gets a new counter: {}", bcb, self.format_counter(&counter_kind)); + self.basic_coverage_blocks[bcb].set_counter(counter_kind) + } + } + } + + fn get_or_make_edge_counter_operand(&mut self, from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock, collect_intermediate_expressions: &mut Vec) -> ExpressionOperandId { + let successors = self.bcb_successors(from_bcb).iter(); + if successors.len() > 1 { + if let Some(counter_kind) = self.basic_coverage_blocks[to_bcb].edge_counter_from(from_bcb) { + debug!(" Edge {:?}->{:?} already has a counter: {}", from_bcb, to_bcb, self.format_counter(counter_kind)); + counter_kind.as_operand_id() + } else { + let counter_kind = self.make_counter(); + debug!(" Edge {:?}->{:?} gets a new counter: {}", from_bcb, to_bcb, self.format_counter(&counter_kind)); + self.basic_coverage_blocks[to_bcb].set_edge_counter_from(from_bcb, counter_kind) + } + } else { + self.get_or_make_counter_operand(from_bcb, collect_intermediate_expressions) + } + } + // loop through backedges - // select inner loops before their outer loops, so the first matched loop for a given target_bcb + // select inner loops before their outer loops, so the first matched loop for a given branch BCB // is it's inner-most loop // CASE #1: - // if a target_bcb is_dominated_by a loop bcb (target of backedge), and if any other target_bcb is NOT dominated by the loop bcb, - // add expression to the first target_bcb that dominated by the loop bcb, and counters to all others. Compute expressions from - // counter pairs as needed, to provide a single sum that can be subtracted from the SwitchInt block's counter. + // if a branch_bcb is_dominated_by a loop bcb (target of backedge), and if any other branch_bcb is NOT dominated by the loop bcb, + // add expression to the first branch_bcb that dominated by the loop bcb, and counters to all others. Compute expressions from + // counter pairs as needed, to provide a single sum that can be subtracted from the branching block's counter. // CASE #2: - // if all target_bcb are dominated_by the loop bcb, no branch ends the loop (directly), so pick any branch to have the expression, + // if all branch_bcb are dominated_by the loop bcb, no branch ends the loop (directly), so pick any branch to have the expression, // CASE #3: - // if NONE of the target_bcb are dominated_by the loop bcb, check if there's an outer loop (from stack of active loops?) + // if NONE of the branch_bcb are dominated_by the loop bcb, check if there's an outer loop (from stack of active loops?) // and re-do this check again to see if one of them jumps out of the outer loop while other(s) don't, and assign the expression - // to one of the target_bcb that is dominated_by that outer loop. (Continue this if none are dominated by the outer loop either.) + // to one of the branch_bcb that is dominated_by that outer loop. (Continue this if none are dominated by the outer loop either.) - // TODO(richkadel): In the last case above, also see the next TODO below. If all targets exit the loop then can we pass that info - // to the predecessor (if only one??) so if the predecessor is a target of another SwitchInt, we know that the predecessor exits - // the loop, and should have the counter, if the predecessor is in CASE #2 (none of the other targets of the predecessor's - // SwitchInt exited the loop?) + // TODO(richkadel): In the last case above, also see the next TODO below. If all branches exit the loop then can we pass that info + // to the predecessor (if only one??) so if the predecessor is a branch of another branching BCB, we know that the predecessor exits + // the loop, and should have the counter, if the predecessor is in CASE #2 (none of the other branches of the predecessor's + // branching BCB exited the loop?) - // TODO(richkadel): What about a target that is another SwitchInt where both branches exit the loop? + // TODO(richkadel): What about a branch BCB that is another branching BCB, where both branches exit the loop? // Can I detect that somehow? // TODO(richkadel): For any node, N, and one of its successors, H (so N -> H), if (_also_) @@ -1045,21 +1664,22 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // // H dominates everything inside the loop. // - // So a SwitchInt target in a BasicBlock that is_dominated_by H and has a branch target to a + // TODO(richkadel): This doesn't make as much sense after renaming some terms: + // So a branching BCB's branch in a BasicBlock that is_dominated_by H and has a branch to a // BasicBlock that is: - // * not H, and ... (what if the SwitchInt branch target _is_ H? `continue`? is this a + // * not H, and ... (what if the branching BCB successor _is_ H? `continue`? is this a // candidate for a middle or optional priority for getting a Counter?) // * not is_dominated_by H // is a branch that jumps outside the loop, and should get an actual Counter, most likely // - // Or perhaps conversely, a SwitchInt dominated by H with a branch that has a target that + // Or perhaps conversely, a branching BCB is dominated by H with a branch that has a target that // ALSO is dominated by H should get a CounterExpression. // // // So I need to identify all of the "H"'s, by identifying all of the backedges. // // If I have multiple H's (multiple loops), how do I decide which loop to compare a branch - // target (by dominator) to? + // BCB (by dominator) to? // // Can I assume the traversal order is helpful here? I.e., the "last" encountered loop // header is the (only?) one to compare to? (Probably not only... I don't see how that would @@ -1077,39 +1697,97 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } } - fn make_expression( + fn make_expression( &mut self, - lhs: ExpressionOperandId, + mut lhs: ExpressionOperandId, op: Op, - rhs: ExpressionOperandId, - ) -> CoverageKind { - CoverageKind::Expression { id: self.next_expression(), lhs, op, rhs } + mut rhs: ExpressionOperandId, + debug_string_fn: F, + ) -> CoverageKind + where F: Fn() -> String + { + if let Some(CoverageKind::Expression { lhs: lhs_lhs, op, rhs: lhs_rhs, .. } ) = self.expressions_cache.get(&lhs) { + if *lhs_rhs == ExpressionOperandId::ZERO { + lhs = *lhs_lhs; + } else if *op == Op::Subtract && *lhs_rhs == rhs { + if let Some(lhs_expression) = self.expressions_cache.get(lhs_lhs) { + let expression = lhs_expression.clone(); + return self.as_duplicate_expression(expression); + } else { + let counter = *lhs_lhs; + return self.make_identity_counter(counter); + } + } + } + + if let Some(CoverageKind::Expression { lhs: rhs_lhs, op, rhs: rhs_rhs, .. } ) = self.expressions_cache.get(&rhs) { + if *rhs_rhs == ExpressionOperandId::ZERO { + rhs = *rhs_rhs; + } else if *op == Op::Subtract && *rhs_rhs == lhs { + if let Some(rhs_expression) = self.expressions_cache.get(rhs_lhs) { + let expression = rhs_expression.clone(); + return self.as_duplicate_expression(expression); + } else { + let counter = *rhs_lhs; + return self.make_identity_counter(counter); + } + } + } + + let id = self.next_expression(); + let expression = CoverageKind::Expression { id, lhs, op, rhs }; + self.expressions_cache.insert(id.into(), expression.clone()); + if self.debug_counters.is_enabled() { + self.debug_counters.add_expression(&expression, (debug_string_fn)()); + } + expression + } + + fn as_duplicate_expression(&mut self, mut expression: CoverageKind) -> CoverageKind { + match expression { + CoverageKind::Expression { ref mut id, .. } => { + *id = self.next_expression(); + self.expressions_cache.insert(id.into(), expression.clone()); + } + _ => bug!("make_duplicate_expression called with non-expression type: {:?}", expression), + } + expression + } + + fn make_identity_counter(&mut self, counter_operand: ExpressionOperandId) -> CoverageKind { + if let Some(expression) = self.expressions_cache.get(&counter_operand) { + let new_expression = expression.clone(); + self.as_duplicate_expression(new_expression) + } else { + let debug_string = if self.debug_counters.is_enabled() { + self.debug_counters.debug_string(counter_operand) + } else { + String::new() + }; + self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO, || debug_string.clone()) + } } fn inject_statement( &mut self, - file_name: Symbol, - source_file: &Lrc, counter_kind: CoverageKind, - span: Span, - bcb: BasicCoverageBlock, + bb: BasicBlock, + code_region: CodeRegion, ) { - let code_region = Some(make_code_region(file_name, source_file, span)); // TODO(richkadel): uncomment debug! // debug!(" injecting statement {:?} covering {:?}", counter_kind, code_region); - let inject_in_bb = self.bcb_data(bcb).leader_bb(); - let data = &mut self.mir_body[inject_in_bb]; + let data = &mut self.mir_body[bb]; let source_info = data.terminator().source_info; let statement = Statement { source_info, - kind: StatementKind::Coverage(box Coverage { kind: counter_kind, code_region }), + kind: StatementKind::Coverage(box Coverage { kind: counter_kind, code_region: Some(code_region) }), }; data.statements.push(statement); } // Non-code expressions are injected into the coverage map, without generating executable code. - fn inject_non_code_expression(&mut self, expression: CoverageKind) { + fn inject_intermediate_expression(&mut self, expression: CoverageKind) { debug_assert!(if let CoverageKind::Expression { .. } = expression { true } else { false }); // TODO(richkadel): uncomment debug! // debug!(" injecting non-code expression {:?}", expression); @@ -1129,20 +1807,11 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { let tcx = self.tcx; let mut span_viewables = Vec::new(); for coverage_span in coverage_spans { - let CoverageSpan { span, bcb, coverage_statements, .. } = coverage_span; + let tooltip = coverage_span.format_coverage_statements(tcx, self.mir_body); + let CoverageSpan { span, bcb, .. } = coverage_span; let bcb_data = self.bcb_data(*bcb); let id = bcb_data.id(); let leader_bb = bcb_data.leader_bb(); - let mut sorted_coverage_statements = coverage_statements.clone(); - sorted_coverage_statements.sort_unstable_by_key(|covstmt| match *covstmt { - CoverageStatement::Statement(bb, _, index) => (bb, index), - CoverageStatement::Terminator(bb, _) => (bb, usize::MAX), - }); - let tooltip = sorted_coverage_statements - .iter() - .map(|covstmt| covstmt.format(tcx, self.mir_body)) - .collect::>() - .join("\n"); span_viewables.push(SpanViewable { bb: leader_bb, span: *span, id, tooltip }); } span_viewables @@ -1252,14 +1921,50 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } } +/// Converts the initial set of `CoverageSpan`s (one per MIR `Statement` or `Terminator`) into a +/// minimal set of `CoverageSpan`s, using the BCB CFG to determine where it is safe and useful to: +/// +/// * Remove duplicate source code coverage regions +/// * Merge spans that represent continuous (both in source code and control flow), non-branching +/// execution +/// * Carve out (leave uncovered) any span that will be counted by another MIR (notably, closures) struct CoverageSpanRefinery<'a> { + + /// The initial set of `CoverageSpan`s, sorted by `Span` (`lo` and `hi`) and by relative + /// dominance between the `BasicCoverageBlock`s of equal `Span`s. sorted_spans_iter: std::vec::IntoIter, + + /// The BCB CFG's dominators tree, used to compute the dominance relationships, if any. bcb_dominators: &'a Dominators, + + /// The current `CoverageSpan` to compare to its `prev`, to possibly merge, discard, force the + /// discard of the `prev` (and or `pending_dups`), or keep both (with `prev` moved to + /// `pending_dups`). If `curr` is not discarded or merged, it becomes `prev` for the next + /// iteration. some_curr: Option, + + /// The original `span` for `curr`, in case the `curr` span is modified. curr_original_span: Span, + + /// The CoverageSpan from a prior iteration; typically assigned from that iteration's `curr`. + /// If that `curr` was discarded, `prev` retains its value from the previous iteration. some_prev: Option, + + /// Assigned from `curr_original_span` from the previous iteration. prev_original_span: Span, + + /// One or more `CoverageSpan`s with the same `Span` but different `BasicCoverageBlock`s, and + /// no `BasicCoverageBlock` in this list dominates another `BasicCoverageBlock` in the list. + /// If a new `curr` span also fits this criteria (compared to an existing list of + /// `pending_dups`), that `curr` `CoverageSpan` moves to `prev` before possibly being added to + /// the `pending_dups` list, on the next iteration. As a result, if `prev` and `pending_dups` + /// have the same `Span`, the criteria for `pending_dups` holds for `prev` as well: a `prev` + /// with a matching `Span` does not dominate any `pending_dup` and no `pending_dup` dominates a + /// `prev` with a matching `Span`) pending_dups: Vec, + + /// The final `CoverageSpan`s to add to the coverage map. A `Counter` or `CounterExpression` + /// will also be injected into the MIR for each `CoverageSpan`. refined_spans: Vec, } @@ -1289,67 +1994,52 @@ impl<'a> CoverageSpanRefinery<'a> { fn to_refined_spans(mut self) -> Vec { while self.next_coverage_span() { if self.curr().is_mergeable(self.prev()) { - // TODO(richkadel): uncomment debug! - // debug!(" same bcb (and neither is a closure), merge with prev={:?}", self.prev()); + debug!(" same bcb (and neither is a closure), merge with prev={:?}", self.prev()); let prev = self.take_prev(); self.curr_mut().merge_from(prev); // Note that curr.span may now differ from curr_original_span } else if self.prev_ends_before_curr() { - // TODO(richkadel): uncomment debug! - // debug!( - // " different bcbs and disjoint spans, so keep curr for next iter, and add \ - // prev={:?}", - // self.prev() - // ); + debug!( + " different bcbs and disjoint spans, so keep curr for next iter, and add \ + prev={:?}", + self.prev() + ); let prev = self.take_prev(); - self.add_refined_span(prev); + self.refined_spans.push(prev); } else if self.prev().is_closure { // drop any equal or overlapping span (`curr`) and keep `prev` to test again in the // next iter - // TODO(richkadel): uncomment debug! - // debug!( - // " curr overlaps a closure (prev). Drop curr and keep prev for next iter. \ - // prev={:?}", - // self.prev() - // ); + debug!( + " curr overlaps a closure (prev). Drop curr and keep prev for next iter. \ + prev={:?}", + self.prev() + ); self.discard_curr(); } else if self.curr().is_closure { self.carve_out_span_for_closure(); } else if self.prev_original_span == self.curr().span { + // Note that this compares the new span to `prev_original_span`, which may not + // be the full `prev.span` (if merged during the previous iteration). self.hold_pending_dups_unless_dominated(); } else { self.cutoff_prev_at_overlapping_curr(); } } - // TODO(richkadel): uncomment debug! - // debug!(" AT END, adding last prev={:?}", self.prev()); - let pending_dups = self.pending_dups.split_off(0); - for dup in pending_dups.into_iter() { - // TODO(richkadel): uncomment debug! - // debug!(" ...adding at least one pending dup={:?}", dup); - self.add_refined_span(dup); - } - let prev = self.take_prev(); - self.add_refined_span(prev); - - // FIXME(richkadel): Replace some counters with expressions if they can be calculated based - // on branching. (For example, one branch of a SwitchInt can be computed from the counter - // for the CoverageSpan just prior to the SwitchInt minus the sum of the counters of all - // other branches). - - self.to_refined_spans_without_closures() - } - fn add_refined_span(&mut self, coverage_span: CoverageSpan) { - self.refined_spans.push(coverage_span); - } + debug!(" AT END, adding last prev={:?}", self.prev()); + let prev = self.take_prev(); + let CoverageSpanRefinery { pending_dups, mut refined_spans, .. } = self; + for dup in pending_dups { + debug!(" ...adding at least one pending dup={:?}", dup); + refined_spans.push(dup); + } + refined_spans.push(prev); - /// Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage - /// regions for the current function leave room for the closure's own coverage regions - /// (injected separately, from the closure's own MIR). - fn to_refined_spans_without_closures(mut self) -> Vec { - self.refined_spans.retain(|covspan| !covspan.is_closure); - self.refined_spans + // Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage + // regions for the current function leave room for the closure's own coverage regions + // (injected separately, from the closure's own MIR). + refined_spans.retain(|covspan| !covspan.is_closure); + refined_spans } fn curr(&self) -> &CoverageSpan { @@ -1383,25 +2073,24 @@ impl<'a> CoverageSpanRefinery<'a> { /// If there are `pending_dups` but `prev` is not a matching dup (`prev.span` doesn't match the /// `pending_dups` spans), then one of the following two things happened during the previous /// iteration: - /// * the `span` of prev was modified (by `curr_mut().merge_from(prev)`); or - /// * the `span` of prev advanced past the end of the span of pending_dups - /// (`prev().span.hi() <= curr().span.lo()`) + /// * the previous `curr` span (which is now `prev`) was not a duplicate of the pending_dups + /// (in which case there should be at least two spans in `pending_dups`); or + /// * the `span` of `prev` was modified by `curr_mut().merge_from(prev)` (in which case + /// `pending_dups` could have as few as one span) /// In either case, no more spans will match the span of `pending_dups`, so /// add the `pending_dups` if they don't overlap `curr`, and clear the list. fn check_pending_dups(&mut self) { if let Some(dup) = self.pending_dups.last() { if dup.span != self.prev().span { - // TODO(richkadel): uncomment debug! - // debug!( - // " SAME spans, but pending_dups are NOT THE SAME, so BCBs matched on \ - // previous iteration, or prev started a new disjoint span" - // ); + debug!( + " SAME spans, but pending_dups are NOT THE SAME, so BCBs matched on \ + previous iteration, or prev started a new disjoint span" + ); if dup.span.hi() <= self.curr().span.lo() { let pending_dups = self.pending_dups.split_off(0); for dup in pending_dups.into_iter() { - // TODO(richkadel): uncomment debug! - // debug!(" ...adding at least one pending={:?}", dup); - self.add_refined_span(dup); + debug!(" ...adding at least one pending={:?}", dup); + self.refined_spans.push(dup); } } else { self.pending_dups.clear(); @@ -1417,15 +2106,13 @@ impl<'a> CoverageSpanRefinery<'a> { self.prev_original_span = self.curr_original_span; } while let Some(curr) = self.sorted_spans_iter.next() { - // TODO(richkadel): uncomment debug! - // debug!("FOR curr={:?}", curr); + debug!("FOR curr={:?}", curr); if self.prev_starts_after_next(&curr) { - // TODO(richkadel): uncomment debug! - // debug!( - // " prev.span starts after curr.span, so curr will be dropped (skipping past \ - // closure?); prev={:?}", - // self.prev() - // ); + debug!( + " prev.span starts after curr.span, so curr will be dropped (skipping past \ + closure?); prev={:?}", + self.prev() + ); } else { // Save a copy of the original span for `curr` in case the `CoverageSpan` is changed // by `self.curr_mut().merge_from(prev)`. @@ -1472,17 +2159,15 @@ impl<'a> CoverageSpanRefinery<'a> { if has_pre_closure_span { let mut pre_closure = self.prev().clone(); pre_closure.span = pre_closure.span.with_hi(left_cutoff); - // TODO(richkadel): uncomment debug! - // debug!(" prev overlaps a closure. Adding span for pre_closure={:?}", pre_closure); + debug!(" prev overlaps a closure. Adding span for pre_closure={:?}", pre_closure); if !pending_dups.is_empty() { for mut dup in pending_dups.iter().cloned() { dup.span = dup.span.with_hi(left_cutoff); - // TODO(richkadel): uncomment debug! - // debug!(" ...and at least one pre_closure dup={:?}", dup); - self.add_refined_span(dup); + debug!(" ...and at least one pre_closure dup={:?}", dup); + self.refined_spans.push(dup); } } - self.add_refined_span(pre_closure); + self.refined_spans.push(pre_closure); } if has_post_closure_span { // Update prev.span to start after the closure (and discard curr) @@ -1498,15 +2183,58 @@ impl<'a> CoverageSpanRefinery<'a> { } } + + + // prev and any/all pending_dups are equal in non-dominance over the other. + // So when comparing to prev, need to also compare to all pending dups. + + + + +// TODO(richkadel): UPDATE COMMENT IF WE ARE CHANGING THIS LOGIC! + /// Called if `curr.span` = `prev.span` (and all `pending_dups` spans, if any). + /// /// When two `CoverageSpan`s have the same `Span`, dominated spans can be discarded; but if /// neither `CoverageSpan` dominates the other, both (or possibly more than two) are held, /// until their disposition is determined. In this latter case, the `prev` dup is moved into /// `pending_dups` so the new `curr` dup can be moved to `prev` for the next iteration. fn hold_pending_dups_unless_dominated(&mut self) { - // equal coverage spans are ordered by dominators before dominated (if any) + // Equal coverage spans are ordered by dominators before dominated (if any), so it should be + // impossible for `curr` to dominate any previous `CoverageSpan`. debug_assert!(!self.prev().is_dominated_by(self.curr(), self.bcb_dominators)); - if self.curr().is_dominated_by(&self.prev(), self.bcb_dominators) { + let initial_pending_count = self.pending_dups.len(); + if initial_pending_count > 0 { + let mut pending_dups = self.pending_dups.split_off(0); + pending_dups.retain(|dup| !self.curr().is_dominated_by(dup, self.bcb_dominators)); + self.pending_dups.append(&mut pending_dups); + if self.pending_dups.len() < initial_pending_count { + debug!( + " discarded {} of {} pending_dups that dominated curr", + initial_pending_count - self.pending_dups.len(), + initial_pending_count + ); + } + } + + if self.curr().is_dominated_by(self.prev(), self.bcb_dominators) { + // Keep in mind, if discarding `prev`, that this method is called if + // `curr.span` == `prev_original_span`, which may not be the full `prev.span` (if merged + // during the previous iteration). + debug!( + " different bcbs but SAME spans, and prev dominates curr. Discard prev={:?}", + self.prev() + ); + // TODO(richkadel): remove? + // let discarded_prev = self.take_prev(); + self.cutoff_prev_at_overlapping_curr(); +// TODO(richkadel): remove? +// if self.curr().is_dominated_by(self.prev(), self.bcb_dominators) { +// IF WE KEEP IT, WE REALLY SHOULD CHECK ALL: +// if self.pending_dups.iter().chain(self.some_prev.iter()).any(|dup| +// self.curr().is_dominated_by(dup, self.bcb_dominators)) { + +// TODO(richkadel): UPDATE COMMENT IF WE ARE CHANGING THIS LOGIC! // If one span dominates the other, assocate the span with the dominator only. // // For example: @@ -1519,23 +2247,32 @@ impl<'a> CoverageSpanRefinery<'a> { // // The dominator's (`prev`) execution count may be higher than the dominated // block's execution count, so drop `curr`. - // TODO(richkadel): uncomment debug! + + // **** MAYBE UNCOMMENT, OR REMOVE IF NEW debug! above *** TODO(richkadel): uncomment debug! // debug!( // " different bcbs but SAME spans, and prev dominates curr. Drop curr and \ // keep prev for next iter. prev={:?}", // self.prev() // ); - self.discard_curr(); + +// TODO(richkadel): Update logic and remove the following line if it works because this may have +// worked for the example above, but it seems to be consuming the `?` (try operator) as part of +// the call span that dominates it. + +// self.discard_curr(); + +// TODO(richkadel): If I don't change it, should I have checked not only prev dominates curr but +// (if not) also check any of the pending dups dominates curr? + } else { // Save `prev` in `pending_dups`. (`curr` will become `prev` in the next iteration.) // If the `curr` CoverageSpan is later discarded, `pending_dups` can be discarded as // well; but if `curr` is added to refined_spans, the `pending_dups` will also be added. - // TODO(richkadel): uncomment debug! - // debug!( - // " different bcbs but SAME spans, and neither dominates, so keep curr for \ - // next iter, and, pending upcoming spans (unless overlapping) add prev={:?}", - // self.prev() - // ); + debug!( + " different bcbs but SAME spans, and neither dominates, so keep curr for \ + next iter, and, pending upcoming spans (unless overlapping) add prev={:?}", + self.prev() + ); let prev = self.take_prev(); self.pending_dups.push(prev); } @@ -1549,23 +2286,20 @@ impl<'a> CoverageSpanRefinery<'a> { /// avoids injecting multiple counters for overlapping spans, and the potential for /// double-counting. fn cutoff_prev_at_overlapping_curr(&mut self) { - // TODO(richkadel): uncomment debug! - // debug!( - // " different bcbs, overlapping spans, so ignore/drop pending and only add prev \ - // if it has statements that end before curr={:?}", - // self.prev() - // ); + debug!( + " different bcbs, overlapping spans, so ignore/drop pending and only add prev \ + if it has statements that end before curr; prev={:?}", + self.prev() + ); if self.pending_dups.is_empty() { let curr_span = self.curr().span; self.prev_mut().cutoff_statements_at(curr_span.lo()); if self.prev().coverage_statements.is_empty() { - // TODO(richkadel): uncomment debug! - // debug!(" ... no non-overlapping statements to add"); + debug!(" ... no non-overlapping statements to add"); } else { - // TODO(richkadel): uncomment debug! - // debug!(" ... adding modified prev={:?}", self.prev()); + debug!(" ... adding modified prev={:?}", self.prev()); let prev = self.take_prev(); - self.add_refined_span(prev); + self.refined_spans.push(prev); } } else { // with `pending_dups`, `prev` cannot have any statements that don't overlap @@ -1657,8 +2391,31 @@ fn source_info_span(source_info: &SourceInfo, body_span: Span) -> Span { if body_span.contains(span) { span } else { body_span } } +/// Make a non-reportable code region +/// Count empty spans, but don't make them reportable as coverage. Set the source +/// position out of range. (Note that `llvm-cov` fails to report coverage if any +/// coverage region starts line `0`, so we are using a line number just beyond +/// the last line of the file.) +fn make_non_reportable_code_region( + file_name: Symbol, + source_file: &Lrc, +) -> CodeRegion { + let line_past_end_of_file = (source_file.lines.len() + 1) as u32; + CodeRegion { + file_name, + start_line: line_past_end_of_file, + start_col: 1, + end_line: line_past_end_of_file, + end_col: 1, + } +} + /// Convert the Span into its file name, start line and column, and end line and column -fn make_code_region(file_name: Symbol, source_file: &Lrc, span: Span) -> CodeRegion { +fn make_code_region( + file_name: Symbol, + source_file: &Lrc, + span: Span, +) -> CodeRegion { let (start_line, mut start_col) = source_file.lookup_file_pos(span.lo()); let (end_line, end_col) = if span.hi() == span.lo() { let (end_line, mut end_col) = (start_line, start_col); @@ -1702,6 +2459,18 @@ fn hash( stable_hasher.finish() } +fn bcb_filtered_successors<'tcx>(term_kind: &'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx> { + let mut successors = term_kind.successors(); + match &term_kind { + // SwitchInt successors are never unwind, and all of them should be traversed. + TerminatorKind::SwitchInt { .. } => successors, + // For all other kinds, return only the first successor, if any, and ignore unwinds. + // NOTE: `chain(&[])` is required to coerce the `option::iter` (from + // `next().into_iter()`) into the `mir::Successors` aliased type. + _ => successors.next().into_iter().chain(&[]), + } +} + pub struct ShortCircuitPreorder< 'a, 'tcx, diff --git a/compiler/rustc_mir/src/util/generic_graphviz.rs b/compiler/rustc_mir/src/util/generic_graphviz.rs new file mode 100644 index 0000000000000..83ee07faa8cb0 --- /dev/null +++ b/compiler/rustc_mir/src/util/generic_graphviz.rs @@ -0,0 +1,175 @@ +use rustc_data_structures::graph::{self, iterate}; +use rustc_graphviz as dot; +use rustc_middle::ty::TyCtxt; +use std::io::{self, Write}; + +pub struct GraphvizWriter<'a, G: graph::DirectedGraph + graph::WithSuccessors + graph::WithStartNode + graph::WithNumNodes, +NodeContentFn: Fn(::Node) -> Vec, +EdgeLabelsFn: Fn(::Node) -> Vec +> { + graph: &'a G, + is_subgraph: bool, + graphviz_name: String, + graph_label: Option, + node_content_fn: NodeContentFn, + edge_labels_fn: EdgeLabelsFn, +} + +impl<'a, G: graph::DirectedGraph + graph::WithSuccessors + graph::WithStartNode + graph::WithNumNodes, +NodeContentFn: Fn(::Node) -> Vec, +EdgeLabelsFn: Fn(::Node) -> Vec +> GraphvizWriter<'a, G, NodeContentFn, EdgeLabelsFn> { + pub fn new(graph: &'a G, graphviz_name: &str, node_content_fn: NodeContentFn, edge_labels_fn: EdgeLabelsFn) -> Self { + Self { + graph, + is_subgraph: false, + graphviz_name: graphviz_name.to_owned(), + graph_label: None, + node_content_fn, + edge_labels_fn, + } + } + + pub fn new_subgraph(graph: &'a G, graphviz_name: &str, node_content_fn: NodeContentFn, edge_labels_fn: EdgeLabelsFn) -> Self { + Self { + graph, + is_subgraph: true, + graphviz_name: graphviz_name.to_owned(), + graph_label: None, + node_content_fn, + edge_labels_fn, + } + } + + pub fn set_graph_label(&mut self, graph_label: &str) { + self.graph_label = Some(graph_label.to_owned()); + } + + /// Write a graphviz DOT of the graph + pub fn write_graphviz<'tcx, W>( + &self, + tcx: TyCtxt<'tcx>, + w: &mut W, + ) -> io::Result<()> + where + W: Write + { + let kind = if self.is_subgraph { "subgraph" } else { "digraph" }; + let cluster = if self.is_subgraph { "cluster_" } else { "" }; // Prints a border around graph + // FIXME(richkadel): If/when migrating the MIR graphviz to this generic implementation, + // prepend "Mir_" to the graphviz_safe_def_name(def_id) + writeln!(w, "{} {}{} {{", kind, cluster, self.graphviz_name)?; + + // Global graph properties + let font = format!(r#"fontname="{}""#, tcx.sess.opts.debugging_opts.graphviz_font); + let mut graph_attrs = vec![&font[..]]; + let mut content_attrs = vec![&font[..]]; + + let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode; + if dark_mode { + graph_attrs.push(r#"bgcolor="black""#); + graph_attrs.push(r#"fontcolor="white""#); + content_attrs.push(r#"color="white""#); + content_attrs.push(r#"fontcolor="white""#); + } + + writeln!(w, r#" graph [{}];"#, graph_attrs.join(" "))?; + let content_attrs_str = content_attrs.join(" "); + writeln!(w, r#" node [{}];"#, content_attrs_str)?; + writeln!(w, r#" edge [{}];"#, content_attrs_str)?; + + // Graph label + if let Some(graph_label) = &self.graph_label { + self.write_graph_label(graph_label, w)?; + } + + // Nodes + for node in iterate::post_order_from(self.graph, self.graph.start_node()) { + self.write_node(node, dark_mode, w)?; + } + + // Edges + for source in iterate::post_order_from(self.graph, self.graph.start_node()) { + self.write_edges(source, w)?; + } + writeln!(w, "}}") + } + + /// Write a graphviz DOT node for the given node. + pub fn write_node( + &self, + node: G::Node, + dark_mode: bool, + w: &mut W, + ) -> io::Result<()> + where + W: Write + { + // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables. + write!(w, r#" {} [shape="none", label=<"#, self.node(node))?; + + write!(w, r#""#)?; + + // FIXME(richkadel): Need generic way to know if node header should have a different color + // let (blk, bgcolor) = if data.is_cleanup { + // (format!("{:?} (cleanup)", node), "lightblue") + // } else { + // let color = if dark_mode { "dimgray" } else { "gray" }; + // (format!("{:?}", node), color) + // }; + let color = if dark_mode { "dimgray" } else { "gray" }; + let (blk, bgcolor) = (format!("{:?}", node), color); + write!( + w, + r#""#, + attrs = r#"align="center""#, + colspan = 1, + blk = blk, + bgcolor = bgcolor + )?; + + for section in (self.node_content_fn)(node) { + write!(w, r#""#, dot::escape_html(§ion).replace("\n", "
"))?; + } + + // Close the table + write!(w, "
{blk}
{}
")?; + + // Close the node label and the node itself. + writeln!(w, ">];") + } + + /// Write graphviz DOT edges with labels between the given node and all of its successors. + fn write_edges(&self, source: G::Node, w: &mut W) -> io::Result<()> + where + W: Write + { + let edge_labels = (self.edge_labels_fn)(source); + for (index, target) in self.graph.successors(source).enumerate() { + let src = self.node(source); + let trg = self.node(target); + let escaped_edge_label = if let Some(edge_label) = edge_labels.get(index) { + dot::escape_html(edge_label).replace("\n", r#"
"#) + } else { + "".to_owned() + }; + writeln!(w, r#" {} -> {} [label=<{}>];"#, src, trg, escaped_edge_label)?; + } + Ok(()) + } + + /// Write the graphviz DOT label for the overall graph. This is essentially a block of text that + /// will appear below the graph. + fn write_graph_label(&self, label: &str, w: &mut W) -> io::Result<()> + where + W: Write + { + let lines = label.split("\n").map(|s| dot::escape_html(s)).collect::>(); + let escaped_label = lines.join(r#"
"#); + write!(w, " label=<{}>;", escaped_label) + } + + fn node(&self, node: G::Node) -> String { + format!("{:?}__{}", node, self.graphviz_name) + } +} \ No newline at end of file diff --git a/compiler/rustc_mir/src/util/graphviz.rs b/compiler/rustc_mir/src/util/graphviz.rs index e04f07774ed0b..625f1a3e6844e 100644 --- a/compiler/rustc_mir/src/util/graphviz.rs +++ b/compiler/rustc_mir/src/util/graphviz.rs @@ -62,6 +62,7 @@ where let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode; if dark_mode { graph_attrs.push(r#"bgcolor="black""#); + graph_attrs.push(r#"fontcolor="white""#); content_attrs.push(r#"color="white""#); content_attrs.push(r#"fontcolor="white""#); } @@ -112,7 +113,8 @@ where // Basic block number at the top. let (blk, bgcolor) = if data.is_cleanup { - (format!("{} (cleanup)", block.index()), "lightblue") + let color = if dark_mode { "royalblue" } else { "lightblue" }; + (format!("{} (cleanup)", block.index()), color) } else { let color = if dark_mode { "dimgray" } else { "gray" }; (format!("{}", block.index()), color) diff --git a/compiler/rustc_mir/src/util/mod.rs b/compiler/rustc_mir/src/util/mod.rs index 7da2f4ffe0889..aaee0bc526db5 100644 --- a/compiler/rustc_mir/src/util/mod.rs +++ b/compiler/rustc_mir/src/util/mod.rs @@ -7,6 +7,7 @@ pub mod storage; mod alignment; pub mod collect_writes; mod find_self_call; +pub(crate) mod generic_graphviz; mod graphviz; pub(crate) mod pretty; pub(crate) mod spanview; diff --git a/src/test/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot b/src/test/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot new file mode 100644 index 0000000000000..49228328e011f --- /dev/null +++ b/src/test/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot @@ -0,0 +1,6 @@ +digraph Cov_0_4 { + graph [fontname="Courier, monospace"]; + node [fontname="Courier, monospace"]; + edge [fontname="Courier, monospace"]; + bcb0__Cov_0_4 [shape="none", label=<
bcb0
Counter(#1) at 19:5-20:2
19:5-19:9: @0[0]: _0 = const true
20:2-20:2: @0.Return: return
bb0: Return
>]; +} diff --git a/src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot b/src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot new file mode 100644 index 0000000000000..907b832d0346f --- /dev/null +++ b/src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot @@ -0,0 +1,11 @@ +digraph Cov_0_3 { + graph [fontname="Courier, monospace"]; + node [fontname="Courier, monospace"]; + edge [fontname="Courier, monospace"]; + bcb2__Cov_0_3 [shape="none", label=<
bcb2
Non-coverage counters:
Expression(#1 - #2)
bb4: Goto
>]; + bcb1__Cov_0_3 [shape="none", label=<
bcb1
Counter(#2) at 12:13-15:2
12:13-12:18: @5[0]: _0 = const ()
15:2-15:2: @5.Return: return
bb3: FalseEdge
bb5: Return
>]; + bcb0__Cov_0_3 [shape="none", label=<
bcb0
Counter(#1) at 11:12-11:17
11:12-11:17: @1.Call: _2 = bar() -> [return: bb2, unwind: bb6]
11:12-11:17: @2[0]: FakeRead(ForMatchedPlace, _2)
bb0: FalseUnwind
bb1: Call
bb2: SwitchInt
>]; + bcb2__Cov_0_3 -> bcb0__Cov_0_3 [label=<>]; + bcb0__Cov_0_3 -> bcb2__Cov_0_3 [label=]; + bcb0__Cov_0_3 -> bcb1__Cov_0_3 [label=]; +} diff --git a/src/test/mir-opt/coverage_graphviz.rs b/src/test/mir-opt/coverage_graphviz.rs new file mode 100644 index 0000000000000..7bb6d33bee900 --- /dev/null +++ b/src/test/mir-opt/coverage_graphviz.rs @@ -0,0 +1,20 @@ +// Test that `-Z instrument-coverage` with `-Z dump-mir-graphviz` generates a graphviz (.dot file) +// rendering of the `BasicCoverageBlock` coverage control flow graph, with counters and +// expressions. + +// needs-profiler-support +// compile-flags: -Z instrument-coverage -Z dump-mir-graphviz +// EMIT_MIR coverage_graphviz.main.InstrumentCoverage.0.dot +// EMIT_MIR coverage_graphviz.bar.InstrumentCoverage.0.dot +fn main() { + loop { + if bar() { + break; + } + } +} + +#[inline(never)] +fn bar() -> bool { + true +} \ No newline at end of file diff --git a/src/test/mir-opt/instrument_coverage.main.InstrumentCoverage.diff b/src/test/mir-opt/instrument_coverage.main.InstrumentCoverage.diff index 598727e677ca2..918f5b246d4eb 100644 --- a/src/test/mir-opt/instrument_coverage.main.InstrumentCoverage.diff +++ b/src/test/mir-opt/instrument_coverage.main.InstrumentCoverage.diff @@ -8,7 +8,6 @@ let mut _3: !; // in scope 0 at /the/src/instrument_coverage.rs:12:18: 14:10 bb0: { -+ Coverage::Counter(1) for /the/src/instrument_coverage.rs:12:12 - 12:17; // scope 0 at /the/src/instrument_coverage.rs:11:5: 15:6 falseUnwind -> [real: bb1, cleanup: bb6]; // scope 0 at /the/src/instrument_coverage.rs:11:5: 15:6 } @@ -22,23 +21,25 @@ bb2: { FakeRead(ForMatchedPlace, _2); // scope 0 at /the/src/instrument_coverage.rs:12:12: 12:17 ++ Coverage::Counter(1) for /the/src/instrument_coverage.rs:12:12 - 12:17; // scope 0 at /the/src/instrument_coverage.rs:12:9: 14:10 switchInt(_2) -> [false: bb4, otherwise: bb3]; // scope 0 at /the/src/instrument_coverage.rs:12:9: 14:10 } bb3: { -+ Coverage::Counter(2) for /the/src/instrument_coverage.rs:13:13 - 16:2; // scope 0 at /the/src/instrument_coverage.rs:12:9: 14:10 falseEdge -> [real: bb5, imaginary: bb4]; // scope 0 at /the/src/instrument_coverage.rs:12:9: 14:10 } bb4: { _1 = const (); // scope 0 at /the/src/instrument_coverage.rs:12:9: 14:10 StorageDead(_2); // scope 0 at /the/src/instrument_coverage.rs:15:5: 15:6 ++ Coverage::Expression(4294967295) = 1 - 2 for /the/src/instrument_coverage.rs:37:1 - 37:1; // scope 0 at /the/src/instrument_coverage.rs:11:5: 15:6 goto -> bb0; // scope 0 at /the/src/instrument_coverage.rs:11:5: 15:6 } bb5: { _0 = const (); // scope 0 at /the/src/instrument_coverage.rs:13:13: 13:18 StorageDead(_2); // scope 0 at /the/src/instrument_coverage.rs:15:5: 15:6 ++ Coverage::Counter(2) for /the/src/instrument_coverage.rs:13:13 - 16:2; // scope 0 at /the/src/instrument_coverage.rs:16:2: 16:2 return; // scope 0 at /the/src/instrument_coverage.rs:16:2: 16:2 } diff --git a/src/test/mir-opt/instrument_coverage.rs b/src/test/mir-opt/instrument_coverage.rs index d9c8e8d43a469..18863edac97e4 100644 --- a/src/test/mir-opt/instrument_coverage.rs +++ b/src/test/mir-opt/instrument_coverage.rs @@ -1,10 +1,10 @@ -// Test that the initial version of Rust coverage injects Coverage statements at the top of each -// function. The Coverage Counter statements are later converted into LLVM instrprof.increment -// intrinsics, during codegen. +// Test that `-Z instrument-coverage` injects Coverage statements. The Coverage Counter statements +// are later converted into LLVM instrprof.increment intrinsics, during codegen. // needs-profiler-support // ignore-windows -// compile-flags: -Zinstrument-coverage --remap-path-prefix={{src-base}}=/the/src +// compile-flags: -Z instrument-coverage --remap-path-prefix={{src-base}}=/the/src + // EMIT_MIR instrument_coverage.main.InstrumentCoverage.diff // EMIT_MIR instrument_coverage.bar.InstrumentCoverage.diff fn main() { diff --git a/src/test/run-make-fulldeps/coverage-llvmir-base/filecheck.testprog.txt b/src/test/run-make-fulldeps/coverage-llvmir-base/filecheck.testprog.txt index 0a3c4aedd5569..bd2a2475d9e10 100644 --- a/src/test/run-make-fulldeps/coverage-llvmir-base/filecheck.testprog.txt +++ b/src/test/run-make-fulldeps/coverage-llvmir-base/filecheck.testprog.txt @@ -34,7 +34,7 @@ CHECK-SAME: section "llvm.metadata" CHECK: [[DEFINE_INTERNAL]] { {{.*}} } @_R{{[a-zA-Z0-9_]+}}testprog14will_be_called() unnamed_addr #{{[0-9]+}} { CHECK-NEXT: start: -CHECK-NOT: bb{{[0-9]+}}: +CHECK-NOT: [[DEFINE_INTERNAL]] CHECK: %pgocount = load i64, i64* getelementptr inbounds CHECK-SAME: * @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called, diff --git a/src/test/run-make-fulldeps/coverage-reports-base/Makefile b/src/test/run-make-fulldeps/coverage-reports-base/Makefile index 880d7fdb1b040..5446252a33c82 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/Makefile +++ b/src/test/run-make-fulldeps/coverage-reports-base/Makefile @@ -54,6 +54,7 @@ endif # when comparing the JSON `export`, the `show` output may be useful when # debugging. "$(LLVM_BIN_DIR)"/llvm-cov show \ + --debug \ --Xdemangler="$(RUST_DEMANGLER)" \ --show-line-counts-or-regions \ --instr-profile="$(TMPDIR)"/$@.profdata \ diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.closure.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.closure.json index 8c6edae280397..e8d871f00a36c 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.closure.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.closure.json @@ -21,10 +21,10 @@ "percent": 82.41758241758241 }, "regions": { - "count": 21, - "covered": 11, - "notcovered": 10, - "percent": 52.38095238095239 + "count": 25, + "covered": 13, + "notcovered": 12, + "percent": 52 } } } @@ -46,10 +46,10 @@ "percent": 82.41758241758241 }, "regions": { - "count": 21, - "covered": 11, - "notcovered": 10, - "percent": 52.38095238095239 + "count": 25, + "covered": 13, + "notcovered": 12, + "percent": 52 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.if.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.if.json index 2ff53ad33fa99..84dcc251f3f4b 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.if.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.if.json @@ -21,10 +21,10 @@ "percent": 100 }, "regions": { - "count": 4, + "count": 5, "covered": 4, - "notcovered": 0, - "percent": 100 + "notcovered": 1, + "percent": 80 } } } @@ -46,10 +46,10 @@ "percent": 100 }, "regions": { - "count": 4, + "count": 5, "covered": 4, - "notcovered": 0, - "percent": 100 + "notcovered": 1, + "percent": 80 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.inner_items.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.inner_items.json index a24e6a33a3397..c178e7f93476f 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.inner_items.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.inner_items.json @@ -21,10 +21,10 @@ "percent": 100 }, "regions": { - "count": 13, + "count": 15, "covered": 13, - "notcovered": 0, - "percent": 100 + "notcovered": 2, + "percent": 86.66666666666667 } } } @@ -46,10 +46,10 @@ "percent": 100 }, "regions": { - "count": 13, + "count": 15, "covered": 13, - "notcovered": 0, - "percent": 100 + "notcovered": 2, + "percent": 86.66666666666667 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.lazy_boolean.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.lazy_boolean.json index 585346dc32a60..f5fae0cb82bd5 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.lazy_boolean.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.lazy_boolean.json @@ -21,10 +21,10 @@ "percent": 90.47619047619048 }, "regions": { - "count": 16, - "covered": 14, - "notcovered": 2, - "percent": 87.5 + "count": 33, + "covered": 21, + "notcovered": 12, + "percent": 63.63636363636363 } } } @@ -46,10 +46,10 @@ "percent": 90.47619047619048 }, "regions": { - "count": 16, - "covered": 14, - "notcovered": 2, - "percent": 87.5 + "count": 33, + "covered": 21, + "notcovered": 12, + "percent": 63.63636363636363 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_loop.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_loop.json index 38bc96898ea36..bcc0e1331e331 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_loop.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_loop.json @@ -21,10 +21,10 @@ "percent": 100 }, "regions": { - "count": 7, - "covered": 7, - "notcovered": 0, - "percent": 100 + "count": 10, + "covered": 9, + "notcovered": 1, + "percent": 90 } } } @@ -46,10 +46,10 @@ "percent": 100 }, "regions": { - "count": 7, - "covered": 7, - "notcovered": 0, - "percent": 100 + "count": 10, + "covered": 9, + "notcovered": 1, + "percent": 90 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_match.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_match.json index f9d91d66f1db2..ed1705e587062 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_match.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_match.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 26, - "covered": 26, + "count": 23, + "covered": 23, "percent": 100 }, "regions": { - "count": 9, - "covered": 9, - "notcovered": 0, - "percent": 100 + "count": 16, + "covered": 14, + "notcovered": 2, + "percent": 87.5 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 26, - "covered": 26, + "count": 23, + "covered": 23, "percent": 100 }, "regions": { - "count": 9, - "covered": 9, - "notcovered": 0, - "percent": 100 + "count": 16, + "covered": 14, + "notcovered": 2, + "percent": 87.5 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.try_error_result.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.try_error_result.json index e6ef2c1ab899e..c647d6e8de55d 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.try_error_result.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.try_error_result.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 16, - "covered": 15, - "percent": 93.75 + "count": 17, + "covered": 16, + "percent": 94.11764705882352 }, "regions": { - "count": 13, - "covered": 12, - "notcovered": 1, - "percent": 92.3076923076923 + "count": 21, + "covered": 15, + "notcovered": 6, + "percent": 71.42857142857143 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 16, - "covered": 15, - "percent": 93.75 + "count": 17, + "covered": 16, + "percent": 94.11764705882352 }, "regions": { - "count": 13, - "covered": 12, - "notcovered": 1, - "percent": 92.3076923076923 + "count": 21, + "covered": 15, + "notcovered": 6, + "percent": 71.42857142857143 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.various_conditions.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.various_conditions.json index 410821ea33592..c7d80a446fe2c 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.various_conditions.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.various_conditions.json @@ -21,10 +21,10 @@ "percent": 46.93877551020408 }, "regions": { - "count": 51, - "covered": 19, - "notcovered": 32, - "percent": 37.254901960784316 + "count": 104, + "covered": 23, + "notcovered": 81, + "percent": 22.115384615384613 } } } @@ -46,10 +46,10 @@ "percent": 46.93877551020408 }, "regions": { - "count": 51, - "covered": 19, - "notcovered": 32, - "percent": 37.254901960784316 + "count": 104, + "covered": 23, + "notcovered": 81, + "percent": 22.115384615384613 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while_early_return.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while_early_return.json index 865b705fa2007..ea6fd2ac12f35 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while_early_return.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while_early_return.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 18, - "covered": 16, - "percent": 88.88888888888889 + "count": 17, + "covered": 15, + "percent": 88.23529411764706 }, "regions": { - "count": 9, - "covered": 7, + "count": 11, + "covered": 9, "notcovered": 2, - "percent": 77.77777777777779 + "percent": 81.81818181818183 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 18, - "covered": 16, - "percent": 88.88888888888889 + "count": 17, + "covered": 15, + "percent": 88.23529411764706 }, "regions": { - "count": 9, - "covered": 7, + "count": 11, + "covered": 9, "notcovered": 2, - "percent": 77.77777777777779 + "percent": 81.81818181818183 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_match.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_match.txt index e42f22cd047fc..d40acc40ace3e 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_match.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_match.txt @@ -11,18 +11,18 @@ 11| 1| countdown = 0; 12| 1| } 13| | - 14| 3| for - 15| 3| _ + 14| | for + 15| 2| _ 16| | in - 17| 1| 0..2 + 17| 3| 0..2 18| | { 19| | let z 20| | ; 21| | match 22| 2| countdown - 23| 2| { - 24| 2| x - 25| 2| if + 23| | { + 24| 1| x + 25| | if 26| 2| x 27| 2| < 28| 2| 1 diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.try_error_result.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.try_error_result.txt index ae288d7d7a000..0714b8d44892d 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.try_error_result.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.try_error_result.txt @@ -13,15 +13,15 @@ 13| 1| let mut 14| 1| countdown = 10 15| | ; - 16| 6| for + 16| | for 17| 6| _ 18| | in - 19| 1| 0..10 + 19| 6| 0..10 20| | { 21| 6| countdown 22| 6| -= 1 - 23| | ; - 24| | if + 23| 6| ; + 24| 6| if 25| 6| countdown < 5 26| | { 27| 1| call(/*return_error=*/ true)?; @@ -29,6 +29,7 @@ 29| | else 30| | { 31| 5| call(/*return_error=*/ false)?; + ^0 32| | } 33| | } 34| 0| Ok(()) diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while_early_return.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while_early_return.txt index 7dce94f25f304..26041136d2f4c 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while_early_return.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while_early_return.txt @@ -3,7 +3,7 @@ 3| | 4| |fn main() -> Result<(),u8> { 5| 1| let mut countdown = 10; - 6| 7| while + 6| | while 7| 7| countdown 8| 7| > 9| 7| 0 diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.closure.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.closure.json index 8c6edae280397..e8d871f00a36c 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.closure.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.closure.json @@ -21,10 +21,10 @@ "percent": 82.41758241758241 }, "regions": { - "count": 21, - "covered": 11, - "notcovered": 10, - "percent": 52.38095238095239 + "count": 25, + "covered": 13, + "notcovered": 12, + "percent": 52 } } } @@ -46,10 +46,10 @@ "percent": 82.41758241758241 }, "regions": { - "count": 21, - "covered": 11, - "notcovered": 10, - "percent": 52.38095238095239 + "count": 25, + "covered": 13, + "notcovered": 12, + "percent": 52 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.if.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.if.json index 2ff53ad33fa99..84dcc251f3f4b 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.if.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.if.json @@ -21,10 +21,10 @@ "percent": 100 }, "regions": { - "count": 4, + "count": 5, "covered": 4, - "notcovered": 0, - "percent": 100 + "notcovered": 1, + "percent": 80 } } } @@ -46,10 +46,10 @@ "percent": 100 }, "regions": { - "count": 4, + "count": 5, "covered": 4, - "notcovered": 0, - "percent": 100 + "notcovered": 1, + "percent": 80 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.inner_items.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.inner_items.json index a24e6a33a3397..c178e7f93476f 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.inner_items.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.inner_items.json @@ -21,10 +21,10 @@ "percent": 100 }, "regions": { - "count": 13, + "count": 15, "covered": 13, - "notcovered": 0, - "percent": 100 + "notcovered": 2, + "percent": 86.66666666666667 } } } @@ -46,10 +46,10 @@ "percent": 100 }, "regions": { - "count": 13, + "count": 15, "covered": 13, - "notcovered": 0, - "percent": 100 + "notcovered": 2, + "percent": 86.66666666666667 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.lazy_boolean.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.lazy_boolean.json index 585346dc32a60..f5fae0cb82bd5 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.lazy_boolean.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.lazy_boolean.json @@ -21,10 +21,10 @@ "percent": 90.47619047619048 }, "regions": { - "count": 16, - "covered": 14, - "notcovered": 2, - "percent": 87.5 + "count": 33, + "covered": 21, + "notcovered": 12, + "percent": 63.63636363636363 } } } @@ -46,10 +46,10 @@ "percent": 90.47619047619048 }, "regions": { - "count": 16, - "covered": 14, - "notcovered": 2, - "percent": 87.5 + "count": 33, + "covered": 21, + "notcovered": 12, + "percent": 63.63636363636363 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_loop.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_loop.json index 38bc96898ea36..bcc0e1331e331 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_loop.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_loop.json @@ -21,10 +21,10 @@ "percent": 100 }, "regions": { - "count": 7, - "covered": 7, - "notcovered": 0, - "percent": 100 + "count": 10, + "covered": 9, + "notcovered": 1, + "percent": 90 } } } @@ -46,10 +46,10 @@ "percent": 100 }, "regions": { - "count": 7, - "covered": 7, - "notcovered": 0, - "percent": 100 + "count": 10, + "covered": 9, + "notcovered": 1, + "percent": 90 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_match.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_match.json index f9d91d66f1db2..ed1705e587062 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_match.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_match.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 26, - "covered": 26, + "count": 23, + "covered": 23, "percent": 100 }, "regions": { - "count": 9, - "covered": 9, - "notcovered": 0, - "percent": 100 + "count": 16, + "covered": 14, + "notcovered": 2, + "percent": 87.5 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 26, - "covered": 26, + "count": 23, + "covered": 23, "percent": 100 }, "regions": { - "count": 9, - "covered": 9, - "notcovered": 0, - "percent": 100 + "count": 16, + "covered": 14, + "notcovered": 2, + "percent": 87.5 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.try_error_result.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.try_error_result.json index e6ef2c1ab899e..c647d6e8de55d 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.try_error_result.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.try_error_result.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 16, - "covered": 15, - "percent": 93.75 + "count": 17, + "covered": 16, + "percent": 94.11764705882352 }, "regions": { - "count": 13, - "covered": 12, - "notcovered": 1, - "percent": 92.3076923076923 + "count": 21, + "covered": 15, + "notcovered": 6, + "percent": 71.42857142857143 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 16, - "covered": 15, - "percent": 93.75 + "count": 17, + "covered": 16, + "percent": 94.11764705882352 }, "regions": { - "count": 13, - "covered": 12, - "notcovered": 1, - "percent": 92.3076923076923 + "count": 21, + "covered": 15, + "notcovered": 6, + "percent": 71.42857142857143 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.various_conditions.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.various_conditions.json index 410821ea33592..c7d80a446fe2c 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.various_conditions.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.various_conditions.json @@ -21,10 +21,10 @@ "percent": 46.93877551020408 }, "regions": { - "count": 51, - "covered": 19, - "notcovered": 32, - "percent": 37.254901960784316 + "count": 104, + "covered": 23, + "notcovered": 81, + "percent": 22.115384615384613 } } } @@ -46,10 +46,10 @@ "percent": 46.93877551020408 }, "regions": { - "count": 51, - "covered": 19, - "notcovered": 32, - "percent": 37.254901960784316 + "count": 104, + "covered": 23, + "notcovered": 81, + "percent": 22.115384615384613 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while_early_return.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while_early_return.json index 865b705fa2007..ea6fd2ac12f35 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while_early_return.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while_early_return.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 18, - "covered": 16, - "percent": 88.88888888888889 + "count": 17, + "covered": 15, + "percent": 88.23529411764706 }, "regions": { - "count": 9, - "covered": 7, + "count": 11, + "covered": 9, "notcovered": 2, - "percent": 77.77777777777779 + "percent": 81.81818181818183 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 18, - "covered": 16, - "percent": 88.88888888888889 + "count": 17, + "covered": 15, + "percent": 88.23529411764706 }, "regions": { - "count": 9, - "covered": 7, + "count": 11, + "covered": 9, "notcovered": 2, - "percent": 77.77777777777779 + "percent": 81.81818181818183 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_match.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_match.txt index e42f22cd047fc..d40acc40ace3e 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_match.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_match.txt @@ -11,18 +11,18 @@ 11| 1| countdown = 0; 12| 1| } 13| | - 14| 3| for - 15| 3| _ + 14| | for + 15| 2| _ 16| | in - 17| 1| 0..2 + 17| 3| 0..2 18| | { 19| | let z 20| | ; 21| | match 22| 2| countdown - 23| 2| { - 24| 2| x - 25| 2| if + 23| | { + 24| 1| x + 25| | if 26| 2| x 27| 2| < 28| 2| 1 diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.try_error_result.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.try_error_result.txt index ae288d7d7a000..0714b8d44892d 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.try_error_result.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.try_error_result.txt @@ -13,15 +13,15 @@ 13| 1| let mut 14| 1| countdown = 10 15| | ; - 16| 6| for + 16| | for 17| 6| _ 18| | in - 19| 1| 0..10 + 19| 6| 0..10 20| | { 21| 6| countdown 22| 6| -= 1 - 23| | ; - 24| | if + 23| 6| ; + 24| 6| if 25| 6| countdown < 5 26| | { 27| 1| call(/*return_error=*/ true)?; @@ -29,6 +29,7 @@ 29| | else 30| | { 31| 5| call(/*return_error=*/ false)?; + ^0 32| | } 33| | } 34| 0| Ok(()) diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while_early_return.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while_early_return.txt index 7dce94f25f304..26041136d2f4c 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while_early_return.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while_early_return.txt @@ -3,7 +3,7 @@ 3| | 4| |fn main() -> Result<(),u8> { 5| 1| let mut countdown = 10; - 6| 7| while + 6| | while 7| 7| countdown 8| 7| > 9| 7| 0 diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main-in_func.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main-in_func.-------.InstrumentCoverage.0.html index 49639cc6884ba..7f1262a6abf0a 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main-in_func.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main-in_func.-------.InstrumentCoverage.0.html @@ -63,8 +63,7 @@ let @0⦊b = 1⦉@0; let @1,2,3,4⦊c⦉@1,2,3,4 = @0⦊a + b⦉@0; +20:21-20:22: @0[7]: _5 = _2">@0⦊a + b⦉@0; fn trait_func(&mut self, incr: u32) { - @0⦊self.in_struct_field += incr⦉@0; + self.in_struct_field += @0⦊incr⦉@0; @1,2⦊in_func(self.in_struct_field); diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html index 4b21d3fc263a3..7dd22ee6e179f 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html @@ -117,9 +117,9 @@ @10,12⦊break⦉@10,12 ; } - @11⦊countdown - -= - 1⦉@11 + @13⦊countdown + -= + 1⦉@13 ; } }@10,12⦊‸⦉@10,12 diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html index 5ba770ef6078e..abcf30e93c04e 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html @@ -92,44 +92,29 @@ }⦉@4,6 - @9,10,11⦊for - _⦉@9,10,11 + for + @13,15,17⦊_⦉@13,15,17 in - @7,8⦊0..2⦉@7,8 + @9,10,11⦊0..2⦉@9,10,11 { let z ; match - @13,15,17⦊countdown - { - x - if - x - @13,15,17⦊countdown⦉@13,15,17 + { + @18⦊x⦉@18 + if + @13,15,17⦊x + < - 1⦉@13,15,17 => countdown = 10⦉@0,1 ; - @2,3,4⦊for - _⦉@2,3,4 + for + @6,8⦊_⦉@6,8 in - @0,1⦊0..10⦉@0,1 + @2,3,4⦊0..10⦉@2,3,4 { - @6,8⦊countdown - -= 1⦉@6,8 - ; - if - @9⦊countdown < 5⦉@9 +25:13-25:26: @9[6]: FakeRead(ForMatchedPlace, _18)">@9⦊countdown + -= 1 + ; + if + countdown < 5⦉@9 { - @10,12,13,14⦊call(/*return_error=*/ true)?⦉@10,12,13,14; + @10,12,13,14⦊call(/*return_error=*/ true)⦉@10,12,13,14@16,18,19,20⦊?⦉@16,18,19,20; } else { - @11,21,22⦊call(/*return_error=*/ false)?⦉@11,21,22; + @11,21,22⦊call(/*return_error=*/ false)⦉@11,21,22@24,26,27,28⦊?⦉@24,26,27,28; } } - @5⦊Ok(())⦉@5 -}@31⦊‸⦉@31 +}@31⦊‸⦉@31 diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.various_conditions/various_conditions.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.various_conditions/various_conditions.main.-------.InstrumentCoverage.0.html index 28f1d013c835f..a0daddb2e88d4 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.various_conditions/various_conditions.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.various_conditions/various_conditions.main.-------.InstrumentCoverage.0.html @@ -74,22 +74,26 @@ let @25⦊x⦉@25 = if @4⦊countdown > 7⦉@4 { - @5,7⦊countdown -= 4⦉@5,7; - @8⦊B⦉@8 - } else if @8⦊countdown -= 4; + B⦉@8 + } else if @6⦊countdown > 2⦉@6 { - if @9,11⦊countdown < 1⦉@9,11 || @18⦊countdown > 5⦉@18 || @14⦊countdown != 9⦉@14 if @9,11⦊countdown < 1⦉@9,11 || @18⦊countdown > 5⦉@18 || @14⦊countdown != 9⦉@14 @20,22⦊{ - countdown = 0; - }⦉@20,22 - @23⦊countdown -= 5⦉@23; - @24⦊countdown⦉@24 + @24⦊countdown -= 5; + countdown⦉@24 } else { @10⦊return⦉@10; }; @@ -107,7 +111,7 @@ if @29⦊countdown > 7⦉@29 { - @30,32⦊countdown -= 4⦉@30,32; + @33⦊countdown -= 4⦉@33; } else if @31⦊countdown > 2⦉@31 { @@ -120,7 +124,7 @@ 31:61-33:10: @47[1]: _30 = const ()"> countdown = 0; }⦉@45,47 - @48⦊countdown -= 5⦉@48; + @49⦊countdown -= 5⦉@49; } else { @35⦊return⦉@35; } @@ -138,7 +142,7 @@ let @77⦊z⦉@77 = if @54⦊countdown > 7⦉@54 { - @55,57⦊countdown -= 4⦉@55,57; + @58⦊countdown -= 4⦉@58; } else if @56⦊countdown > 2⦉@56 { @@ -151,7 +155,7 @@ 47:61-49:10: @72[1]: _50 = const ()"> countdown = 0; }⦉@70,72 - @73⦊countdown -= 5⦉@73; + @74⦊countdown -= 5⦉@74; } else { let let @98⦊w⦉@98 = if @77⦊countdown > 7⦉@77 { - @78,80⦊countdown -= 4⦉@78,80; + @81⦊countdown -= 4⦉@81; } else if @79⦊countdown > 2⦉@79 { @@ -219,7 +223,7 @@ 60:61-62:10: @95[1]: _80 = const ()"> countdown = 0; }⦉@93,95 - @96⦊countdown -= 5⦉@96; + @97⦊countdown -= 5⦉@97; } else { @83⦊return⦉@83; }; diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html index b96789a92194d..7053319a817c7 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html @@ -62,12 +62,10 @@
fn main() -> Result<(),u8> { let @0⦊mut countdown = 10⦉@0; - while + @1,2⦊while - countdown +7:9-9:10: @2[5]: FakeRead(ForMatchedPlace, _4)">@1,2⦊countdown > @@ -107,9 +105,9 @@ } ; } - @7⦊countdown - -= - 1⦉@7 + @12⦊countdown + -= + 1⦉@12 ; } let @0⦊b = 1⦉@0; let @1,2,3,4⦊c⦉@1,2,3,4 = @0⦊a + b⦉@0; +20:21-20:22: @0[7]: _5 = _2">@0⦊a + b⦉@0; fn trait_func(&mut self, incr: u32) { - @0⦊self.in_struct_field += incr⦉@0; + self.in_struct_field += @0⦊incr⦉@0; @1,2⦊in_func(self.in_struct_field); diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html index 4b21d3fc263a3..7dd22ee6e179f 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html @@ -117,9 +117,9 @@ @10,12⦊break⦉@10,12 ; } - @11⦊countdown - -= - 1⦉@11 + @13⦊countdown + -= + 1⦉@13 ; } }@10,12⦊‸⦉@10,12
diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html index 5ba770ef6078e..abcf30e93c04e 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html @@ -92,44 +92,29 @@ }⦉@4,6
- @9,10,11⦊for - _⦉@9,10,11 + for + @13,15,17⦊_⦉@13,15,17 in - @7,8⦊0..2⦉@7,8 + @9,10,11⦊0..2⦉@9,10,11 { let z ; match - @13,15,17⦊countdown - { - x - if - x - @13,15,17⦊countdown⦉@13,15,17 + { + @18⦊x⦉@18 + if + @13,15,17⦊x + < - 1⦉@13,15,17 => countdown = 10⦉@0,1 ; - @2,3,4⦊for - _⦉@2,3,4 + for + @6,8⦊_⦉@6,8 in - @0,1⦊0..10⦉@0,1 + @2,3,4⦊0..10⦉@2,3,4 { - @6,8⦊countdown - -= 1⦉@6,8 - ; - if - @9⦊countdown < 5⦉@9 +25:13-25:26: @9[6]: FakeRead(ForMatchedPlace, _18)">@9⦊countdown + -= 1 + ; + if + countdown < 5⦉@9 { - @10,12,13,14⦊call(/*return_error=*/ true)?⦉@10,12,13,14; + @10,12,13,14⦊call(/*return_error=*/ true)⦉@10,12,13,14@16,18,19,20⦊?⦉@16,18,19,20; } else { - @11,21,22⦊call(/*return_error=*/ false)?⦉@11,21,22; + @11,21,22⦊call(/*return_error=*/ false)⦉@11,21,22@24,26,27,28⦊?⦉@24,26,27,28; } } - @5⦊Ok(())⦉@5 -}@31⦊‸⦉@31 +}@31⦊‸⦉@31 diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.various_conditions/various_conditions.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.various_conditions/various_conditions.main.-------.InstrumentCoverage.0.html index 28f1d013c835f..a0daddb2e88d4 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.various_conditions/various_conditions.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.various_conditions/various_conditions.main.-------.InstrumentCoverage.0.html @@ -74,22 +74,26 @@ let @25⦊x⦉@25 = if @4⦊countdown > 7⦉@4 { - @5,7⦊countdown -= 4⦉@5,7; - @8⦊B⦉@8 - } else if @8⦊countdown -= 4; + B⦉@8 + } else if @6⦊countdown > 2⦉@6 { - if @9,11⦊countdown < 1⦉@9,11 || @18⦊countdown > 5⦉@18 || @14⦊countdown != 9⦉@14 if @9,11⦊countdown < 1⦉@9,11 || @18⦊countdown > 5⦉@18 || @14⦊countdown != 9⦉@14 @20,22⦊{ - countdown = 0; - }⦉@20,22 - @23⦊countdown -= 5⦉@23; - @24⦊countdown⦉@24 + @24⦊countdown -= 5; + countdown⦉@24 } else { @10⦊return⦉@10; }; @@ -107,7 +111,7 @@ if @29⦊countdown > 7⦉@29 { - @30,32⦊countdown -= 4⦉@30,32; + @33⦊countdown -= 4⦉@33; } else if @31⦊countdown > 2⦉@31 { @@ -120,7 +124,7 @@ 31:61-33:10: @47[1]: _30 = const ()"> countdown = 0; }⦉@45,47 - @48⦊countdown -= 5⦉@48; + @49⦊countdown -= 5⦉@49; } else { @35⦊return⦉@35; } @@ -138,7 +142,7 @@ let @77⦊z⦉@77 = if @54⦊countdown > 7⦉@54 { - @55,57⦊countdown -= 4⦉@55,57; + @58⦊countdown -= 4⦉@58; } else if @56⦊countdown > 2⦉@56 { @@ -151,7 +155,7 @@ 47:61-49:10: @72[1]: _50 = const ()"> countdown = 0; }⦉@70,72 - @73⦊countdown -= 5⦉@73; + @74⦊countdown -= 5⦉@74; } else { let let @98⦊w⦉@98 = if @77⦊countdown > 7⦉@77 { - @78,80⦊countdown -= 4⦉@78,80; + @81⦊countdown -= 4⦉@81; } else if @79⦊countdown > 2⦉@79 { @@ -219,7 +223,7 @@ 60:61-62:10: @95[1]: _80 = const ()"> countdown = 0; }⦉@93,95 - @96⦊countdown -= 5⦉@96; + @97⦊countdown -= 5⦉@97; } else { @83⦊return⦉@83; }; diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html index b96789a92194d..7053319a817c7 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html @@ -62,12 +62,10 @@
fn main() -> Result<(),u8> { let @0⦊mut countdown = 10⦉@0; - while + @1,2⦊while - countdown +7:9-9:10: @2[5]: FakeRead(ForMatchedPlace, _4)">@1,2⦊countdown > @@ -107,9 +105,9 @@ } ; } - @7⦊countdown - -= - 1⦉@7 + @12⦊countdown + -= + 1⦉@12 ; } MirPass<'tcx> for InstrumentCoverage { /// a `Goto`, and merged with its successor into the same BCB. /// /// Each BCB with at least one computed `CoverageSpan` will have no more than one `Counter`. -/// In some cases, a BCB's execution count can be computed by `CounterExpression`. Additional -/// disjoint `CoverageSpan`s in a BCB can also be counted by `CounterExpression` (by adding `ZERO` +/// In some cases, a BCB's execution count can be computed by `Expression`. Additional +/// disjoint `CoverageSpan`s in a BCB can also be counted by `Expression` (by adding `ZERO` /// to the BCB's primary counter or expression). /// /// The BCB CFG is critical to simplifying the coverage analysis by ensuring graph path-based @@ -903,7 +903,7 @@ struct Instrumentor<'a, 'tcx> { function_source_hash: Option, next_counter_id: u32, num_expressions: u32, - expressions_cache: FxHashMap, + debug_expressions_cache: Option>, debug_counters: DebugCounters, } @@ -922,7 +922,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { function_source_hash: None, next_counter_id: CounterValueReference::START.as_u32(), num_expressions: 0, - expressions_cache: FxHashMap::default(), + debug_expressions_cache: None, debug_counters: DebugCounters::new(), } } @@ -935,8 +935,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { CounterValueReference::from(next) } - /// Expression IDs start from u32::MAX and go down because a CounterExpression can reference - /// (add or subtract counts) of both Counter regions and CounterExpression regions. The counter + /// Expression IDs start from u32::MAX and go down because a Expression can reference + /// (add or subtract counts) of both Counter regions and Expression regions. The counter /// expression operand IDs must be unique across both types. fn next_expression(&mut self) -> InjectedExpressionIndex { assert!(self.next_counter_id < u32::MAX - self.num_expressions); @@ -975,13 +975,11 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } let coverage_spans = self.coverage_spans(); - let span_viewables = if dump_spanview { Some(self.span_viewables(&coverage_spans)) } else { None }; - let mut collect_intermediate_expressions = Vec::with_capacity(self.basic_coverage_blocks.num_nodes()); - self.make_bcb_counters(&mut collect_intermediate_expressions); + // When debugging with BCB graphviz output, initialize additional data structures. let mut debug_bcb_to_coverage_spans_with_counters = None; let mut debug_bcb_to_dependency_counter = None; @@ -990,6 +988,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { debug_bcb_to_coverage_spans_with_counters = Some(FxHashMap::default()); debug_bcb_to_dependency_counter = Some(FxHashMap::default()); debug_edge_to_counter = Some(FxHashMap::default()); + + self.debug_expressions_cache.replace(FxHashMap::default()); } let mut debug_used_expression_operands = None; @@ -997,6 +997,14 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { debug_used_expression_operands = Some(FxHashMap::default()); } + // Analyze the coverage graph (aka, BCB control flow graph), and inject expression-optimized + // counters. + + self.make_bcb_counters(&mut collect_intermediate_expressions); + + // If debugging, add any intermediate expressions (which are not associated with any BCB) to + // the `debug_used_expression_operands` map. + if let Some(used_expression_operands) = debug_used_expression_operands.as_mut() { for intermediate_expression in &collect_intermediate_expressions { if let CoverageKind::Expression { id, lhs, rhs, .. } = *intermediate_expression { @@ -1009,8 +1017,11 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // Inject a counter for each `CoverageSpan`. There can be multiple `CoverageSpan`s for a // given BCB, but only one actual counter needs to be incremented per BCB. `bb_counters` // maps each `bcb` to its `Counter`, when injected. Subsequent `CoverageSpan`s - // for a BCB that already has a `Counter` will inject a `CounterExpression` instead, and + // for a BCB that already has a `Counter` will inject an `Expression` instead, and // compute its value by adding `ZERO` to the BCB `Counter` value. + // + // If debugging, add every BCB `Expression` associated with a `CoverageSpan`s to the + // `debug_used_expression_operands` map. let mut bcb_counters = IndexVec::from_elem_n(None, self.basic_coverage_blocks.num_nodes()); for covspan in coverage_spans { let bcb = covspan.bcb; @@ -1027,7 +1038,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } counter_kind } else { - bug!("Every BasicCoverageBlock should have a Counter or CounterExpression"); + bug!("Every BasicCoverageBlock should have a Counter or Expression"); }; // TODO(richkadel): uncomment debug! // debug!( @@ -1039,7 +1050,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { if let Some(bcb_to_coverage_spans_with_counters) = debug_bcb_to_coverage_spans_with_counters.as_mut() { bcb_to_coverage_spans_with_counters.entry(bcb).or_insert_with(|| Vec::new()).push((covspan.clone(), counter_kind.clone())); } - self.inject_statement(counter_kind, self.bcb_last_bb(bcb), make_code_region(file_name, &source_file, span)); + self.inject_statement(counter_kind, self.bcb_last_bb(bcb), make_code_region(file_name, &source_file, span, body_span)); } // The previous step looped through the `CoverageSpan`s and injected the counter from the @@ -1048,7 +1059,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // // Any other counter associated with a `BasicCoverageBlock`, or its incoming edge, but not // associated with a `CoverageSpan`, should only exist if the counter is a - // `CounterExpression` dependency (one of the expression operands). Collect them, and inject + // `Expression` dependency (one of the expression operands). Collect them, and inject // the additional counters into the MIR, without a reportable coverage span. let mut bcb_counters_without_direct_coverage_spans = Vec::new(); for (target_bcb, target_bcb_data) in self.basic_coverage_blocks.iter_enumerated_mut() { @@ -1084,6 +1095,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } } +// let (last_line, last_col) = source_file.lookup_file_pos(body_span.hi()); for (edge_counter_from_bcb, target_bcb, counter_kind) in bcb_counters_without_direct_coverage_spans { if let Some(used_expression_operands) = debug_used_expression_operands.as_ref() { if !used_expression_operands.contains_key(&counter_kind.as_operand_id()) { @@ -1091,71 +1103,91 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // simplification. For example, expression: x + (y - x) becomes just "y", and the // expression: y - x may no longer be a dependency. Similarly, expression: // x + (y + 0) becomes just x + y. - if let Some(from_bcb) = edge_counter_from_bcb.as_ref() { - debug!( + let unused_counter_message = if let Some(from_bcb) = edge_counter_from_bcb.as_ref() { + format!( "non-coverage edge counter found without a dependent expression, in {:?}->{:?}; counter={}", from_bcb, target_bcb, self.format_counter(&counter_kind), - ); + ) } else { - debug!( + format!( "non-coverage counter found without a dependent expression, in {:?}; counter={}", target_bcb, self.format_counter(&counter_kind), - ); + ) + }; + if self.debug_expressions_cache.is_some() { + // Expect some unused counters when simplifying expressions to optimize out + // some redundant arithmetic. + debug!("{}", unused_counter_message); + } else { + bug!("{}", unused_counter_message); } } } - let inject_to_bb = if let Some(from_bcb) = edge_counter_from_bcb { - // The MIR edge starts `from_bb` (the outgoing / last BasicBlock in `from_bcb`) and - // ends at `to_bb` (the incoming / first BasicBlock in the `target_bcb`; also called - // the `leader_bb`). - let from_bb = self.bcb_last_bb(from_bcb); - let to_bb = self.bcb_leader_bb(target_bcb); - - debug!( - "Edge {:?} (last {:?}) -> {:?} (leader {:?}) requires a new MIR BasicBlock, for unclaimed edge counter {}", - edge_counter_from_bcb, from_bb, target_bcb, to_bb, self.format_counter(&counter_kind), - ); - debug!( - " from_bb {:?} has successors: {:?}", - from_bb, self.mir_body[from_bb].terminator().successors(), - ); - let span = self.mir_body[from_bb].terminator().source_info.span.shrink_to_hi(); - let new_bb = self.mir_body.basic_blocks_mut().push(BasicBlockData { - statements: vec![], // counter will be injected here - terminator: Some(Terminator { - source_info: SourceInfo::outermost(span), - kind: TerminatorKind::Goto { target: to_bb }, - }), - is_cleanup: false, - }); - let edge_ref = self.mir_body[from_bb].terminator_mut().successors_mut().find(|successor| **successor == to_bb).expect("from_bb should have a successor for to_bb"); - *edge_ref = new_bb; + match counter_kind { + CoverageKind::Counter { .. } => { + let inject_to_bb = if let Some(from_bcb) = edge_counter_from_bcb { + // The MIR edge starts `from_bb` (the outgoing / last BasicBlock in `from_bcb`) and + // ends at `to_bb` (the incoming / first BasicBlock in the `target_bcb`; also called + // the `leader_bb`). + let from_bb = self.bcb_last_bb(from_bcb); + let to_bb = self.bcb_leader_bb(target_bcb); - if let Some(edge_to_counter) = debug_edge_to_counter.as_mut() { - debug!("from_bcb={:?} to new_bb={:?} has edge_counter={}", - from_bcb, new_bb, self.format_counter(&counter_kind)); - edge_to_counter.insert((from_bcb, new_bb), counter_kind.clone()).expect_none("invalid attempt to insert more than one edge counter for the same edge"); - } + debug!( + "Edge {:?} (last {:?}) -> {:?} (leader {:?}) requires a new MIR BasicBlock, for unclaimed edge counter {}", + edge_counter_from_bcb, from_bb, target_bcb, to_bb, self.format_counter(&counter_kind), + ); + debug!( + " from_bb {:?} has successors: {:?}", + from_bb, self.mir_body[from_bb].terminator().successors(), + ); + let span = self.mir_body[from_bb].terminator().source_info.span.shrink_to_hi(); + let new_bb = self.mir_body.basic_blocks_mut().push(BasicBlockData { + statements: vec![], // counter will be injected here + terminator: Some(Terminator { + source_info: SourceInfo::outermost(span), + kind: TerminatorKind::Goto { target: to_bb }, + }), + is_cleanup: false, + }); + let edge_ref = self.mir_body[from_bb].terminator_mut().successors_mut().find(|successor| **successor == to_bb).expect("from_bb should have a successor for to_bb"); + *edge_ref = new_bb; + + if let Some(edge_to_counter) = debug_edge_to_counter.as_mut() { + debug!("from_bcb={:?} to new_bb={:?} has edge_counter={}", + from_bcb, new_bb, self.format_counter(&counter_kind), + ); + edge_to_counter.insert((from_bcb, new_bb), counter_kind.clone()).expect_none("invalid attempt to insert more than one edge counter for the same edge"); + } - new_bb - } else { - if let Some(bcb_to_dependency_counter) = debug_bcb_to_dependency_counter.as_mut() { - bcb_to_dependency_counter.entry(target_bcb).or_insert_with(|| Vec::new()).push(counter_kind.clone()); + new_bb + } else { + if let Some(bcb_to_dependency_counter) = debug_bcb_to_dependency_counter.as_mut() { + bcb_to_dependency_counter.entry(target_bcb).or_insert_with(|| Vec::new()).push(counter_kind.clone()); + } + let target_bb = self.bcb_last_bb(target_bcb); + debug!( + "{:?} ({:?}) gets a new Coverage statement for unclaimed counter {}", + target_bcb, + target_bb, + self.format_counter(&counter_kind), + ); + target_bb + }; +// debug!("make_non_reportable_code_region for {:?} at last_line={:?}, last_col={:?}, counter={}", +// inject_to_bb, last_line, last_col, self.format_counter(&counter_kind)); +// self.inject_statement(counter_kind, inject_to_bb, make_non_reportable_code_region(file_name, last_line, last_col)); + let span = self.mir_body[inject_to_bb].terminator().source_info.span; + debug!("make_non_reportable_code_region for {:?} at span={:?}, counter={}", + inject_to_bb, span, self.format_counter(&counter_kind)); + self.inject_statement(counter_kind, inject_to_bb, make_non_reportable_code_region(file_name, &source_file, span)); } - let target_bb = self.bcb_last_bb(target_bcb); - debug!( - "{:?} ({:?}) gets a new Coverage statement for unclaimed counter {}", - target_bcb, - target_bb, - self.format_counter(&counter_kind), - ); - target_bb - }; - self.inject_statement(counter_kind, inject_to_bb, make_non_reportable_code_region(file_name, &source_file)); + CoverageKind::Expression { .. } => self.inject_intermediate_expression(counter_kind), + _ => bug!("CoverageKind should be a counter"), + } } if dump_graphviz { @@ -1251,13 +1283,13 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } } - /// Traverse the BCB CFG and add either a `Counter` or `CounterExpression` to ever BCB, to be - /// injected with `CoverageSpan`s. `CounterExpressions` have no runtime overhead, so if a viable + /// Traverse the BCB CFG and add either a `Counter` or `Expression` to ever BCB, to be + /// injected with `CoverageSpan`s. `Expressions` have no runtime overhead, so if a viable /// expression (adding or subtracting two other counters or expressions) can compute the same - /// result as an embedded counter, a `CounterExpression` should be used. + /// result as an embedded counter, an `Expression` should be used. /// /// If two `BasicCoverageBlocks` branch from another `BasicCoverageBlock`, one of the branches - /// can be counted by `CounterExpression` by subtracting the other branch from the branching + /// can be counted by `Expression` by subtracting the other branch from the branching /// block. Otherwise, the `BasicCoverageBlock` executed the least should have the `Counter`. /// One way to predict which branch executes the least is by considering loops. A loop is exited /// at a branch, so the branch that jumps to a `BasicCoverageBlock` outside the loop is almost @@ -1405,11 +1437,11 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { ); if !found_loop_exit { // No branches exit a loop, so there is no specific recommended branch for - // an `ExpressionCounter`. + // an `Expression`. break; } if some_reloop_branch.is_some() { - // A recommended branch for an `ExpressionCounter` was found. + // A recommended branch for an `Expression` was found. break; } // else all branches exited this loop context, so run the same checks with @@ -1436,8 +1468,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { branch_without_counter }; - // Assign a Counter or CounterExpression to each branch, plus additional - // `CounterExpression`s, as needed, to sum up intermediate results. + // Assign a Counter or Expression to each branch, plus additional + // `Expression`s, as needed, to sum up intermediate results. let mut some_sumup_counter_operand = None; for branch in branches { if branch != expression_branch { @@ -1497,8 +1529,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // // Does this mean that we should assert that a bcb ONLY has either a single bcb counter, or 2 or more edge counters, but not both? // WELL... not exactly. -// 1. A BCB with edge counters always has an ExpressionCounter -// 2. A BCB with multiple predecessors always gets an ExpressionCounter. +// 1. A BCB with edge counters always has an Expression +// 2. A BCB with multiple predecessors always gets an Expression. // // ??? // 3. If NO predecessor comes from a branching BCB, then the BCB Expression can be the sum of all predecessor BCB counters @@ -1673,7 +1705,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // is a branch that jumps outside the loop, and should get an actual Counter, most likely // // Or perhaps conversely, a branching BCB is dominated by H with a branch that has a target that - // ALSO is dominated by H should get a CounterExpression. + // ALSO is dominated by H should get a Expression. // // // So I need to identify all of the "H"'s, by identifying all of the backedges. @@ -1706,37 +1738,41 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { ) -> CoverageKind where F: Fn() -> String { - if let Some(CoverageKind::Expression { lhs: lhs_lhs, op, rhs: lhs_rhs, .. } ) = self.expressions_cache.get(&lhs) { - if *lhs_rhs == ExpressionOperandId::ZERO { - lhs = *lhs_lhs; - } else if *op == Op::Subtract && *lhs_rhs == rhs { - if let Some(lhs_expression) = self.expressions_cache.get(lhs_lhs) { - let expression = lhs_expression.clone(); - return self.as_duplicate_expression(expression); - } else { - let counter = *lhs_lhs; - return self.make_identity_counter(counter); + if let Some(expressions_cache) = self.debug_expressions_cache.as_ref() { + if let Some(CoverageKind::Expression { lhs: lhs_lhs, op, rhs: lhs_rhs, .. } ) = expressions_cache.get(&lhs) { + if *lhs_rhs == ExpressionOperandId::ZERO { + lhs = *lhs_lhs; + } else if *op == Op::Subtract && *lhs_rhs == rhs { + if let Some(lhs_expression) = expressions_cache.get(lhs_lhs) { + let expression = lhs_expression.clone(); + return self.as_duplicate_expression(expression); + } else { + let counter = *lhs_lhs; + return self.make_identity_counter(counter); + } } } - } - if let Some(CoverageKind::Expression { lhs: rhs_lhs, op, rhs: rhs_rhs, .. } ) = self.expressions_cache.get(&rhs) { - if *rhs_rhs == ExpressionOperandId::ZERO { - rhs = *rhs_rhs; - } else if *op == Op::Subtract && *rhs_rhs == lhs { - if let Some(rhs_expression) = self.expressions_cache.get(rhs_lhs) { - let expression = rhs_expression.clone(); - return self.as_duplicate_expression(expression); - } else { - let counter = *rhs_lhs; - return self.make_identity_counter(counter); + if let Some(CoverageKind::Expression { lhs: rhs_lhs, op, rhs: rhs_rhs, .. } ) = expressions_cache.get(&rhs) { + if *rhs_rhs == ExpressionOperandId::ZERO { + rhs = *rhs_rhs; + } else if *op == Op::Subtract && *rhs_rhs == lhs { + if let Some(rhs_expression) = expressions_cache.get(rhs_lhs) { + let expression = rhs_expression.clone(); + return self.as_duplicate_expression(expression); + } else { + let counter = *rhs_lhs; + return self.make_identity_counter(counter); + } } } } let id = self.next_expression(); let expression = CoverageKind::Expression { id, lhs, op, rhs }; - self.expressions_cache.insert(id.into(), expression.clone()); + if let Some(expressions_cache) = self.debug_expressions_cache.as_mut() { + expressions_cache.insert(id.into(), expression.clone()); + } if self.debug_counters.is_enabled() { self.debug_counters.add_expression(&expression, (debug_string_fn)()); } @@ -1744,10 +1780,16 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } fn as_duplicate_expression(&mut self, mut expression: CoverageKind) -> CoverageKind { + let next_expression_id = if self.debug_expressions_cache.is_some() { + Some(self.next_expression()) + } else { + None + }; + let expressions_cache = self.debug_expressions_cache.as_mut().expect("`as_duplicate_expression()` requires the debug_expressions_cache"); match expression { CoverageKind::Expression { ref mut id, .. } => { - *id = self.next_expression(); - self.expressions_cache.insert(id.into(), expression.clone()); + *id = next_expression_id.expect("next_expression_id should be Some if there is a debug_expressions_cache"); + expressions_cache.insert(id.into(), expression.clone()); } _ => bug!("make_duplicate_expression called with non-expression type: {:?}", expression), } @@ -1755,7 +1797,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } fn make_identity_counter(&mut self, counter_operand: ExpressionOperandId) -> CoverageKind { - if let Some(expression) = self.expressions_cache.get(&counter_operand) { + if let Some(expression) = self.debug_expressions_cache.as_ref().map_or(None, |c| c.get(&counter_operand)) { let new_expression = expression.clone(); self.as_duplicate_expression(new_expression) } else { @@ -1916,7 +1958,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { .unwrap() }); - let refinery = CoverageSpanRefinery::from_sorted_spans(initial_spans, &self.bcb_dominators); + let refinery = CoverageSpanRefinery::from_sorted_spans(initial_spans, &self.mir_body, &self.basic_coverage_blocks, &self.bcb_dominators); refinery.to_refined_spans() } } @@ -1928,12 +1970,18 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { /// * Merge spans that represent continuous (both in source code and control flow), non-branching /// execution /// * Carve out (leave uncovered) any span that will be counted by another MIR (notably, closures) -struct CoverageSpanRefinery<'a> { +struct CoverageSpanRefinery<'a, 'tcx> { /// The initial set of `CoverageSpan`s, sorted by `Span` (`lo` and `hi`) and by relative /// dominance between the `BasicCoverageBlock`s of equal `Span`s. sorted_spans_iter: std::vec::IntoIter, + /// The MIR, used to look up `BasicBlockData`. + mir_body: &'a mir::Body<'tcx>, + + /// The BasicCoverageBlock Control Flow Graph (BCB CFG). + basic_coverage_blocks: &'a BasicCoverageBlocks, + /// The BCB CFG's dominators tree, used to compute the dominance relationships, if any. bcb_dominators: &'a Dominators, @@ -1963,14 +2011,16 @@ struct CoverageSpanRefinery<'a> { /// `prev` with a matching `Span`) pending_dups: Vec, - /// The final `CoverageSpan`s to add to the coverage map. A `Counter` or `CounterExpression` + /// The final `CoverageSpan`s to add to the coverage map. A `Counter` or `Expression` /// will also be injected into the MIR for each `CoverageSpan`. refined_spans: Vec, } -impl<'a> CoverageSpanRefinery<'a> { +impl<'a, 'tcx> CoverageSpanRefinery<'a, 'tcx> { fn from_sorted_spans( sorted_spans: Vec, + mir_body: &'a mir::Body<'tcx>, + basic_coverage_blocks: &'a BasicCoverageBlocks, bcb_dominators: &'a Dominators, ) -> Self { let refined_spans = Vec::with_capacity(sorted_spans.len()); @@ -1979,6 +2029,8 @@ impl<'a> CoverageSpanRefinery<'a> { let prev_original_span = prev.span; Self { sorted_spans_iter, + mir_body, + basic_coverage_blocks, bcb_dominators, refined_spans, some_curr: None, @@ -2028,13 +2080,29 @@ impl<'a> CoverageSpanRefinery<'a> { debug!(" AT END, adding last prev={:?}", self.prev()); let prev = self.take_prev(); - let CoverageSpanRefinery { pending_dups, mut refined_spans, .. } = self; + let CoverageSpanRefinery { mir_body, basic_coverage_blocks, pending_dups, mut refined_spans, .. } = self; for dup in pending_dups { debug!(" ...adding at least one pending dup={:?}", dup); refined_spans.push(dup); } refined_spans.push(prev); + // Remove `CoverageSpan`s with empty spans ONLY if the empty `CoverageSpan`s BCB also has at + // least one other non-empty `CoverageSpan`. + let mut has_coverage = BitSet::new_empty(basic_coverage_blocks.num_nodes()); + for covspan in &refined_spans { + if ! covspan.span.is_empty() { + has_coverage.insert(covspan.bcb); + } + } + refined_spans.retain(|covspan| { + !( + covspan.span.is_empty() + && is_goto(&basic_coverage_blocks[covspan.bcb].terminator(mir_body).kind) + && has_coverage.contains(covspan.bcb) + ) + }); + // Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage // regions for the current function leave room for the closure's own coverage regions // (injected separately, from the closure's own MIR). @@ -2350,7 +2418,7 @@ fn filtered_statement_span(statement: &'a Statement<'tcx>, body_span: Span) -> O | StatementKind::LlvmInlineAsm(_) | StatementKind::Retag(_, _) | StatementKind::AscribeUserType(_, _) => { - Some(source_info_span(&statement.source_info, body_span)) + Some(function_source_span(statement.source_info.span, body_span)) } } } @@ -2367,10 +2435,44 @@ fn filtered_terminator_span(terminator: &'a Terminator<'tcx>, body_span: Span) - | TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } | TerminatorKind::SwitchInt { .. } - | TerminatorKind::Goto { .. } +// TODO(richkadel): Should Goto still be here? Delete if not +// | TerminatorKind::Goto { .. } // For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`. | TerminatorKind::FalseEdge { .. } => None, +// TODO(richkadel): Add a comment if this works. Any `Goto` still in the BCB CFG is probably +// required, as an intermediate target for some conditional branches (typically, `SwitchInt` +// targets). Some of these `Goto` branch blocks (BCB and BB) have multiple incoming edges (that is, +// not just a single incoming edge from a SwitchInt), so they are often required to accurately +// represent the control flow. +// +// Since these retained `Goto` terminators generally impact control flow, they often generate their +// own `BasicCoverageBlock`, but their `source_info.span` typically covers the source code for an +// entire branch (that might or might not be executed, depending on the condition that may (or may +// not) lead to the `Goto` branch. This source span is far too broad. For `Goto` terminators that +// are part of a BCB with other statements/terminators with usable `Span`s, ignoring the `Goto` +// `Span` is OK, because the other statements/terminators in the BCB generate `CoverageSpans` +// that represent the code executed leading up to (and including) that `Goto`. +// +// But for `Goto` terminators without other statements/terminators, there needs to be some visible +// region to count, that doesn't include code that was not executed. So in this case, we set the +// `Span` to the 0-length span from and to `source_info.span.hi()`. +// +// At least one character will be covered--the character immediately following the `Span` position. +// (`make_code_region()` ensures all zero-length spans are extended to at least one character.) +// +// Note that, if we did not select some non-zero length `Span` at this location, but the `Goto`s +// BCB still needed to be counted (other `CoverageSpan`-dependent `Expression`s may depend on the +// execution count for this `Goto`), we have at least two other less-appealing choices: +// 1. Insert a truly zero-length span on some line. In this case, there may be no visible +// coverage region, but its line is still counted, which is more confusing. +// 2. Insert a span for a line beyond the end of the file. This works well for `llvm-cov show` +// reports, but `llvm-cov export` reports still count the lines and regions outside the +// file's line range. + TerminatorKind::Goto { .. } => { + Some(function_source_span(terminator.source_info.span.shrink_to_hi(), body_span)) + } + // Retain spans from all other terminators TerminatorKind::Resume | TerminatorKind::Abort @@ -2380,17 +2482,23 @@ fn filtered_terminator_span(terminator: &'a Terminator<'tcx>, body_span: Span) - | TerminatorKind::GeneratorDrop | TerminatorKind::FalseUnwind { .. } | TerminatorKind::InlineAsm { .. } => { - Some(source_info_span(&terminator.source_info, body_span)) + Some(function_source_span(terminator.source_info.span, body_span)) } } } #[inline(always)] -fn source_info_span(source_info: &SourceInfo, body_span: Span) -> Span { - let span = original_sp(source_info.span, body_span).with_ctxt(SyntaxContext::root()); +fn function_source_span(span: Span, body_span: Span) -> Span { + let span = original_sp(span, body_span).with_ctxt(SyntaxContext::root()); if body_span.contains(span) { span } else { body_span } } +// TODO(richkadel): Update this function comment. +// It only works to use an empty span on an actual source line if the line already has +// a coverage code region. It works because +// compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +// knows to look for empty spans, and generate a `GapRegion` instead of a `CodeRegion`. +// /// Make a non-reportable code region /// Count empty spans, but don't make them reportable as coverage. Set the source /// position out of range. (Note that `llvm-cov` fails to report coverage if any @@ -2398,15 +2506,34 @@ fn source_info_span(source_info: &SourceInfo, body_span: Span) -> Span { /// the last line of the file.) fn make_non_reportable_code_region( file_name: Symbol, +// last_line: usize, +// last_col: CharPos, source_file: &Lrc, + span: Span, ) -> CodeRegion { - let line_past_end_of_file = (source_file.lines.len() + 1) as u32; + // let line_past_end_of_file = (source_file.lines.len() + 1) as u32; + // CodeRegion { + // file_name, + // start_line: line_past_end_of_file, + // start_col: 1, + // end_line: line_past_end_of_file, + // end_col: 1, + // } + // CodeRegion { + // file_name, + // start_line: last_line as u32, + // start_col: last_col.to_u32() + 1, + // end_line: last_line as u32, + // end_col: last_col.to_u32() + 1, + // } + let (start_line, start_col) = source_file.lookup_file_pos(span.hi()); + let (end_line, end_col) = (start_line, start_col); CodeRegion { file_name, - start_line: line_past_end_of_file, - start_col: 1, - end_line: line_past_end_of_file, - end_col: 1, + start_line: start_line as u32, + start_col: start_col.to_u32() + 1, + end_line: end_line as u32, + end_col: end_col.to_u32() + 1, } } @@ -2415,13 +2542,14 @@ fn make_code_region( file_name: Symbol, source_file: &Lrc, span: Span, + body_span: Span, ) -> CodeRegion { let (start_line, mut start_col) = source_file.lookup_file_pos(span.lo()); let (end_line, end_col) = if span.hi() == span.lo() { let (end_line, mut end_col) = (start_line, start_col); // Extend an empty span by one character so the region will be counted. let CharPos(char_pos) = start_col; - if char_pos > 0 { + if span.hi() == body_span.hi() { start_col = CharPos(char_pos - 1); } else { end_col = CharPos(char_pos + 1); @@ -2439,6 +2567,14 @@ fn make_code_region( } } +#[inline(always)] +fn is_goto(term_kind: &TerminatorKind<'tcx>) -> bool { + match term_kind { + TerminatorKind::Goto { .. } => true, + _ => false, + } +} + fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx rustc_hir::Body<'tcx> { let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local"); let fn_body_id = hir::map::associated_body(hir_node).expect("HIR node is a function with body"); @@ -2543,7 +2679,7 @@ impl<'a: 'tcx, 'tcx, F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>> // Injecting unreachable code regions will probably require computing the set difference between the // basic blocks found without filtering out unreachable blocks, and the basic blocks found with a // filter (similar to or as an extension of the `filter_unwind_paths` filter); then computing the -// `CoverageSpans` without the filter; and then injecting `Counter`s or `CounterExpression`s for +// `CoverageSpans` without the filter; and then injecting `Counter`s or `Expression`s for // blocks that are not unreachable, or injecting `Unreachable` code regions otherwise. This seems // straightforward, but not trivial. // diff --git a/src/test/run-make-fulldeps/coverage/lazy_boolean.rs b/src/test/run-make-fulldeps/coverage/lazy_boolean.rs index 1d83eb30dea24..bb6219e851c8a 100644 --- a/src/test/run-make-fulldeps/coverage/lazy_boolean.rs +++ b/src/test/run-make-fulldeps/coverage/lazy_boolean.rs @@ -26,18 +26,36 @@ fn main() { || b < c ; - let - somebool - = - a < b - && - b < c - ; - let - somebool - = - b < a - && - b < c - ; + let somebool = a < b && b < c; + let somebool = b < a && b < c; + + if + ! + is_true + { + a = 2 + ; + } + + if + is_true + { + b = 30 + ; + } + else + { + c = 400 + ; + } + + if !is_true { + a = 2; + } + + if is_true { + b = 30; + } else { + c = 400; + } } From bce5815caa230eac12aeea6bea188caea3533e78 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Fri, 16 Oct 2020 08:27:47 -0700 Subject: [PATCH 06/18] Generally working expressions Still not fully optimized to remove unneeded expressions, or expressions that complicate the MIR a bit without really improving coverage or efficiency. Getting some coverage results that don't seem right: * In three tests, the last line of the function (last brace) is counted twice. * In simple_match, the { let z; match is counted zero times (and used to be uncovered) but how would a developer ever get non-zero coverage for this code? --- .../src/coverageinfo/mod.rs | 4 +- .../rustc_codegen_ssa/src/coverageinfo/ffi.rs | 12 +- .../rustc_codegen_ssa/src/coverageinfo/map.rs | 58 ++-- .../src/traits/coverageinfo.rs | 2 +- compiler/rustc_middle/src/mir/coverage.rs | 31 +- .../rustc_middle/src/ty/structural_impls.rs | 1 + .../src/transform/instrument_coverage.rs | 154 +++++++--- .../rustc_mir/src/util/generic_graphviz.rs | 2 +- ...age_graphviz.main.InstrumentCoverage.0.dot | 4 +- ...ment_coverage.main.InstrumentCoverage.diff | 5 +- .../coverage-reports-base/Makefile | 17 +- .../expected_export_coverage.closure.json | 8 +- ...expected_export_coverage.lazy_boolean.json | 28 +- .../expected_export_coverage.simple_loop.json | 20 +- ...expected_export_coverage.simple_match.json | 24 +- ...cted_export_coverage.try_error_result.json | 24 +- ...ed_export_coverage.various_conditions.json | 24 +- ...ed_export_coverage.while_early_return.json | 12 +- .../expected_show_coverage.closure.txt | 4 +- .../expected_show_coverage.if.txt | 1 + .../expected_show_coverage.inner_items.txt | 2 + .../expected_show_coverage.lazy_boolean.txt | 50 +++- .../expected_show_coverage.simple_loop.txt | 3 +- .../expected_show_coverage.simple_match.txt | 12 +- ...xpected_show_coverage.try_error_result.txt | 8 +- ...ected_show_coverage.various_conditions.txt | 5 +- ...ected_show_coverage.while_early_return.txt | 2 +- ...xpected_show_coverage_counters.closure.txt | 95 ++++++ ...cted_show_coverage_counters.drop_trait.txt | 23 ++ ...pected_show_coverage_counters.generics.txt | 49 ++++ .../expected_show_coverage_counters.if.txt | 22 ++ ...xpected_show_coverage_counters.if_else.txt | 31 ++ ...ted_show_coverage_counters.inner_items.txt | 61 ++++ ...ed_show_coverage_counters.lazy_boolean.txt | 142 +++++++++ ...how_coverage_counters.loop_break_value.txt | 7 + ...ted_show_coverage_counters.simple_loop.txt | 38 +++ ...ed_show_coverage_counters.simple_match.txt | 60 ++++ ...how_coverage_counters.try_error_result.txt | 69 +++++ ...w_coverage_counters.various_conditions.txt | 273 ++++++++++++++++++ ...w_coverage_counters.while_early_return.txt | 40 +++ .../expected_export_coverage.closure.json | 8 +- ...expected_export_coverage.lazy_boolean.json | 28 +- .../expected_export_coverage.simple_loop.json | 20 +- ...expected_export_coverage.simple_match.json | 24 +- ...cted_export_coverage.try_error_result.json | 24 +- ...ed_export_coverage.various_conditions.json | 24 +- ...ed_export_coverage.while_early_return.json | 12 +- .../expected_show_coverage.closure.txt | 4 +- .../expected_show_coverage.if.txt | 1 + .../expected_show_coverage.inner_items.txt | 2 + .../expected_show_coverage.lazy_boolean.txt | 50 +++- .../expected_show_coverage.simple_loop.txt | 3 +- .../expected_show_coverage.simple_match.txt | 12 +- ...xpected_show_coverage.try_error_result.txt | 8 +- ...ected_show_coverage.various_conditions.txt | 5 +- ...ected_show_coverage.while_early_return.txt | 2 +- ...xpected_show_coverage_counters.closure.txt | 95 ++++++ ...cted_show_coverage_counters.drop_trait.txt | 23 ++ ...pected_show_coverage_counters.generics.txt | 49 ++++ .../expected_show_coverage_counters.if.txt | 22 ++ ...xpected_show_coverage_counters.if_else.txt | 31 ++ ...ted_show_coverage_counters.inner_items.txt | 61 ++++ ...ed_show_coverage_counters.lazy_boolean.txt | 142 +++++++++ ...how_coverage_counters.loop_break_value.txt | 7 + ...ted_show_coverage_counters.simple_loop.txt | 38 +++ ...ed_show_coverage_counters.simple_match.txt | 60 ++++ ...how_coverage_counters.try_error_result.txt | 69 +++++ ...w_coverage_counters.various_conditions.txt | 273 ++++++++++++++++++ ...w_coverage_counters.while_early_return.txt | 40 +++ ...osure#0}.-------.InstrumentCoverage.0.html | 13 +- ...osure#1}.-------.InstrumentCoverage.0.html | 13 +- ...osure#2}.-------.InstrumentCoverage.0.html | 13 +- ...osure#3}.-------.InstrumentCoverage.0.html | 13 +- .../if.main.-------.InstrumentCoverage.0.html | 20 +- ...ems.main.-------.InstrumentCoverage.0.html | 26 +- ...ean.main.-------.InstrumentCoverage.0.html | 143 ++++++--- ...oop.main.-------.InstrumentCoverage.0.html | 34 ++- ...tch.main.-------.InstrumentCoverage.0.html | 27 +- ...ult.main.-------.InstrumentCoverage.0.html | 8 +- ...ons.main.-------.InstrumentCoverage.0.html | 129 +++++---- ...urn.main.-------.InstrumentCoverage.0.html | 2 +- ...osure#0}.-------.InstrumentCoverage.0.html | 13 +- ...osure#1}.-------.InstrumentCoverage.0.html | 13 +- ...osure#2}.-------.InstrumentCoverage.0.html | 13 +- ...osure#3}.-------.InstrumentCoverage.0.html | 13 +- .../if.main.-------.InstrumentCoverage.0.html | 20 +- ...ems.main.-------.InstrumentCoverage.0.html | 26 +- ...ean.main.-------.InstrumentCoverage.0.html | 143 ++++++--- ...oop.main.-------.InstrumentCoverage.0.html | 34 ++- ...tch.main.-------.InstrumentCoverage.0.html | 27 +- ...ult.main.-------.InstrumentCoverage.0.html | 8 +- ...ons.main.-------.InstrumentCoverage.0.html | 129 +++++---- ...urn.main.-------.InstrumentCoverage.0.html | 2 +- 93 files changed, 2814 insertions(+), 632 deletions(-) create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.closure.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.drop_trait.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.generics.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.if.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.if_else.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.inner_items.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.lazy_boolean.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.loop_break_value.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.simple_loop.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.simple_match.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.try_error_result.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.various_conditions.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while_early_return.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.closure.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.drop_trait.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.generics.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.if.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.if_else.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.inner_items.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.lazy_boolean.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.loop_break_value.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.simple_loop.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.simple_match.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.try_error_result.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.various_conditions.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while_early_return.txt diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 7a7e8276c8763..c5a3e46f9dfbc 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -12,7 +12,7 @@ use rustc_codegen_ssa::traits::{ use rustc_data_structures::fx::FxHashMap; use rustc_llvm::RustString; use rustc_middle::mir::coverage::{ - CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionIndex, Op, + CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId, Op, }; use rustc_middle::ty::Instance; @@ -80,7 +80,7 @@ impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { fn add_coverage_counter_expression( &mut self, instance: Instance<'tcx>, - id: InjectedExpressionIndex, + id: InjectedExpressionId, lhs: ExpressionOperandId, op: Op, rhs: ExpressionOperandId, diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs index a266d179a421b..bcac2c90fdc20 100644 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs @@ -3,7 +3,7 @@ use rustc_middle::mir::coverage::{CounterValueReference, MappedExpressionIndex}; /// Aligns with [llvm::coverage::Counter::CounterKind](https://github.com/rust-lang/llvm-project/blob/rustc/10.0-2020-05-05/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L91) #[derive(Copy, Clone, Debug)] #[repr(C)] -enum CounterKind { +pub enum CounterKind { Zero = 0, CounterValueReference = 1, Expression = 2, @@ -23,8 +23,8 @@ enum CounterKind { #[repr(C)] pub struct Counter { // Important: The layout (order and types of fields) must match its C++ counterpart. - kind: CounterKind, - id: u32, + pub kind: CounterKind, + pub id: u32, } impl Counter { @@ -55,9 +55,9 @@ pub enum ExprKind { #[derive(Copy, Clone, Debug)] #[repr(C)] pub struct CounterExpression { - kind: ExprKind, - lhs: Counter, - rhs: Counter, + pub kind: ExprKind, + pub lhs: Counter, + pub rhs: Counter, } impl CounterExpression { diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs index 9f2999702d8a5..1b7e20e15bf1f 100644 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs @@ -2,8 +2,8 @@ pub use super::ffi::*; use rustc_index::vec::IndexVec; use rustc_middle::mir::coverage::{ - CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionIndex, - MappedExpressionIndex, Op, + CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId, + InjectedExpressionIndex, MappedExpressionIndex, Op, }; use rustc_middle::ty::Instance; use rustc_middle::ty::TyCtxt; @@ -78,12 +78,13 @@ impl FunctionCoverage { /// counters and expressions have been added. pub fn add_counter_expression( &mut self, - expression_id: InjectedExpressionIndex, + expression_id: InjectedExpressionId, lhs: ExpressionOperandId, op: Op, rhs: ExpressionOperandId, region: Option, ) { + debug!("add_counter_expression({:?}, lhs={:?}, op={:?}, rhs={:?} at {:?}", expression_id, lhs, op, rhs, region); let expression_index = self.expression_index(u32::from(expression_id)); self.expressions[expression_index] .replace(Expression { lhs, op, rhs, region }) @@ -134,18 +135,14 @@ impl FunctionCoverage { let mut counter_expressions = Vec::with_capacity(self.expressions.len()); let mut expression_regions = Vec::with_capacity(self.expressions.len()); let mut new_indexes = - IndexVec::from_elem_n(MappedExpressionIndex::from(u32::MAX), self.expressions.len()); - // Note, the initial value shouldn't matter since every index in use in `self.expressions` - // will be set, and after that, `new_indexes` will only be accessed using those same - // indexes. - + IndexVec::from_elem_n(None, self.expressions.len()); // Note that an `Expression`s at any given index can include other expressions as // operands, but expression operands can only come from the subset of expressions having // `expression_index`s lower than the referencing `Expression`. Therefore, it is // reasonable to look up the new index of an expression operand while the `new_indexes` // vector is only complete up to the current `ExpressionIndex`. let id_to_counter = - |new_indexes: &IndexVec, + |new_indexes: &IndexVec>, id: ExpressionOperandId| { if id == ExpressionOperandId::ZERO { Some(Counter::zero()) @@ -153,44 +150,21 @@ impl FunctionCoverage { let index = CounterValueReference::from(id.index()); self.counters .get(index) - .unwrap() // pre-validated - // TODO(richkadel): is it really pre-validated? - // What if I add some counters that never get added to the map, and they are - // larger than the number of counters in the MIR (as seems to happen with expressions below?) + .expect("counter id is out of range") .as_ref() .map(|_| Counter::counter_value_reference(index)) } else { let index = self.expression_index(u32::from(id)); - // TODO(richkadel): remove this debug - debug!( - "id_to_counter expression id={:?}, self.expressions.get(index={:?}) = {:?}", - id, - index, - self.expressions.get(index) - ); self.expressions .get(index) - // TODO(richkadel): Now some tests generate segfault, and other tests hit this out of range error - // Some expressions reference blocks that ended up not needing counters. - // Can we assume the expression is no longer relevant? If not, then instrument_counters - // transform pass will need to figure this out earlier (MAYBE IT SHOULD ANYWAY?) - // and if the counter is needed for an expression that can no longer be resolved, - // create a new make_counter() right there? - // - // MUST FIX! - // - // It looks like the segfault is at: - // - // /usr/local/google/home/richkadel/rust/src/llvm-project/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp:93 - // AdjustedExpressionIDs[ID] = 1; - // - // I think we have expressions with operand IDs that don't exist as either counters or expressions, and that's breaking - // the LLVM code. - // TODO(richkadel): replace expect() with unwrap_or()? .expect("expression id is out of range") - // .unwrap_or(&None) .as_ref() - .map(|_| Counter::expression(new_indexes[index])) + // If an expression was optimized out, assume it would have produced a count + // of zero. This ensures that expressions dependent on optimized-out + // expressions are still valid. + .map_or(Some(Counter::zero()), |_| { + new_indexes[index].map(|new_index| Counter::expression(new_index)) + }) } }; @@ -201,6 +175,8 @@ impl FunctionCoverage { entry.as_ref().map(|expression| (original_index, expression)) }) { + // TODO(richkadel): remove this debug: + debug!("Attempting to add {:?} = {:?}", original_index, expression); let optional_region = &expression.region; let Expression { lhs, op, rhs, .. } = *expression; @@ -209,6 +185,8 @@ impl FunctionCoverage { id_to_counter(&new_indexes, rhs).map(|rhs_counter| (lhs_counter, rhs_counter)) }) { + debug_assert!((lhs_counter.id as usize) < usize::max(self.counters.len(), self.expressions.len())); + debug_assert!((rhs_counter.id as usize) < usize::max(self.counters.len(), self.expressions.len())); // Both operands exist. `Expression` operands exist in `self.expressions` and have // been assigned a `new_index`. let mapped_expression_index = @@ -226,7 +204,7 @@ impl FunctionCoverage { mapped_expression_index, expression, optional_region ); counter_expressions.push(expression); - new_indexes[original_index] = mapped_expression_index; + new_indexes[original_index] = Some(mapped_expression_index); if let Some(region) = optional_region { expression_regions.push((Counter::expression(mapped_expression_index), region)); } diff --git a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs index 414c1be98261b..5aca48dc3f90e 100644 --- a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs @@ -20,7 +20,7 @@ pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { fn add_coverage_counter_expression( &mut self, instance: Instance<'tcx>, - id: InjectedExpressionIndex, + id: InjectedExpressionId, lhs: ExpressionOperandId, op: Op, rhs: ExpressionOperandId, diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 29b885136122e..92ca187632d26 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -7,6 +7,10 @@ use std::cmp::Ord; use std::fmt::{self, Debug, Formatter}; rustc_index::newtype_index! { + /// An ExpressionOperandId value is assigned directly from either a + /// CounterValueReference.as_u32() (which ascend from 1) or an ExpressionOperandId.as_u32() + /// (which _*descend*_ from u32::MAX). Id value `0` (zero) represents a virtual counter with a + /// constant value of `0`. pub struct ExpressionOperandId { derive [HashStable] DEBUG_FORMAT = "ExpressionOperandId({})", @@ -42,6 +46,20 @@ impl CounterValueReference { } rustc_index::newtype_index! { + /// InjectedExpressionId.as_u32() converts to ExpressionOperandId.as_u32() + /// + /// Values descend from u32::MAX. + pub struct InjectedExpressionId { + derive [HashStable] + DEBUG_FORMAT = "InjectedExpressionId({})", + MAX = 0xFFFF_FFFF, + } +} + +rustc_index::newtype_index! { + /// InjectedExpressionIndex.as_u32() translates to u32::MAX - ExpressionOperandId.as_u32() + /// + /// Values ascend from 0. pub struct InjectedExpressionIndex { derive [HashStable] DEBUG_FORMAT = "InjectedExpressionIndex({})", @@ -50,6 +68,9 @@ rustc_index::newtype_index! { } rustc_index::newtype_index! { + /// MappedExpressionIndex values ascend from zero, and are recalculated indexes based on their + /// array position in the LLVM coverage map "Expressions" array, which is assembled during the + /// "mapgen" process. They cannot be computed algorithmically, from the other `newtype_index`s. pub struct MappedExpressionIndex { derive [HashStable] DEBUG_FORMAT = "MappedExpressionIndex({})", @@ -71,16 +92,16 @@ impl From<&mut CounterValueReference> for ExpressionOperandId { } } -impl From for ExpressionOperandId { +impl From for ExpressionOperandId { #[inline] - fn from(v: InjectedExpressionIndex) -> ExpressionOperandId { + fn from(v: InjectedExpressionId) -> ExpressionOperandId { ExpressionOperandId::from(v.as_u32()) } } -impl From<&mut InjectedExpressionIndex> for ExpressionOperandId { +impl From<&mut InjectedExpressionId> for ExpressionOperandId { #[inline] - fn from(v: &mut InjectedExpressionIndex) -> ExpressionOperandId { + fn from(v: &mut InjectedExpressionId) -> ExpressionOperandId { ExpressionOperandId::from(v.as_u32()) } } @@ -92,7 +113,7 @@ pub enum CoverageKind { id: CounterValueReference, }, Expression { - id: InjectedExpressionIndex, + id: InjectedExpressionId, lhs: ExpressionOperandId, op: Op, rhs: ExpressionOperandId, diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 15803dbd8429f..1bfeed1700e88 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -299,6 +299,7 @@ CloneTypeFoldableAndLiftImpls! { ::rustc_target::spec::abi::Abi, crate::mir::coverage::ExpressionOperandId, crate::mir::coverage::CounterValueReference, + crate::mir::coverage::InjectedExpressionId, crate::mir::coverage::InjectedExpressionIndex, crate::mir::coverage::MappedExpressionIndex, crate::mir::Local, diff --git a/compiler/rustc_mir/src/transform/instrument_coverage.rs b/compiler/rustc_mir/src/transform/instrument_coverage.rs index 61cfbafe1f888..8f152f9de387b 100644 --- a/compiler/rustc_mir/src/transform/instrument_coverage.rs +++ b/compiler/rustc_mir/src/transform/instrument_coverage.rs @@ -864,9 +864,14 @@ impl DebugCounters { } fn format_expression(&self, expression: &CoverageKind) -> String { - if let CoverageKind::Expression { lhs, op, rhs, .. } = *expression { + if let CoverageKind::Expression { id, lhs, op, rhs } = *expression { format!( - "{} {} {}", + "{}{} {} {}", + if self.some_expressions.is_some() { + String::new() + } else { + format!("#{} = ", id.index() ) + }, self.format_operand(lhs), if op == Op::Add { "+" } else { "-" }, self.format_operand(rhs), @@ -938,11 +943,11 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { /// Expression IDs start from u32::MAX and go down because a Expression can reference /// (add or subtract counts) of both Counter regions and Expression regions. The counter /// expression operand IDs must be unique across both types. - fn next_expression(&mut self) -> InjectedExpressionIndex { + fn next_expression(&mut self) -> InjectedExpressionId { assert!(self.next_counter_id < u32::MAX - self.num_expressions); let next = u32::MAX - self.num_expressions; self.num_expressions += 1; - InjectedExpressionIndex::from(next) + InjectedExpressionId::from(next) } fn function_source_hash(&mut self) -> u64 { @@ -979,8 +984,38 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { if dump_spanview { Some(self.span_viewables(&coverage_spans)) } else { None }; let mut collect_intermediate_expressions = Vec::with_capacity(self.basic_coverage_blocks.num_nodes()); - // When debugging with BCB graphviz output, initialize additional data structures. + // When debug logging, or generating the coverage graphviz output, initialize the following + // data structures: + let mut debug_used_expression_operands = None; + let mut debug_unused_expressions = None; + if level_enabled!(tracing::Level::DEBUG) || dump_graphviz { + debug_used_expression_operands = Some(FxHashMap::default()); + debug_unused_expressions = Some(Vec::new()); + + const SIMPLIFY_EXPRESSIONS: bool = false; + if SIMPLIFY_EXPRESSIONS { + self.debug_expressions_cache.replace(FxHashMap::default()); + } + // CAUTION! The `SIMPLIFY_EXPRESSIONS` option is only helpful for some debugging + // situations and it can change the generated MIR `Coverage` statements (resulting in + // differences in behavior when enabled, under `DEBUG`, compared to normal operation and + // testing). + // + // For debugging purposes, it is sometimes helpful to simplify some expression equations: + // + // * `x + (y - x)` becomes just `y` + // * `x + (y + 0)` becomes just x + y. + // + // Expression dependencies can deeply nested expressions, which can look quite long in + // printed debug messages and in graphs produced by `-Zdump-graphviz`. In reality, each + // referenced/nested expression is only present because that value is necessary to + // compute a counter value for another part of the coverage report. Simplifying expressions + // Does not result in less `Coverage` statements, so there is very little, if any, benefit + // to binary size or runtime to simplifying expressions, and adds additional compile-time + // complexity. Only enable this temporarily, if helpful to parse the debug output. + } + // When debugging with BCB graphviz output, initialize additional data structures. let mut debug_bcb_to_coverage_spans_with_counters = None; let mut debug_bcb_to_dependency_counter = None; let mut debug_edge_to_counter = None; @@ -988,13 +1023,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { debug_bcb_to_coverage_spans_with_counters = Some(FxHashMap::default()); debug_bcb_to_dependency_counter = Some(FxHashMap::default()); debug_edge_to_counter = Some(FxHashMap::default()); - - self.debug_expressions_cache.replace(FxHashMap::default()); - } - - let mut debug_used_expression_operands = None; - if level_enabled!(tracing::Level::DEBUG) { - debug_used_expression_operands = Some(FxHashMap::default()); } // Analyze the coverage graph (aka, BCB control flow graph), and inject expression-optimized @@ -1097,33 +1125,15 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // let (last_line, last_col) = source_file.lookup_file_pos(body_span.hi()); for (edge_counter_from_bcb, target_bcb, counter_kind) in bcb_counters_without_direct_coverage_spans { - if let Some(used_expression_operands) = debug_used_expression_operands.as_ref() { + if let ( + Some(used_expression_operands), + Some(unused_expressions), + ) = ( + debug_used_expression_operands.as_ref(), + debug_unused_expressions.as_mut(), + ) { if !used_expression_operands.contains_key(&counter_kind.as_operand_id()) { - // These unused counters may happen for expressions replaced during expression - // simplification. For example, expression: x + (y - x) becomes just "y", and the - // expression: y - x may no longer be a dependency. Similarly, expression: - // x + (y + 0) becomes just x + y. - let unused_counter_message = if let Some(from_bcb) = edge_counter_from_bcb.as_ref() { - format!( - "non-coverage edge counter found without a dependent expression, in {:?}->{:?}; counter={}", - from_bcb, - target_bcb, - self.format_counter(&counter_kind), - ) - } else { - format!( - "non-coverage counter found without a dependent expression, in {:?}; counter={}", - target_bcb, - self.format_counter(&counter_kind), - ) - }; - if self.debug_expressions_cache.is_some() { - // Expect some unused counters when simplifying expressions to optimize out - // some redundant arithmetic. - debug!("{}", unused_counter_message); - } else { - bug!("{}", unused_counter_message); - } + unused_expressions.push((counter_kind.clone(), edge_counter_from_bcb, target_bcb)); } } @@ -1162,7 +1172,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { ); edge_to_counter.insert((from_bcb, new_bb), counter_kind.clone()).expect_none("invalid attempt to insert more than one edge counter for the same edge"); } - new_bb } else { if let Some(bcb_to_dependency_counter) = debug_bcb_to_dependency_counter.as_mut() { @@ -1221,15 +1230,35 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } }).collect::>() }; - let graphviz_writer = GraphvizWriter::new( + let mut graphviz_writer = GraphvizWriter::new( &self.basic_coverage_blocks, &graphviz_name, node_content, edge_labels, ); - // If there is any additional information to show, not related to a specific node or - // edge, add it with: - // graphviz_writer.set_graph_label(""); + if let Some(unused_expressions) = debug_unused_expressions.as_ref() { + if unused_expressions.len() > 0 { + graphviz_writer.set_graph_label(&format!( + "Unused expressions:\n {}", + unused_expressions.as_slice().iter().map(|(counter_kind, edge_counter_from_bcb, target_bcb)| { + if let Some(from_bcb) = edge_counter_from_bcb.as_ref() { + format!( + "{:?}->{:?}: {}", + from_bcb, + target_bcb, + self.format_counter(&counter_kind), + ) + } else { + format!( + "{:?}: {}", + target_bcb, + self.format_counter(&counter_kind), + ) + } + }).collect::>().join("\n ") + )); + } + } let mut file = pretty::create_dump_file(tcx, "dot", None, self.pass_name, &0, mir_source) .expect("Unexpected error creating BasicCoverageBlock graphviz DOT file"); @@ -1238,6 +1267,45 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { .expect("Unexpected error writing BasicCoverageBlock graphviz DOT file"); } + if let Some(unused_expressions) = debug_unused_expressions.as_ref() { + for (counter_kind, edge_counter_from_bcb, target_bcb) in unused_expressions { + let unused_counter_message = if let Some(from_bcb) = edge_counter_from_bcb.as_ref() { + format!( + "non-coverage edge counter found without a dependent expression, in {:?}->{:?}; counter={}", + from_bcb, + target_bcb, + self.format_counter(&counter_kind), + ) + } else { + format!( + "non-coverage counter found without a dependent expression, in {:?}; counter={}", + target_bcb, + self.format_counter(&counter_kind), + ) + }; + + // FIXME(richkadel): Determine if unused expressions can and should be prevented, and + // if so, it would be a `bug!` if encountered in the future. At the present time, + // however we do sometimes encounter unused expressions (in only a subset of test cases) + // even when `SIMPLIFY_EXPRESSIONS` is disabled. It's not yet clear what causes this, or + // if it should be allowed in the long term, but as long as the coverage capability + // still works, generate warning messages only, for now. + const ALLOW_UNUSED_EXPRESSIONS: bool = true; + if self.debug_expressions_cache.is_some() || ALLOW_UNUSED_EXPRESSIONS { + // Note, the debugging const `SIMPLIFY_EXPRESSIONS`, which initializes the + // `debug_expressions_cache` can cause some counters to become unused, and + // is not a bug. + // + // For example, converting `x + (y - x)` to just `y` removes a dependency + // on `y - x`. If that expression is not a dependency elsewhere, and if it is + // not associated with a `CoverageSpan`, it is now considered `unused`. + debug!("WARNING: {}", unused_counter_message); + } else { + bug!("{}", unused_counter_message); + } + } + } + for intermediate_expression in collect_intermediate_expressions { self.inject_intermediate_expression(intermediate_expression); } diff --git a/compiler/rustc_mir/src/util/generic_graphviz.rs b/compiler/rustc_mir/src/util/generic_graphviz.rs index 83ee07faa8cb0..7576c32af1962 100644 --- a/compiler/rustc_mir/src/util/generic_graphviz.rs +++ b/compiler/rustc_mir/src/util/generic_graphviz.rs @@ -166,7 +166,7 @@ EdgeLabelsFn: Fn(::Node) -> Ve { let lines = label.split("\n").map(|s| dot::escape_html(s)).collect::>(); let escaped_label = lines.join(r#"
"#); - write!(w, " label=<{}>;", escaped_label) + writeln!(w, r#" label=<

{}



>;"#, escaped_label) } fn node(&self, node: G::Node) -> String { diff --git a/src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot b/src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot index 907b832d0346f..3554dad09fb75 100644 --- a/src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot +++ b/src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot @@ -2,8 +2,8 @@ digraph Cov_0_3 { graph [fontname="Courier, monospace"]; node [fontname="Courier, monospace"]; edge [fontname="Courier, monospace"]; - bcb2__Cov_0_3 [shape="none", label=<
bcb2
Non-coverage counters:
Expression(#1 - #2)
bb4: Goto
>]; - bcb1__Cov_0_3 [shape="none", label=<
bcb1
Counter(#2) at 12:13-15:2
12:13-12:18: @5[0]: _0 = const ()
15:2-15:2: @5.Return: return
bb3: FalseEdge
bb5: Return
>]; + bcb2__Cov_0_3 [shape="none", label=<
bcb2
Expression(#1 - #2) at 14:6-14:6
14:6-14:6: @4.Goto: goto -> bb0
bb4: Goto
>]; + bcb1__Cov_0_3 [shape="none", label=<
bcb1
Counter(#2) at 12:13-12:18
12:13-12:18: @5[0]: _0 = const ()
Expression(#2 + 0) at 15:2-15:2
15:2-15:2: @5.Return: return
bb3: FalseEdge
bb5: Return
>]; bcb0__Cov_0_3 [shape="none", label=<
bcb0
Counter(#1) at 11:12-11:17
11:12-11:17: @1.Call: _2 = bar() -> [return: bb2, unwind: bb6]
11:12-11:17: @2[0]: FakeRead(ForMatchedPlace, _2)
bb0: FalseUnwind
bb1: Call
bb2: SwitchInt
>]; bcb2__Cov_0_3 -> bcb0__Cov_0_3 [label=<>]; bcb0__Cov_0_3 -> bcb2__Cov_0_3 [label=]; diff --git a/src/test/mir-opt/instrument_coverage.main.InstrumentCoverage.diff b/src/test/mir-opt/instrument_coverage.main.InstrumentCoverage.diff index 918f5b246d4eb..c67d0e2ffe656 100644 --- a/src/test/mir-opt/instrument_coverage.main.InstrumentCoverage.diff +++ b/src/test/mir-opt/instrument_coverage.main.InstrumentCoverage.diff @@ -32,14 +32,15 @@ bb4: { _1 = const (); // scope 0 at /the/src/instrument_coverage.rs:12:9: 14:10 StorageDead(_2); // scope 0 at /the/src/instrument_coverage.rs:15:5: 15:6 -+ Coverage::Expression(4294967295) = 1 - 2 for /the/src/instrument_coverage.rs:37:1 - 37:1; // scope 0 at /the/src/instrument_coverage.rs:11:5: 15:6 ++ Coverage::Expression(4294967295) = 1 - 2 for /the/src/instrument_coverage.rs:15:6 - 15:7; // scope 0 at /the/src/instrument_coverage.rs:11:5: 15:6 goto -> bb0; // scope 0 at /the/src/instrument_coverage.rs:11:5: 15:6 } bb5: { _0 = const (); // scope 0 at /the/src/instrument_coverage.rs:13:13: 13:18 StorageDead(_2); // scope 0 at /the/src/instrument_coverage.rs:15:5: 15:6 -+ Coverage::Counter(2) for /the/src/instrument_coverage.rs:13:13 - 16:2; // scope 0 at /the/src/instrument_coverage.rs:16:2: 16:2 ++ Coverage::Counter(2) for /the/src/instrument_coverage.rs:13:13 - 13:18; // scope 0 at /the/src/instrument_coverage.rs:16:2: 16:2 ++ Coverage::Expression(4294967294) = 2 + 0 for /the/src/instrument_coverage.rs:16:1 - 16:2; // scope 0 at /the/src/instrument_coverage.rs:16:2: 16:2 return; // scope 0 at /the/src/instrument_coverage.rs:16:2: 16:2 } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/Makefile b/src/test/run-make-fulldeps/coverage-reports-base/Makefile index 5446252a33c82..bfecc5fd7f557 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/Makefile +++ b/src/test/run-make-fulldeps/coverage-reports-base/Makefile @@ -21,6 +21,7 @@ clear_expected_if_blessed: ifdef RUSTC_BLESS_TEST rm -f expected_export_coverage.*.json rm -f expected_show_coverage.*.txt + rm -f expected_show_coverage_counters.*.txt endif -include clear_expected_if_blessed @@ -59,10 +60,14 @@ endif --show-line-counts-or-regions \ --instr-profile="$(TMPDIR)"/$@.profdata \ $(call BIN,"$(TMPDIR)"/$@) \ - > "$(TMPDIR)"/actual_show_coverage.$@.txt + > "$(TMPDIR)"/actual_show_coverage.$@.txt \ + 2> "$(TMPDIR)"/actual_show_coverage_counters.$@.txt ifdef RUSTC_BLESS_TEST - cp "$(TMPDIR)"/actual_show_coverage.$@.txt expected_show_coverage.$@.txt + cp "$(TMPDIR)"/actual_show_coverage.$@.txt \ + expected_show_coverage.$@.txt + cp "$(TMPDIR)"/actual_show_coverage_counters.$@.txt \ + expected_show_coverage_counters.$@.txt else # Compare the show coverage output (`--bless` refreshes `typical` files) # Note `llvm-cov show` output for some programs can vary, but can be ignored @@ -76,6 +81,14 @@ else false \ ) + $(DIFF) expected_show_coverage_counters.$@.txt "$(TMPDIR)"/actual_show_coverage_counters.$@.txt || \ + ( grep -q '^\/\/ ignore-llvm-cov-show-diffs' $(SOURCEDIR)/$@.rs && \ + >&2 echo 'diff failed, but suppressed with `// ignore-llvm-cov-show-diffs` in $(SOURCEDIR)/$@.rs' \ + ) || \ + ( >&2 echo 'diff failed, and not suppressed without `// ignore-llvm-cov-show-diffs` in $(SOURCEDIR)/$@.rs'; \ + false \ + ) + endif # Generate a coverage report in JSON, using `llvm-cov export`, and fail if diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.closure.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.closure.json index e8d871f00a36c..bff55300b3ca3 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.closure.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.closure.json @@ -17,8 +17,8 @@ }, "lines": { "count": 91, - "covered": 75, - "percent": 82.41758241758241 + "covered": 77, + "percent": 84.61538461538461 }, "regions": { "count": 25, @@ -42,8 +42,8 @@ }, "lines": { "count": 91, - "covered": 75, - "percent": 82.41758241758241 + "covered": 77, + "percent": 84.61538461538461 }, "regions": { "count": 25, diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.lazy_boolean.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.lazy_boolean.json index f5fae0cb82bd5..0d81693814606 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.lazy_boolean.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.lazy_boolean.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 21, - "covered": 19, - "percent": 90.47619047619048 + "count": 40, + "covered": 29, + "percent": 72.5 }, "regions": { - "count": 33, - "covered": 21, - "notcovered": 12, - "percent": 63.63636363636363 + "count": 37, + "covered": 26, + "notcovered": 11, + "percent": 70.27027027027027 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 21, - "covered": 19, - "percent": 90.47619047619048 + "count": 40, + "covered": 29, + "percent": 72.5 }, "regions": { - "count": 33, - "covered": 21, - "notcovered": 12, - "percent": 63.63636363636363 + "count": 37, + "covered": 26, + "notcovered": 11, + "percent": 70.27027027027027 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_loop.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_loop.json index bcc0e1331e331..ada6bb062dd1e 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_loop.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_loop.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 18, - "covered": 18, + "count": 19, + "covered": 19, "percent": 100 }, "regions": { - "count": 10, - "covered": 9, + "count": 9, + "covered": 8, "notcovered": 1, - "percent": 90 + "percent": 88.88888888888889 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 18, - "covered": 18, + "count": 19, + "covered": 19, "percent": 100 }, "regions": { - "count": 10, - "covered": 9, + "count": 9, + "covered": 8, "notcovered": 1, - "percent": 90 + "percent": 88.88888888888889 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_match.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_match.json index ed1705e587062..2ae9ad9c4ebd4 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_match.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_match.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 23, - "covered": 23, - "percent": 100 + "count": 28, + "covered": 24, + "percent": 85.71428571428571 }, "regions": { - "count": 16, + "count": 15, "covered": 14, - "notcovered": 2, - "percent": 87.5 + "notcovered": 1, + "percent": 93.33333333333333 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 23, - "covered": 23, - "percent": 100 + "count": 28, + "covered": 24, + "percent": 85.71428571428571 }, "regions": { - "count": 16, + "count": 15, "covered": 14, - "notcovered": 2, - "percent": 87.5 + "notcovered": 1, + "percent": 93.33333333333333 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.try_error_result.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.try_error_result.json index c647d6e8de55d..bcefa927e8fae 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.try_error_result.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.try_error_result.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 17, - "covered": 16, - "percent": 94.11764705882352 + "count": 20, + "covered": 18, + "percent": 90 }, "regions": { - "count": 21, + "count": 18, "covered": 15, - "notcovered": 6, - "percent": 71.42857142857143 + "notcovered": 3, + "percent": 83.33333333333334 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 17, - "covered": 16, - "percent": 94.11764705882352 + "count": 20, + "covered": 18, + "percent": 90 }, "regions": { - "count": 21, + "count": 18, "covered": 15, - "notcovered": 6, - "percent": 71.42857142857143 + "notcovered": 3, + "percent": 83.33333333333334 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.various_conditions.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.various_conditions.json index c7d80a446fe2c..d5f954b4f7e07 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.various_conditions.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.various_conditions.json @@ -17,14 +17,14 @@ }, "lines": { "count": 49, - "covered": 23, - "percent": 46.93877551020408 + "covered": 24, + "percent": 48.97959183673469 }, "regions": { - "count": 104, - "covered": 23, - "notcovered": 81, - "percent": 22.115384615384613 + "count": 70, + "covered": 19, + "notcovered": 51, + "percent": 27.142857142857142 } } } @@ -42,14 +42,14 @@ }, "lines": { "count": 49, - "covered": 23, - "percent": 46.93877551020408 + "covered": 24, + "percent": 48.97959183673469 }, "regions": { - "count": 104, - "covered": 23, - "notcovered": 81, - "percent": 22.115384615384613 + "count": 70, + "covered": 19, + "notcovered": 51, + "percent": 27.142857142857142 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while_early_return.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while_early_return.json index ea6fd2ac12f35..a116a91a60aae 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while_early_return.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while_early_return.json @@ -21,10 +21,10 @@ "percent": 88.23529411764706 }, "regions": { - "count": 11, - "covered": 9, + "count": 10, + "covered": 8, "notcovered": 2, - "percent": 81.81818181818183 + "percent": 80 } } } @@ -46,10 +46,10 @@ "percent": 88.23529411764706 }, "regions": { - "count": 11, - "covered": 9, + "count": 10, + "covered": 8, "notcovered": 2, - "percent": 81.81818181818183 + "percent": 80 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.closure.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.closure.txt index 17054490e9b3c..aef26a62e25fb 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.closure.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.closure.txt @@ -62,7 +62,7 @@ 62| 1| let mut countdown = 0; 63| 1| if is_false { 64| 0| countdown = 10; - 65| 0| } + 65| 1| } 66| 1| "alt string 3".to_owned() 67| 1| } 68| 1| ) @@ -77,7 +77,7 @@ 77| 1| let mut countdown = 0; 78| 1| if is_false { 79| 0| countdown = 10; - 80| 0| } + 80| 1| } 81| 1| "alt string 4".to_owned() 82| 1| }; 83| 1| println!( diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.if.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.if.txt index bc2f9b108b2f3..85e6440ab3729 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.if.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.if.txt @@ -25,5 +25,6 @@ 25| 1| 10 26| 1| ; 27| 1| } + ^0 28| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.inner_items.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.inner_items.txt index b13ca83d018f9..4a51f842a4bb2 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.inner_items.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.inner_items.txt @@ -10,6 +10,7 @@ 10| 1| if is_true { 11| 1| countdown = 10; 12| 1| } + ^0 13| | 14| | mod in_mod { 15| | const IN_MOD_CONST: u32 = 1000; @@ -48,6 +49,7 @@ 48| 1| if is_true { 49| 1| in_func(countdown); 50| 1| } + ^0 51| | 52| 1| let mut val = InStruct { 53| 1| in_struct_field: 101, diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.lazy_boolean.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.lazy_boolean.txt index ded4369751587..eff74aa3b82f6 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.lazy_boolean.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.lazy_boolean.txt @@ -12,6 +12,7 @@ 12| 1| b = 10; 13| 1| c = 100; 14| 1| } + ^0 15| | let 16| 1| somebool 17| | = @@ -26,19 +27,38 @@ 26| | || 27| 1| b < c 28| | ; - 29| | let - 30| 1| somebool - 31| | = - 32| 1| a < b - 33| | && - 34| 1| b < c - 35| | ; - 36| | let - 37| 1| somebool - 38| | = - 39| 1| b < a - 40| | && - 41| 0| b < c - 42| | ; - 43| 1|} + 29| 1| let somebool = a < b && b < c; + 30| 1| let somebool = b < a && b < c; + ^0 + 31| | + 32| | if + 33| 1| ! + 34| 1| is_true + 35| 0| { + 36| 0| a = 2 + 37| 0| ; + 38| 1| } + 39| | + 40| | if + 41| 1| is_true + 42| 1| { + 43| 1| b = 30 + 44| 1| ; + 45| 1| } + 46| | else + 47| 0| { + 48| 0| c = 400 + 49| 0| ; + 50| 0| } + 51| | + 52| 1| if !is_true { + 53| 0| a = 2; + 54| 1| } + 55| | + 56| 1| if is_true { + 57| 1| b = 30; + 58| 1| } else { + 59| 0| c = 400; + 60| 0| } + 61| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_loop.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_loop.txt index f1acb7c545940..064930dd75c93 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_loop.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_loop.txt @@ -16,6 +16,7 @@ 16| 1| 10 17| 1| ; 18| 1| } + ^0 19| | 20| | loop 21| | { @@ -31,6 +32,6 @@ 31| 10| -= 32| 10| 1 33| | ; - 34| | } + 34| 1| } 35| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_match.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_match.txt index d40acc40ace3e..55b23dd882035 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_match.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_match.txt @@ -10,15 +10,16 @@ 10| 1| if is_true { 11| 1| countdown = 0; 12| 1| } + ^0 13| | 14| | for 15| 2| _ 16| | in 17| 3| 0..2 - 18| | { - 19| | let z - 20| | ; - 21| | match + 18| 0| { + 19| 0| let z + 20| 0| ; + 21| 0| match 22| 2| countdown 23| | { 24| 1| x @@ -26,6 +27,7 @@ 26| 2| x 27| 2| < 28| 2| 1 + ^1 29| | => 30| 1| { 31| 1| z = countdown @@ -39,6 +41,6 @@ 39| | => 40| 1| {} 41| | } - 42| | } + 42| 3| } 43| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.try_error_result.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.try_error_result.txt index 0714b8d44892d..489a5324a4446 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.try_error_result.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.try_error_result.txt @@ -17,7 +17,7 @@ 17| 6| _ 18| | in 19| 6| 0..10 - 20| | { + 20| 0| { 21| 6| countdown 22| 6| -= 1 23| 6| ; @@ -30,8 +30,8 @@ 30| | { 31| 5| call(/*return_error=*/ false)?; ^0 - 32| | } - 33| | } + 32| 5| } + 33| 5| } 34| 0| Ok(()) - 35| 1|} + 35| 2|} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.various_conditions.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.various_conditions.txt index 173ff4aa4c481..9f2deae8a9e55 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.various_conditions.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.various_conditions.txt @@ -27,7 +27,8 @@ 27| | 28| 1| if countdown > 7 { 29| 1| countdown -= 4; - 30| 0| } else if countdown > 2 { + 30| 1| } else if countdown > 2 { + ^0 31| 0| if countdown < 1 || countdown > 5 || countdown != 9 { 32| 0| countdown = 0; 33| 0| } @@ -65,5 +66,5 @@ 64| | } else { 65| 0| return; 66| | }; - 67| 1|} + 67| 2|} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while_early_return.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while_early_return.txt index 26041136d2f4c..e5c5b05a6fc80 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while_early_return.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while_early_return.txt @@ -33,7 +33,7 @@ 33| | ; 34| | } 35| 0| Ok(()) - 36| 1|} + 36| 2|} 37| | 38| |// ISSUE(77553): Originally, this test had `Err(1)` on line 22 (instead of `Ok(())`) and 39| |// `std::process::exit(2)` on line 26 (instead of `Err(1)`); and this worked as expected on Linux diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.closure.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.closure.txt new file mode 100644 index 0000000000000..ef43bbd78a6b5 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.closure.txt @@ -0,0 +1,95 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/closure.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/closure +Counter in file 0 20:21 -> 20:38, #1 +Counter in file 0 21:20 -> 21:28, (#1 + 0) +Counter in file 0 21:29 -> 23:18, #2 +Counter in file 0 23:18 -> 23:19, (#1 - #2) +Counter in file 0 24:17 -> 25:14, (#2 + (#1 - #2)) +Counter in file 0 3:11 -> 18:13, #1 +Counter in file 0 25:14 -> 33:9, (#1 + 0) +Counter in file 0 40:6 -> 60:13, (#1 + 0) +Counter in file 0 67:14 -> 75:9, (#1 + 0) +Counter in file 0 82:6 -> 93:2, (#1 + 0) +Counter in file 0 77:13 -> 77:30, #1 +Counter in file 0 78:12 -> 78:20, (#1 + 0) +Counter in file 0 78:21 -> 80:10, #2 +Counter in file 0 80:10 -> 80:11, (#1 - #2) +Counter in file 0 81:9 -> 82:6, (#2 + (#1 - #2)) +Counter in file 0 62:21 -> 62:38, #1 +Counter in file 0 63:20 -> 63:28, (#1 + 0) +Counter in file 0 63:29 -> 65:18, #2 +Counter in file 0 65:18 -> 65:19, (#1 - #2) +Counter in file 0 66:17 -> 67:14, (#2 + (#1 - #2)) +Counter in file 0 35:13 -> 35:30, #1 +Counter in file 0 36:12 -> 36:20, (#1 + 0) +Counter in file 0 36:21 -> 38:10, #2 +Counter in file 0 38:10 -> 38:11, (#1 - #2) +Counter in file 0 39:9 -> 40:6, (#2 + (#1 - #2)) +Emitting segments for file: ../coverage/closure.rs +Combined regions: + 3:11 -> 18:13 (count=1) + 20:21 -> 20:38 (count=0) + 21:20 -> 21:28 (count=0) + 21:29 -> 23:18 (count=0) + 23:18 -> 23:19 (count=0) + 24:17 -> 25:14 (count=0) + 25:14 -> 33:9 (count=1) + 35:13 -> 35:30 (count=0) + 36:12 -> 36:20 (count=0) + 36:21 -> 38:10 (count=0) + 38:10 -> 38:11 (count=0) + 39:9 -> 40:6 (count=0) + 40:6 -> 60:13 (count=1) + 62:21 -> 62:38 (count=1) + 63:20 -> 63:28 (count=1) + 63:29 -> 65:18 (count=0) + 65:18 -> 65:19 (count=1) + 66:17 -> 67:14 (count=1) + 67:14 -> 75:9 (count=1) + 77:13 -> 77:30 (count=1) + 78:12 -> 78:20 (count=1) + 78:21 -> 80:10 (count=0) + 80:10 -> 80:11 (count=1) + 81:9 -> 82:6 (count=1) + 82:6 -> 93:2 (count=1) +Segment at 3:11 (count = 1), RegionEntry +Segment at 18:13 (count = 0), Skipped +Segment at 20:21 (count = 0), RegionEntry +Segment at 20:38 (count = 0), Skipped +Segment at 21:20 (count = 0), RegionEntry +Segment at 21:28 (count = 0), Skipped +Segment at 21:29 (count = 0), RegionEntry +Segment at 23:18 (count = 0), RegionEntry +Segment at 23:19 (count = 0), Skipped +Segment at 24:17 (count = 0), RegionEntry +Segment at 25:14 (count = 1), RegionEntry +Segment at 33:9 (count = 0), Skipped +Segment at 35:13 (count = 0), RegionEntry +Segment at 35:30 (count = 0), Skipped +Segment at 36:12 (count = 0), RegionEntry +Segment at 36:20 (count = 0), Skipped +Segment at 36:21 (count = 0), RegionEntry +Segment at 38:10 (count = 0), RegionEntry +Segment at 38:11 (count = 0), Skipped +Segment at 39:9 (count = 0), RegionEntry +Segment at 40:6 (count = 1), RegionEntry +Segment at 60:13 (count = 0), Skipped +Segment at 62:21 (count = 1), RegionEntry +Segment at 62:38 (count = 0), Skipped +Segment at 63:20 (count = 1), RegionEntry +Segment at 63:28 (count = 0), Skipped +Segment at 63:29 (count = 0), RegionEntry +Segment at 65:18 (count = 1), RegionEntry +Segment at 65:19 (count = 0), Skipped +Segment at 66:17 (count = 1), RegionEntry +Segment at 67:14 (count = 1), RegionEntry +Segment at 75:9 (count = 0), Skipped +Segment at 77:13 (count = 1), RegionEntry +Segment at 77:30 (count = 0), Skipped +Segment at 78:12 (count = 1), RegionEntry +Segment at 78:20 (count = 0), Skipped +Segment at 78:21 (count = 0), RegionEntry +Segment at 80:10 (count = 1), RegionEntry +Segment at 80:11 (count = 0), Skipped +Segment at 81:9 (count = 1), RegionEntry +Segment at 82:6 (count = 1), RegionEntry +Segment at 93:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.drop_trait.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.drop_trait.txt new file mode 100644 index 0000000000000..9716284f52521 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.drop_trait.txt @@ -0,0 +1,23 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/drop_trait.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/drop_trait +Counter in file 0 9:24 -> 11:6, #1 +Counter in file 0 15:9 -> 17:42, #1 +Counter in file 0 19:8 -> 19:12, (#1 + 0) +Counter in file 0 20:9 -> 21:22, #2 +Counter in file 0 27:1 -> 27:2, (#2 + 0) +Emitting segments for file: ../coverage/drop_trait.rs +Combined regions: + 9:24 -> 11:6 (count=2) + 15:9 -> 17:42 (count=1) + 19:8 -> 19:12 (count=1) + 20:9 -> 21:22 (count=1) + 27:1 -> 27:2 (count=1) +Segment at 9:24 (count = 2), RegionEntry +Segment at 11:6 (count = 0), Skipped +Segment at 15:9 (count = 1), RegionEntry +Segment at 17:42 (count = 0), Skipped +Segment at 19:8 (count = 1), RegionEntry +Segment at 19:12 (count = 0), Skipped +Segment at 20:9 (count = 1), RegionEntry +Segment at 21:22 (count = 0), Skipped +Segment at 27:1 (count = 1), RegionEntry +Segment at 27:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.generics.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.generics.txt new file mode 100644 index 0000000000000..5fbfe69b891c0 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.generics.txt @@ -0,0 +1,49 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/generics.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/generics +Counter in file 0 17:24 -> 19:6, #1 +Counter in file 0 17:24 -> 19:6, #1 +Counter in file 0 23:9 -> 28:28, #1 +Counter in file 0 30:8 -> 30:12, (#1 + 0) +Counter in file 0 31:9 -> 32:22, #2 +Counter in file 0 38:1 -> 38:2, (#2 + 0) +Counter in file 0 10:49 -> 12:6, #1 +Counter in file 0 10:49 -> 12:6, #1 +Emitting segments for file: ../coverage/generics.rs +Combined regions: + 10:49 -> 12:6 (count=3) + 17:24 -> 19:6 (count=2) + 23:9 -> 28:28 (count=1) + 30:8 -> 30:12 (count=1) + 31:9 -> 32:22 (count=1) + 38:1 -> 38:2 (count=1) +Segment at 10:49 (count = 3), RegionEntry +Segment at 12:6 (count = 0), Skipped +Segment at 17:24 (count = 2), RegionEntry +Segment at 19:6 (count = 0), Skipped +Segment at 23:9 (count = 1), RegionEntry +Segment at 28:28 (count = 0), Skipped +Segment at 30:8 (count = 1), RegionEntry +Segment at 30:12 (count = 0), Skipped +Segment at 31:9 (count = 1), RegionEntry +Segment at 32:22 (count = 0), Skipped +Segment at 38:1 (count = 1), RegionEntry +Segment at 38:2 (count = 0), Skipped +Emitting segments for function: _RNvMCs4fqI2P2rA04_8genericsINtB2_8FireworkdE12set_strengthB2_ +Combined regions: + 10:49 -> 12:6 (count=2) +Segment at 10:49 (count = 2), RegionEntry +Segment at 12:6 (count = 0), Skipped +Emitting segments for function: _RNvMCs4fqI2P2rA04_8genericsINtB2_8FireworklE12set_strengthB2_ +Combined regions: + 10:49 -> 12:6 (count=1) +Segment at 10:49 (count = 1), RegionEntry +Segment at 12:6 (count = 0), Skipped +Emitting segments for function: _RNvXs_Cs4fqI2P2rA04_8genericsINtB4_8FireworklENtNtNtCs7f2nZg1zwMz_4core3ops4drop4Drop4dropB4_ +Combined regions: + 17:24 -> 19:6 (count=1) +Segment at 17:24 (count = 1), RegionEntry +Segment at 19:6 (count = 0), Skipped +Emitting segments for function: _RNvXs_Cs4fqI2P2rA04_8genericsINtB4_8FireworkdENtNtNtCs7f2nZg1zwMz_4core3ops4drop4Drop4dropB4_ +Combined regions: + 17:24 -> 19:6 (count=1) +Segment at 17:24 (count = 1), RegionEntry +Segment at 19:6 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.if.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.if.txt new file mode 100644 index 0000000000000..48c9278a23917 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.if.txt @@ -0,0 +1,22 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/if.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/if +Counter in file 0 8:5 -> 18:10, #1 +Counter in file 0 21:9 -> 21:16, (#1 + 0) +Counter in file 0 22:5 -> 27:6, #2 +Counter in file 0 27:6 -> 27:7, (#1 - #2) +Counter in file 0 28:1 -> 28:2, (#2 + (#1 - #2)) +Emitting segments for file: ../coverage/if.rs +Combined regions: + 8:5 -> 18:10 (count=1) + 21:9 -> 21:16 (count=1) + 22:5 -> 27:6 (count=1) + 27:6 -> 27:7 (count=0) + 28:1 -> 28:2 (count=1) +Segment at 8:5 (count = 1), RegionEntry +Segment at 18:10 (count = 0), Skipped +Segment at 21:9 (count = 1), RegionEntry +Segment at 21:16 (count = 0), Skipped +Segment at 22:5 (count = 1), RegionEntry +Segment at 27:6 (count = 0), RegionEntry +Segment at 27:7 (count = 0), Skipped +Segment at 28:1 (count = 1), RegionEntry +Segment at 28:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.if_else.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.if_else.txt new file mode 100644 index 0000000000000..1adf92300bfbd --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.if_else.txt @@ -0,0 +1,31 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/if_else.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/if_else +Counter in file 0 7:9 -> 11:16, #1 +Counter in file 0 12:5 -> 17:6, #2 +Counter in file 0 20:9 -> 22:16, (#1 - #2) +Counter in file 0 26:9 -> 26:16, (#2 + (#1 - #2)) +Counter in file 0 27:5 -> 32:6, #3 +Counter in file 0 34:5 -> 39:6, ((#2 + (#1 - #2)) - #3) +Counter in file 0 40:1 -> 40:2, (#3 + ((#2 + (#1 - #2)) - #3)) +Emitting segments for file: ../coverage/if_else.rs +Combined regions: + 7:9 -> 11:16 (count=1) + 12:5 -> 17:6 (count=1) + 20:9 -> 22:16 (count=0) + 26:9 -> 26:16 (count=1) + 27:5 -> 32:6 (count=1) + 34:5 -> 39:6 (count=0) + 40:1 -> 40:2 (count=1) +Segment at 7:9 (count = 1), RegionEntry +Segment at 11:16 (count = 0), Skipped +Segment at 12:5 (count = 1), RegionEntry +Segment at 17:6 (count = 0), Skipped +Segment at 20:9 (count = 0), RegionEntry +Segment at 22:16 (count = 0), Skipped +Segment at 26:9 (count = 1), RegionEntry +Segment at 26:16 (count = 0), Skipped +Segment at 27:5 (count = 1), RegionEntry +Segment at 32:6 (count = 0), Skipped +Segment at 34:5 (count = 0), RegionEntry +Segment at 39:6 (count = 0), Skipped +Segment at 40:1 (count = 1), RegionEntry +Segment at 40:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.inner_items.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.inner_items.txt new file mode 100644 index 0000000000000..0678be30dc341 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.inner_items.txt @@ -0,0 +1,61 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/inner_items.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/inner_items +Counter in file 0 19:13 -> 19:18, #1 +Counter in file 0 20:13 -> 20:14, #2 +Counter in file 0 20:17 -> 20:22, (#1 + 0) +Counter in file 0 21:9 -> 22:6, (#2 + 0) +Counter in file 0 7:9 -> 9:26, #1 +Counter in file 0 10:8 -> 10:15, (#1 + 0) +Counter in file 0 10:16 -> 12:6, #2 +Counter in file 0 12:6 -> 12:7, (#1 - #2) +Counter in file 0 48:8 -> 48:15, (#2 + (#1 - #2)) +Counter in file 0 48:16 -> 50:6, #3 +Counter in file 0 50:6 -> 50:7, ((#2 + (#1 - #2)) - #3) +Counter in file 0 52:9 -> 57:2, (#3 + ((#2 + (#1 - #2)) - #3)) +Counter in file 0 33:42 -> 36:10, #1 +Counter in file 0 41:37 -> 41:41, #1 +Counter in file 0 42:13 -> 43:10, #2 +Emitting segments for file: ../coverage/inner_items.rs +Combined regions: + 7:9 -> 9:26 (count=1) + 10:8 -> 10:15 (count=1) + 10:16 -> 12:6 (count=1) + 12:6 -> 12:7 (count=0) + 19:13 -> 19:18 (count=3) + 20:13 -> 20:14 (count=3) + 20:17 -> 20:22 (count=3) + 21:9 -> 22:6 (count=3) + 33:42 -> 36:10 (count=1) + 41:37 -> 41:41 (count=1) + 42:13 -> 43:10 (count=1) + 48:8 -> 48:15 (count=1) + 48:16 -> 50:6 (count=1) + 50:6 -> 50:7 (count=0) + 52:9 -> 57:2 (count=1) +Segment at 7:9 (count = 1), RegionEntry +Segment at 9:26 (count = 0), Skipped +Segment at 10:8 (count = 1), RegionEntry +Segment at 10:15 (count = 0), Skipped +Segment at 10:16 (count = 1), RegionEntry +Segment at 12:6 (count = 0), RegionEntry +Segment at 12:7 (count = 0), Skipped +Segment at 19:13 (count = 3), RegionEntry +Segment at 19:18 (count = 0), Skipped +Segment at 20:13 (count = 3), RegionEntry +Segment at 20:14 (count = 0), Skipped +Segment at 20:17 (count = 3), RegionEntry +Segment at 20:22 (count = 0), Skipped +Segment at 21:9 (count = 3), RegionEntry +Segment at 22:6 (count = 0), Skipped +Segment at 33:42 (count = 1), RegionEntry +Segment at 36:10 (count = 0), Skipped +Segment at 41:37 (count = 1), RegionEntry +Segment at 41:41 (count = 0), Skipped +Segment at 42:13 (count = 1), RegionEntry +Segment at 43:10 (count = 0), Skipped +Segment at 48:8 (count = 1), RegionEntry +Segment at 48:15 (count = 0), Skipped +Segment at 48:16 (count = 1), RegionEntry +Segment at 50:6 (count = 0), RegionEntry +Segment at 50:7 (count = 0), Skipped +Segment at 52:9 (count = 1), RegionEntry +Segment at 57:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.lazy_boolean.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.lazy_boolean.txt new file mode 100644 index 0000000000000..57b8b9b3c49f6 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.lazy_boolean.txt @@ -0,0 +1,142 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/lazy_boolean.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/lazy_boolean +Counter in file 0 7:9 -> 9:42, #1 +Counter in file 0 10:8 -> 10:15, (#1 + 0) +Counter in file 0 10:16 -> 14:6, #2 +Counter in file 0 14:6 -> 14:7, (#1 - #2) +Counter in file 0 16:9 -> 16:17, ((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) +Counter in file 0 18:13 -> 18:18, (#2 + (#1 - #2)) +Counter in file 0 20:13 -> 20:18, ((#2 + (#1 - #2)) - #3) +Counter in file 0 20:18 -> 20:19, (((#2 + (#1 - #2)) - #3) - #4) +Counter in file 0 20:18 -> 20:19, (#3 + #4) +Counter in file 0 20:18 -> 20:18, #3 +Counter in file 0 20:18 -> 20:18, #4 +Counter in file 0 23:9 -> 23:17, ((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) +Counter in file 0 25:13 -> 25:18, (((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) + 0) +Counter in file 0 27:13 -> 27:18, (((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) +Counter in file 0 27:18 -> 27:19, (#5 + #6) +Counter in file 0 27:18 -> 27:19, ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6) +Counter in file 0 27:18 -> 27:18, #5 +Counter in file 0 27:18 -> 27:18, #6 +Counter in file 0 29:9 -> 29:17, ((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) +Counter in file 0 29:20 -> 29:25, (((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) + 0) +Counter in file 0 29:29 -> 29:34, #7 +Counter in file 0 29:34 -> 29:35, ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8) +Counter in file 0 29:34 -> 29:35, (#7 - #8) +Counter in file 0 29:34 -> 29:34, #8 +Counter in file 0 30:9 -> 30:17, ((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) +Counter in file 0 30:20 -> 30:25, (((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) + 0) +Counter in file 0 30:29 -> 30:34, #9 +Counter in file 0 30:34 -> 30:35, ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10) +Counter in file 0 30:34 -> 30:35, (#9 - #10) +Counter in file 0 30:34 -> 30:34, #10 +Counter in file 0 33:9 -> 34:16, (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) + 0) +Counter in file 0 35:5 -> 38:6, #11 +Counter in file 0 38:6 -> 38:7, (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11) +Counter in file 0 41:9 -> 41:16, (#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) +Counter in file 0 42:5 -> 45:6, #12 +Counter in file 0 47:5 -> 50:6, ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12) +Counter in file 0 52:8 -> 52:16, (#12 + ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12)) +Counter in file 0 52:17 -> 54:6, #13 +Counter in file 0 54:6 -> 54:7, ((#12 + ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12)) - #13) +Counter in file 0 56:8 -> 56:15, (#13 + ((#12 + ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12)) - #13)) +Counter in file 0 56:16 -> 58:6, #14 +Counter in file 0 58:12 -> 60:6, ((#13 + ((#12 + ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12)) - #13)) - #14) +Counter in file 0 61:1 -> 61:2, (#14 + ((#13 + ((#12 + ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12)) - #13)) - #14)) +Emitting segments for file: ../coverage/lazy_boolean.rs +Combined regions: + 7:9 -> 9:42 (count=1) + 10:8 -> 10:15 (count=1) + 10:16 -> 14:6 (count=1) + 14:6 -> 14:7 (count=0) + 16:9 -> 16:17 (count=1) + 18:13 -> 18:18 (count=1) + 20:13 -> 20:18 (count=0) + 20:18 -> 20:19 (count=1) + 20:18 -> 20:18 (count=1) + 23:9 -> 23:17 (count=1) + 25:13 -> 25:18 (count=1) + 27:13 -> 27:18 (count=1) + 27:18 -> 27:19 (count=1) + 27:18 -> 27:18 (count=1) + 29:9 -> 29:17 (count=1) + 29:20 -> 29:25 (count=1) + 29:29 -> 29:34 (count=1) + 29:34 -> 29:35 (count=1) + 29:34 -> 29:34 (count=0) + 30:9 -> 30:17 (count=1) + 30:20 -> 30:25 (count=1) + 30:29 -> 30:34 (count=0) + 30:34 -> 30:35 (count=1) + 30:34 -> 30:34 (count=0) + 33:9 -> 34:16 (count=1) + 35:5 -> 38:6 (count=0) + 38:6 -> 38:7 (count=1) + 41:9 -> 41:16 (count=1) + 42:5 -> 45:6 (count=1) + 47:5 -> 50:6 (count=0) + 52:8 -> 52:16 (count=1) + 52:17 -> 54:6 (count=0) + 54:6 -> 54:7 (count=1) + 56:8 -> 56:15 (count=1) + 56:16 -> 58:6 (count=1) + 58:12 -> 60:6 (count=0) + 61:1 -> 61:2 (count=1) +Segment at 7:9 (count = 1), RegionEntry +Segment at 9:42 (count = 0), Skipped +Segment at 10:8 (count = 1), RegionEntry +Segment at 10:15 (count = 0), Skipped +Segment at 10:16 (count = 1), RegionEntry +Segment at 14:6 (count = 0), RegionEntry +Segment at 14:7 (count = 0), Skipped +Segment at 16:9 (count = 1), RegionEntry +Segment at 16:17 (count = 0), Skipped +Segment at 18:13 (count = 1), RegionEntry +Segment at 18:18 (count = 0), Skipped +Segment at 20:13 (count = 0), RegionEntry +Segment at 20:18 (count = 1) +Segment at 20:19 (count = 0), Skipped +Segment at 23:9 (count = 1), RegionEntry +Segment at 23:17 (count = 0), Skipped +Segment at 25:13 (count = 1), RegionEntry +Segment at 25:18 (count = 0), Skipped +Segment at 27:13 (count = 1), RegionEntry +Segment at 27:18 (count = 1) +Segment at 27:19 (count = 0), Skipped +Segment at 29:9 (count = 1), RegionEntry +Segment at 29:17 (count = 0), Skipped +Segment at 29:20 (count = 1), RegionEntry +Segment at 29:25 (count = 0), Skipped +Segment at 29:29 (count = 1), RegionEntry +Segment at 29:34 (count = 1) +Segment at 29:35 (count = 0), Skipped +Segment at 30:9 (count = 1), RegionEntry +Segment at 30:17 (count = 0), Skipped +Segment at 30:20 (count = 1), RegionEntry +Segment at 30:25 (count = 0), Skipped +Segment at 30:29 (count = 0), RegionEntry +Segment at 30:34 (count = 1) +Segment at 30:35 (count = 0), Skipped +Segment at 33:9 (count = 1), RegionEntry +Segment at 34:16 (count = 0), Skipped +Segment at 35:5 (count = 0), RegionEntry +Segment at 38:6 (count = 1), RegionEntry +Segment at 38:7 (count = 0), Skipped +Segment at 41:9 (count = 1), RegionEntry +Segment at 41:16 (count = 0), Skipped +Segment at 42:5 (count = 1), RegionEntry +Segment at 45:6 (count = 0), Skipped +Segment at 47:5 (count = 0), RegionEntry +Segment at 50:6 (count = 0), Skipped +Segment at 52:8 (count = 1), RegionEntry +Segment at 52:16 (count = 0), Skipped +Segment at 52:17 (count = 0), RegionEntry +Segment at 54:6 (count = 1), RegionEntry +Segment at 54:7 (count = 0), Skipped +Segment at 56:8 (count = 1), RegionEntry +Segment at 56:15 (count = 0), Skipped +Segment at 56:16 (count = 1), RegionEntry +Segment at 58:6 (count = 0), Skipped +Segment at 58:12 (count = 0), RegionEntry +Segment at 60:6 (count = 0), Skipped +Segment at 61:1 (count = 1), RegionEntry +Segment at 61:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.loop_break_value.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.loop_break_value.txt new file mode 100644 index 0000000000000..df16c54617c43 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.loop_break_value.txt @@ -0,0 +1,7 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/loop_break_value.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/loop_break_value +Counter in file 0 3:11 -> 13:2, #1 +Emitting segments for file: ../coverage/loop_break_value.rs +Combined regions: + 3:11 -> 13:2 (count=1) +Segment at 3:11 (count = 1), RegionEntry +Segment at 13:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.simple_loop.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.simple_loop.txt new file mode 100644 index 0000000000000..148768f423ba0 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.simple_loop.txt @@ -0,0 +1,38 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/simple_loop.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/simple_loop +Counter in file 0 7:9 -> 9:26, #1 +Counter in file 0 12:9 -> 12:16, (#1 + 0) +Counter in file 0 13:5 -> 18:6, #2 +Counter in file 0 18:6 -> 18:7, (#1 - #2) +Counter in file 0 23:13 -> 25:14, ((#2 + (#1 - #2)) + #3) +Counter in file 0 27:13 -> 27:18, #4 +Counter in file 0 30:9 -> 32:10, #3 +Counter in file 0 34:6 -> 34:7, (#2 + (#1 - #2)) +Counter in file 0 35:1 -> 35:2, (#4 + 0) +Emitting segments for file: ../coverage/simple_loop.rs +Combined regions: + 7:9 -> 9:26 (count=1) + 12:9 -> 12:16 (count=1) + 13:5 -> 18:6 (count=1) + 18:6 -> 18:7 (count=0) + 23:13 -> 25:14 (count=11) + 27:13 -> 27:18 (count=1) + 30:9 -> 32:10 (count=10) + 34:6 -> 34:7 (count=1) + 35:1 -> 35:2 (count=1) +Segment at 7:9 (count = 1), RegionEntry +Segment at 9:26 (count = 0), Skipped +Segment at 12:9 (count = 1), RegionEntry +Segment at 12:16 (count = 0), Skipped +Segment at 13:5 (count = 1), RegionEntry +Segment at 18:6 (count = 0), RegionEntry +Segment at 18:7 (count = 0), Skipped +Segment at 23:13 (count = 11), RegionEntry +Segment at 25:14 (count = 0), Skipped +Segment at 27:13 (count = 1), RegionEntry +Segment at 27:18 (count = 0), Skipped +Segment at 30:9 (count = 10), RegionEntry +Segment at 32:10 (count = 0), Skipped +Segment at 34:6 (count = 1), RegionEntry +Segment at 34:7 (count = 0), Skipped +Segment at 35:1 (count = 1), RegionEntry +Segment at 35:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.simple_match.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.simple_match.txt new file mode 100644 index 0000000000000..a21a26b7089b9 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.simple_match.txt @@ -0,0 +1,60 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/simple_match.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/simple_match +Counter in file 0 7:9 -> 9:26, #1 +Counter in file 0 10:8 -> 10:15, (#1 + 0) +Counter in file 0 10:16 -> 12:6, #2 +Counter in file 0 12:6 -> 12:7, (#1 - #2) +Counter in file 0 15:9 -> 15:10, (((#2 + (#1 - #2)) + (#3 + #4)) - (#6 + #5)) +Counter in file 0 17:9 -> 17:13, ((#2 + (#1 - #2)) + (#3 + #4)) +Counter in file 0 17:13 -> 17:13, #6 +Counter in file 0 22:13 -> 22:22, ((((#2 + (#1 - #2)) + (#3 + #4)) - (#6 + #5)) + 0) +Counter in file 0 24:13 -> 24:14, #3 +Counter in file 0 26:17 -> 28:18, ((((#2 + (#1 - #2)) + (#3 + #4)) - (#6 + #5)) + 0) +Counter in file 0 28:18 -> 28:19, ((((#2 + (#1 - #2)) + (#3 + #4)) - (#6 + #5)) - #3) +Counter in file 0 30:13 -> 37:14, (#3 + 0) +Counter in file 0 40:13 -> 40:15, #4 +Counter in file 0 42:6 -> 42:7, (#2 + (#1 - #2)) +Counter in file 0 42:6 -> 42:7, (#3 + #4) +Counter in file 0 43:1 -> 43:2, #5 +Emitting segments for file: ../coverage/simple_match.rs +Combined regions: + 7:9 -> 9:26 (count=1) + 10:8 -> 10:15 (count=1) + 10:16 -> 12:6 (count=1) + 12:6 -> 12:7 (count=0) + 15:9 -> 15:10 (count=2) + 17:9 -> 17:13 (count=3) + 17:13 -> 17:13 (count=0) + 22:13 -> 22:22 (count=2) + 24:13 -> 24:14 (count=1) + 26:17 -> 28:18 (count=2) + 28:18 -> 28:19 (count=1) + 30:13 -> 37:14 (count=1) + 40:13 -> 40:15 (count=1) + 42:6 -> 42:7 (count=3) + 43:1 -> 43:2 (count=1) +Segment at 7:9 (count = 1), RegionEntry +Segment at 9:26 (count = 0), Skipped +Segment at 10:8 (count = 1), RegionEntry +Segment at 10:15 (count = 0), Skipped +Segment at 10:16 (count = 1), RegionEntry +Segment at 12:6 (count = 0), RegionEntry +Segment at 12:7 (count = 0), Skipped +Segment at 15:9 (count = 2), RegionEntry +Segment at 15:10 (count = 0), Skipped +Segment at 17:9 (count = 3), RegionEntry +Segment at 17:13 (count = 0), Gap +Segment at 22:13 (count = 2), RegionEntry +Segment at 22:22 (count = 0), Skipped +Segment at 24:13 (count = 1), RegionEntry +Segment at 24:14 (count = 0), Skipped +Segment at 26:17 (count = 2), RegionEntry +Segment at 28:18 (count = 1), RegionEntry +Segment at 28:19 (count = 0), Skipped +Segment at 30:13 (count = 1), RegionEntry +Segment at 37:14 (count = 0), Skipped +Segment at 40:13 (count = 1), RegionEntry +Segment at 40:15 (count = 0), Skipped +Segment at 42:6 (count = 3), RegionEntry +Segment at 42:7 (count = 0), Skipped +Segment at 43:1 (count = 1), RegionEntry +Segment at 43:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.try_error_result.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.try_error_result.txt new file mode 100644 index 0000000000000..ea1d99623e80a --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.try_error_result.txt @@ -0,0 +1,69 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/try_error_result.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/try_error_result +Counter in file 0 13:9 -> 14:23, #1 +Counter in file 0 17:9 -> 17:10, ((#1 + (#2 + #3)) - (#5 + #4)) +Counter in file 0 19:9 -> 19:14, (#1 + (#2 + #3)) +Counter in file 0 19:14 -> 19:14, #5 +Counter in file 0 21:9 -> 25:26, #8 +Counter in file 0 27:13 -> 27:41, #9 +Counter in file 0 27:41 -> 27:42, #6 +Counter in file 0 31:13 -> 31:42, (#8 - #9) +Counter in file 0 31:42 -> 31:43, #7 +Counter in file 0 32:10 -> 32:11, #2 +Counter in file 0 32:10 -> 32:11, #3 +Counter in file 0 33:6 -> 33:7, (#2 + #3) +Counter in file 0 34:5 -> 34:11, #4 +Counter in file 0 35:1 -> 35:2, (#6 + #7) +Counter in file 0 35:1 -> 35:2, ((#6 + #7) + #4) +Counter in file 0 5:8 -> 5:20, #1 +Counter in file 0 6:9 -> 6:16, #2 +Counter in file 0 8:9 -> 8:15, (#1 - #2) +Counter in file 0 10:1 -> 10:2, (#2 + (#1 - #2)) +Emitting segments for file: ../coverage/try_error_result.rs +Combined regions: + 5:8 -> 5:20 (count=6) + 6:9 -> 6:16 (count=1) + 8:9 -> 8:15 (count=5) + 10:1 -> 10:2 (count=6) + 13:9 -> 14:23 (count=1) + 17:9 -> 17:10 (count=6) + 19:9 -> 19:14 (count=6) + 19:14 -> 19:14 (count=0) + 21:9 -> 25:26 (count=6) + 27:13 -> 27:41 (count=1) + 27:41 -> 27:42 (count=1) + 31:13 -> 31:42 (count=5) + 31:42 -> 31:43 (count=0) + 32:10 -> 32:11 (count=5) + 33:6 -> 33:7 (count=5) + 34:5 -> 34:11 (count=0) + 35:1 -> 35:2 (count=2) +Segment at 5:8 (count = 6), RegionEntry +Segment at 5:20 (count = 0), Skipped +Segment at 6:9 (count = 1), RegionEntry +Segment at 6:16 (count = 0), Skipped +Segment at 8:9 (count = 5), RegionEntry +Segment at 8:15 (count = 0), Skipped +Segment at 10:1 (count = 6), RegionEntry +Segment at 10:2 (count = 0), Skipped +Segment at 13:9 (count = 1), RegionEntry +Segment at 14:23 (count = 0), Skipped +Segment at 17:9 (count = 6), RegionEntry +Segment at 17:10 (count = 0), Skipped +Segment at 19:9 (count = 6), RegionEntry +Segment at 19:14 (count = 0), Gap +Segment at 21:9 (count = 6), RegionEntry +Segment at 25:26 (count = 0), Skipped +Segment at 27:13 (count = 1), RegionEntry +Segment at 27:41 (count = 1), RegionEntry +Segment at 27:42 (count = 0), Skipped +Segment at 31:13 (count = 5), RegionEntry +Segment at 31:42 (count = 0), RegionEntry +Segment at 31:43 (count = 0), Skipped +Segment at 32:10 (count = 5), RegionEntry +Segment at 32:11 (count = 0), Skipped +Segment at 33:6 (count = 5), RegionEntry +Segment at 33:7 (count = 0), Skipped +Segment at 34:5 (count = 0), RegionEntry +Segment at 34:11 (count = 0), Skipped +Segment at 35:1 (count = 2), RegionEntry +Segment at 35:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.various_conditions.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.various_conditions.txt new file mode 100644 index 0000000000000..ddc0acb78fad2 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.various_conditions.txt @@ -0,0 +1,273 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/various_conditions.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/various_conditions +Counter in file 0 4:9 -> 4:26, #1 +Counter in file 0 5:8 -> 5:12, (#1 + 0) +Counter in file 0 5:13 -> 7:6, #2 +Counter in file 0 10:9 -> 10:10, (#4 + #11) +Counter in file 0 10:16 -> 10:29, (#2 + 0) +Counter in file 0 11:9 -> 12:10, #4 +Counter in file 0 11:23 -> 11:23, #3 +Counter in file 0 13:15 -> 13:28, ((#2 + 0) - #3) +Counter in file 0 14:12 -> 14:25, #5 +Counter in file 0 14:29 -> 14:42, (#5 - #13) +Counter in file 0 14:42 -> 14:43, ((#5 - #13) - #14) +Counter in file 0 14:42 -> 14:43, (#13 + #14) +Counter in file 0 14:42 -> 14:42, #13 +Counter in file 0 14:42 -> 14:42, #14 +Counter in file 0 14:46 -> 14:60, (((#13 + #14) + ((#5 - #13) - #14)) - #17) +Counter in file 0 14:60 -> 14:61, ((((#13 + #14) + ((#5 - #13) - #14)) - #17) - #18) +Counter in file 0 14:60 -> 14:61, (#17 + #18) +Counter in file 0 14:60 -> 14:60, #17 +Counter in file 0 14:60 -> 14:60, #18 +Counter in file 0 14:61 -> 16:10, #21 +Counter in file 0 16:10 -> 16:11, (((#17 + #18) + ((((#13 + #14) + ((#5 - #13) - #14)) - #17) - #18)) - #21) +Counter in file 0 17:9 -> 18:18, #11 +Counter in file 0 20:9 -> 20:15, (((#2 + 0) - #3) - #5) +Counter in file 0 23:9 -> 23:26, ((#4 + #11) + 0) +Counter in file 0 24:8 -> 24:12, ((#4 + #11) + 0) +Counter in file 0 24:13 -> 26:6, #12 +Counter in file 0 28:8 -> 28:21, (#12 + 0) +Counter in file 0 29:9 -> 29:23, #16 +Counter in file 0 29:23 -> 29:23, #15 +Counter in file 0 30:15 -> 30:28, ((#12 + 0) - #15) +Counter in file 0 31:12 -> 31:25, (((#12 + 0) - #15) - #8) +Counter in file 0 31:29 -> 31:42, ((((#12 + 0) - #15) - #8) - #22) +Counter in file 0 31:42 -> 31:43, (#22 + #23) +Counter in file 0 31:42 -> 31:43, (((((#12 + 0) - #15) - #8) - #22) - #23) +Counter in file 0 31:42 -> 31:42, #22 +Counter in file 0 31:42 -> 31:42, #23 +Counter in file 0 31:46 -> 31:60, (((#22 + #23) + (((((#12 + 0) - #15) - #8) - #22) - #23)) - #26) +Counter in file 0 31:60 -> 31:61, (#26 + #27) +Counter in file 0 31:60 -> 31:61, ((((#22 + #23) + (((((#12 + 0) - #15) - #8) - #22) - #23)) - #26) - #27) +Counter in file 0 31:60 -> 31:60, #27 +Counter in file 0 31:60 -> 31:60, #26 +Counter in file 0 31:61 -> 33:10, #30 +Counter in file 0 33:10 -> 33:11, (((#26 + #27) + ((((#22 + #23) + (((((#12 + 0) - #15) - #8) - #22) - #23)) - #26) - #27)) - #30) +Counter in file 0 34:9 -> 34:23, #19 +Counter in file 0 36:9 -> 36:15, #8 +Counter in file 0 39:9 -> 39:26, (#16 + #19) +Counter in file 0 40:8 -> 40:12, ((#16 + #19) + 0) +Counter in file 0 40:13 -> 42:6, #20 +Counter in file 0 44:9 -> 44:10, (#25 + #28) +Counter in file 0 44:16 -> 44:29, (#20 + 0) +Counter in file 0 45:9 -> 45:23, #25 +Counter in file 0 45:23 -> 45:23, #24 +Counter in file 0 46:15 -> 46:28, ((#20 + 0) - #24) +Counter in file 0 47:12 -> 47:25, (((#20 + 0) - #24) - #7) +Counter in file 0 47:29 -> 47:42, ((((#20 + 0) - #24) - #7) - #31) +Counter in file 0 47:42 -> 47:43, (((((#20 + 0) - #24) - #7) - #31) - #32) +Counter in file 0 47:42 -> 47:43, (#31 + #32) +Counter in file 0 47:42 -> 47:42, #32 +Counter in file 0 47:42 -> 47:42, #31 +Counter in file 0 47:46 -> 47:60, (((#31 + #32) + (((((#20 + 0) - #24) - #7) - #31) - #32)) - #33) +Counter in file 0 47:60 -> 47:61, (#33 + #34) +Counter in file 0 47:60 -> 47:61, ((((#31 + #32) + (((((#20 + 0) - #24) - #7) - #31) - #32)) - #33) - #34) +Counter in file 0 47:60 -> 47:60, #34 +Counter in file 0 47:60 -> 47:60, #33 +Counter in file 0 47:61 -> 49:10, #37 +Counter in file 0 49:10 -> 49:11, (((#33 + #34) + ((((#31 + #32) + (((((#20 + 0) - #24) - #7) - #31) - #32)) - #33) - #34)) - #37) +Counter in file 0 50:9 -> 50:23, #28 +Counter in file 0 52:13 -> 54:15, #7 +Counter in file 0 57:9 -> 57:10, (#9 + #10) +Counter in file 0 57:16 -> 57:29, ((#25 + #28) + 0) +Counter in file 0 58:9 -> 58:23, #9 +Counter in file 0 58:23 -> 58:23, #29 +Counter in file 0 59:15 -> 59:28, ((#25 + #28) - #29) +Counter in file 0 60:12 -> 60:25, (((#25 + #28) - #29) - #6) +Counter in file 0 60:29 -> 60:42, ((((#25 + #28) - #29) - #6) - #35) +Counter in file 0 60:42 -> 60:43, (#35 + #36) +Counter in file 0 60:42 -> 60:43, (((((#25 + #28) - #29) - #6) - #35) - #36) +Counter in file 0 60:42 -> 60:42, #35 +Counter in file 0 60:42 -> 60:42, #36 +Counter in file 0 60:46 -> 60:60, (((#35 + #36) + (((((#25 + #28) - #29) - #6) - #35) - #36)) - #38) +Counter in file 0 60:60 -> 60:61, (#38 + #39) +Counter in file 0 60:60 -> 60:61, ((((#35 + #36) + (((((#25 + #28) - #29) - #6) - #35) - #36)) - #38) - #39) +Counter in file 0 60:60 -> 60:60, #38 +Counter in file 0 60:60 -> 60:60, #39 +Counter in file 0 60:61 -> 62:10, #40 +Counter in file 0 62:10 -> 62:11, (((#38 + #39) + ((((#35 + #36) + (((((#25 + #28) - #29) - #6) - #35) - #36)) - #38) - #39)) - #40) +Counter in file 0 63:9 -> 63:23, #10 +Counter in file 0 65:9 -> 65:15, #6 +Counter in file 0 67:1 -> 67:2, ((#9 + #10) + (((#6 + #7) + #8) + (((#2 + 0) - #3) - #5))) +Counter in file 0 67:1 -> 67:2, (((#6 + #7) + #8) + (((#2 + 0) - #3) - #5)) +Emitting segments for file: ../coverage/various_conditions.rs +Combined regions: + 4:9 -> 4:26 (count=1) + 5:8 -> 5:12 (count=1) + 5:13 -> 7:6 (count=1) + 10:9 -> 10:10 (count=1) + 10:16 -> 10:29 (count=1) + 11:9 -> 12:10 (count=1) + 11:23 -> 11:23 (count=1) + 13:15 -> 13:28 (count=0) + 14:12 -> 14:25 (count=0) + 14:29 -> 14:42 (count=0) + 14:42 -> 14:43 (count=0) + 14:42 -> 14:42 (count=0) + 14:46 -> 14:60 (count=0) + 14:60 -> 14:61 (count=0) + 14:60 -> 14:60 (count=0) + 14:61 -> 16:10 (count=0) + 16:10 -> 16:11 (count=0) + 17:9 -> 18:18 (count=0) + 20:9 -> 20:15 (count=0) + 23:9 -> 23:26 (count=1) + 24:8 -> 24:12 (count=1) + 24:13 -> 26:6 (count=1) + 28:8 -> 28:21 (count=1) + 29:9 -> 29:23 (count=1) + 29:23 -> 29:23 (count=1) + 30:15 -> 30:28 (count=0) + 31:12 -> 31:25 (count=0) + 31:29 -> 31:42 (count=0) + 31:42 -> 31:43 (count=0) + 31:42 -> 31:42 (count=0) + 31:46 -> 31:60 (count=0) + 31:60 -> 31:61 (count=0) + 31:60 -> 31:60 (count=0) + 31:61 -> 33:10 (count=0) + 33:10 -> 33:11 (count=0) + 34:9 -> 34:23 (count=0) + 36:9 -> 36:15 (count=0) + 39:9 -> 39:26 (count=1) + 40:8 -> 40:12 (count=1) + 40:13 -> 42:6 (count=1) + 44:9 -> 44:10 (count=0) + 44:16 -> 44:29 (count=1) + 45:9 -> 45:23 (count=0) + 45:23 -> 45:23 (count=0) + 46:15 -> 46:28 (count=1) + 47:12 -> 47:25 (count=0) + 47:29 -> 47:42 (count=0) + 47:42 -> 47:43 (count=0) + 47:42 -> 47:42 (count=0) + 47:46 -> 47:60 (count=0) + 47:60 -> 47:61 (count=0) + 47:60 -> 47:60 (count=0) + 47:61 -> 49:10 (count=0) + 49:10 -> 49:11 (count=0) + 50:9 -> 50:23 (count=0) + 52:13 -> 54:15 (count=1) + 57:9 -> 57:10 (count=0) + 57:16 -> 57:29 (count=0) + 58:9 -> 58:23 (count=0) + 58:23 -> 58:23 (count=0) + 59:15 -> 59:28 (count=0) + 60:12 -> 60:25 (count=0) + 60:29 -> 60:42 (count=0) + 60:42 -> 60:43 (count=0) + 60:42 -> 60:42 (count=0) + 60:46 -> 60:60 (count=0) + 60:60 -> 60:61 (count=0) + 60:60 -> 60:60 (count=0) + 60:61 -> 62:10 (count=0) + 62:10 -> 62:11 (count=0) + 63:9 -> 63:23 (count=0) + 65:9 -> 65:15 (count=0) + 67:1 -> 67:2 (count=2) +Segment at 4:9 (count = 1), RegionEntry +Segment at 4:26 (count = 0), Skipped +Segment at 5:8 (count = 1), RegionEntry +Segment at 5:12 (count = 0), Skipped +Segment at 5:13 (count = 1), RegionEntry +Segment at 7:6 (count = 0), Skipped +Segment at 10:9 (count = 1), RegionEntry +Segment at 10:10 (count = 0), Skipped +Segment at 10:16 (count = 1), RegionEntry +Segment at 10:29 (count = 0), Skipped +Segment at 11:9 (count = 1), RegionEntry +Segment at 11:23 (count = 1) +Segment at 12:10 (count = 0), Skipped +Segment at 13:15 (count = 0), RegionEntry +Segment at 13:28 (count = 0), Skipped +Segment at 14:12 (count = 0), RegionEntry +Segment at 14:25 (count = 0), Skipped +Segment at 14:29 (count = 0), RegionEntry +Segment at 14:42 (count = 0) +Segment at 14:43 (count = 0), Skipped +Segment at 14:46 (count = 0), RegionEntry +Segment at 14:60 (count = 0) +Segment at 14:61 (count = 0), RegionEntry +Segment at 16:10 (count = 0), RegionEntry +Segment at 16:11 (count = 0), Skipped +Segment at 17:9 (count = 0), RegionEntry +Segment at 18:18 (count = 0), Skipped +Segment at 20:9 (count = 0), RegionEntry +Segment at 20:15 (count = 0), Skipped +Segment at 23:9 (count = 1), RegionEntry +Segment at 23:26 (count = 0), Skipped +Segment at 24:8 (count = 1), RegionEntry +Segment at 24:12 (count = 0), Skipped +Segment at 24:13 (count = 1), RegionEntry +Segment at 26:6 (count = 0), Skipped +Segment at 28:8 (count = 1), RegionEntry +Segment at 28:21 (count = 0), Skipped +Segment at 29:9 (count = 1), RegionEntry +Segment at 29:23 (count = 1), Gap +Segment at 30:15 (count = 0), RegionEntry +Segment at 30:28 (count = 0), Skipped +Segment at 31:12 (count = 0), RegionEntry +Segment at 31:25 (count = 0), Skipped +Segment at 31:29 (count = 0), RegionEntry +Segment at 31:42 (count = 0) +Segment at 31:43 (count = 0), Skipped +Segment at 31:46 (count = 0), RegionEntry +Segment at 31:60 (count = 0) +Segment at 31:61 (count = 0), RegionEntry +Segment at 33:10 (count = 0), RegionEntry +Segment at 33:11 (count = 0), Skipped +Segment at 34:9 (count = 0), RegionEntry +Segment at 34:23 (count = 0), Skipped +Segment at 36:9 (count = 0), RegionEntry +Segment at 36:15 (count = 0), Skipped +Segment at 39:9 (count = 1), RegionEntry +Segment at 39:26 (count = 0), Skipped +Segment at 40:8 (count = 1), RegionEntry +Segment at 40:12 (count = 0), Skipped +Segment at 40:13 (count = 1), RegionEntry +Segment at 42:6 (count = 0), Skipped +Segment at 44:9 (count = 0), RegionEntry +Segment at 44:10 (count = 0), Skipped +Segment at 44:16 (count = 1), RegionEntry +Segment at 44:29 (count = 0), Skipped +Segment at 45:9 (count = 0), RegionEntry +Segment at 45:23 (count = 0), Gap +Segment at 46:15 (count = 1), RegionEntry +Segment at 46:28 (count = 0), Skipped +Segment at 47:12 (count = 0), RegionEntry +Segment at 47:25 (count = 0), Skipped +Segment at 47:29 (count = 0), RegionEntry +Segment at 47:42 (count = 0) +Segment at 47:43 (count = 0), Skipped +Segment at 47:46 (count = 0), RegionEntry +Segment at 47:60 (count = 0) +Segment at 47:61 (count = 0), RegionEntry +Segment at 49:10 (count = 0), RegionEntry +Segment at 49:11 (count = 0), Skipped +Segment at 50:9 (count = 0), RegionEntry +Segment at 50:23 (count = 0), Skipped +Segment at 52:13 (count = 1), RegionEntry +Segment at 54:15 (count = 0), Skipped +Segment at 57:9 (count = 0), RegionEntry +Segment at 57:10 (count = 0), Skipped +Segment at 57:16 (count = 0), RegionEntry +Segment at 57:29 (count = 0), Skipped +Segment at 58:9 (count = 0), RegionEntry +Segment at 58:23 (count = 0), Gap +Segment at 59:15 (count = 0), RegionEntry +Segment at 59:28 (count = 0), Skipped +Segment at 60:12 (count = 0), RegionEntry +Segment at 60:25 (count = 0), Skipped +Segment at 60:29 (count = 0), RegionEntry +Segment at 60:42 (count = 0) +Segment at 60:43 (count = 0), Skipped +Segment at 60:46 (count = 0), RegionEntry +Segment at 60:60 (count = 0) +Segment at 60:61 (count = 0), RegionEntry +Segment at 62:10 (count = 0), RegionEntry +Segment at 62:11 (count = 0), Skipped +Segment at 63:9 (count = 0), RegionEntry +Segment at 63:23 (count = 0), Skipped +Segment at 65:9 (count = 0), RegionEntry +Segment at 65:15 (count = 0), Skipped +Segment at 67:1 (count = 2), RegionEntry +Segment at 67:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while_early_return.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while_early_return.txt new file mode 100644 index 0000000000000..2ca90e5eb2fd5 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while_early_return.txt @@ -0,0 +1,40 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/while_early_return.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/while_early_return +Counter in file 0 5:9 -> 5:27, #1 +Counter in file 0 7:9 -> 9:10, (#1 + #2) +Counter in file 0 12:13 -> 14:14, ((#1 + #2) - #3) +Counter in file 0 18:21 -> 20:22, #6 +Counter in file 0 22:21 -> 22:27, #4 +Counter in file 0 26:21 -> 26:27, #5 +Counter in file 0 30:9 -> 32:10, #2 +Counter in file 0 35:5 -> 35:11, #3 +Counter in file 0 36:1 -> 36:2, (#4 + #5) +Counter in file 0 36:1 -> 36:2, ((#4 + #5) + #3) +Emitting segments for file: ../coverage/while_early_return.rs +Combined regions: + 5:9 -> 5:27 (count=1) + 7:9 -> 9:10 (count=7) + 12:13 -> 14:14 (count=7) + 18:21 -> 20:22 (count=1) + 22:21 -> 22:27 (count=0) + 26:21 -> 26:27 (count=1) + 30:9 -> 32:10 (count=6) + 35:5 -> 35:11 (count=0) + 36:1 -> 36:2 (count=2) +Segment at 5:9 (count = 1), RegionEntry +Segment at 5:27 (count = 0), Skipped +Segment at 7:9 (count = 7), RegionEntry +Segment at 9:10 (count = 0), Skipped +Segment at 12:13 (count = 7), RegionEntry +Segment at 14:14 (count = 0), Skipped +Segment at 18:21 (count = 1), RegionEntry +Segment at 20:22 (count = 0), Skipped +Segment at 22:21 (count = 0), RegionEntry +Segment at 22:27 (count = 0), Skipped +Segment at 26:21 (count = 1), RegionEntry +Segment at 26:27 (count = 0), Skipped +Segment at 30:9 (count = 6), RegionEntry +Segment at 32:10 (count = 0), Skipped +Segment at 35:5 (count = 0), RegionEntry +Segment at 35:11 (count = 0), Skipped +Segment at 36:1 (count = 2), RegionEntry +Segment at 36:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.closure.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.closure.json index e8d871f00a36c..bff55300b3ca3 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.closure.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.closure.json @@ -17,8 +17,8 @@ }, "lines": { "count": 91, - "covered": 75, - "percent": 82.41758241758241 + "covered": 77, + "percent": 84.61538461538461 }, "regions": { "count": 25, @@ -42,8 +42,8 @@ }, "lines": { "count": 91, - "covered": 75, - "percent": 82.41758241758241 + "covered": 77, + "percent": 84.61538461538461 }, "regions": { "count": 25, diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.lazy_boolean.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.lazy_boolean.json index f5fae0cb82bd5..0d81693814606 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.lazy_boolean.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.lazy_boolean.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 21, - "covered": 19, - "percent": 90.47619047619048 + "count": 40, + "covered": 29, + "percent": 72.5 }, "regions": { - "count": 33, - "covered": 21, - "notcovered": 12, - "percent": 63.63636363636363 + "count": 37, + "covered": 26, + "notcovered": 11, + "percent": 70.27027027027027 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 21, - "covered": 19, - "percent": 90.47619047619048 + "count": 40, + "covered": 29, + "percent": 72.5 }, "regions": { - "count": 33, - "covered": 21, - "notcovered": 12, - "percent": 63.63636363636363 + "count": 37, + "covered": 26, + "notcovered": 11, + "percent": 70.27027027027027 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_loop.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_loop.json index bcc0e1331e331..ada6bb062dd1e 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_loop.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_loop.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 18, - "covered": 18, + "count": 19, + "covered": 19, "percent": 100 }, "regions": { - "count": 10, - "covered": 9, + "count": 9, + "covered": 8, "notcovered": 1, - "percent": 90 + "percent": 88.88888888888889 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 18, - "covered": 18, + "count": 19, + "covered": 19, "percent": 100 }, "regions": { - "count": 10, - "covered": 9, + "count": 9, + "covered": 8, "notcovered": 1, - "percent": 90 + "percent": 88.88888888888889 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_match.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_match.json index ed1705e587062..2ae9ad9c4ebd4 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_match.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_match.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 23, - "covered": 23, - "percent": 100 + "count": 28, + "covered": 24, + "percent": 85.71428571428571 }, "regions": { - "count": 16, + "count": 15, "covered": 14, - "notcovered": 2, - "percent": 87.5 + "notcovered": 1, + "percent": 93.33333333333333 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 23, - "covered": 23, - "percent": 100 + "count": 28, + "covered": 24, + "percent": 85.71428571428571 }, "regions": { - "count": 16, + "count": 15, "covered": 14, - "notcovered": 2, - "percent": 87.5 + "notcovered": 1, + "percent": 93.33333333333333 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.try_error_result.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.try_error_result.json index c647d6e8de55d..bcefa927e8fae 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.try_error_result.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.try_error_result.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 17, - "covered": 16, - "percent": 94.11764705882352 + "count": 20, + "covered": 18, + "percent": 90 }, "regions": { - "count": 21, + "count": 18, "covered": 15, - "notcovered": 6, - "percent": 71.42857142857143 + "notcovered": 3, + "percent": 83.33333333333334 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 17, - "covered": 16, - "percent": 94.11764705882352 + "count": 20, + "covered": 18, + "percent": 90 }, "regions": { - "count": 21, + "count": 18, "covered": 15, - "notcovered": 6, - "percent": 71.42857142857143 + "notcovered": 3, + "percent": 83.33333333333334 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.various_conditions.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.various_conditions.json index c7d80a446fe2c..d5f954b4f7e07 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.various_conditions.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.various_conditions.json @@ -17,14 +17,14 @@ }, "lines": { "count": 49, - "covered": 23, - "percent": 46.93877551020408 + "covered": 24, + "percent": 48.97959183673469 }, "regions": { - "count": 104, - "covered": 23, - "notcovered": 81, - "percent": 22.115384615384613 + "count": 70, + "covered": 19, + "notcovered": 51, + "percent": 27.142857142857142 } } } @@ -42,14 +42,14 @@ }, "lines": { "count": 49, - "covered": 23, - "percent": 46.93877551020408 + "covered": 24, + "percent": 48.97959183673469 }, "regions": { - "count": 104, - "covered": 23, - "notcovered": 81, - "percent": 22.115384615384613 + "count": 70, + "covered": 19, + "notcovered": 51, + "percent": 27.142857142857142 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while_early_return.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while_early_return.json index ea6fd2ac12f35..a116a91a60aae 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while_early_return.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while_early_return.json @@ -21,10 +21,10 @@ "percent": 88.23529411764706 }, "regions": { - "count": 11, - "covered": 9, + "count": 10, + "covered": 8, "notcovered": 2, - "percent": 81.81818181818183 + "percent": 80 } } } @@ -46,10 +46,10 @@ "percent": 88.23529411764706 }, "regions": { - "count": 11, - "covered": 9, + "count": 10, + "covered": 8, "notcovered": 2, - "percent": 81.81818181818183 + "percent": 80 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.closure.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.closure.txt index 17054490e9b3c..aef26a62e25fb 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.closure.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.closure.txt @@ -62,7 +62,7 @@ 62| 1| let mut countdown = 0; 63| 1| if is_false { 64| 0| countdown = 10; - 65| 0| } + 65| 1| } 66| 1| "alt string 3".to_owned() 67| 1| } 68| 1| ) @@ -77,7 +77,7 @@ 77| 1| let mut countdown = 0; 78| 1| if is_false { 79| 0| countdown = 10; - 80| 0| } + 80| 1| } 81| 1| "alt string 4".to_owned() 82| 1| }; 83| 1| println!( diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.if.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.if.txt index bc2f9b108b2f3..85e6440ab3729 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.if.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.if.txt @@ -25,5 +25,6 @@ 25| 1| 10 26| 1| ; 27| 1| } + ^0 28| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.inner_items.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.inner_items.txt index b13ca83d018f9..4a51f842a4bb2 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.inner_items.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.inner_items.txt @@ -10,6 +10,7 @@ 10| 1| if is_true { 11| 1| countdown = 10; 12| 1| } + ^0 13| | 14| | mod in_mod { 15| | const IN_MOD_CONST: u32 = 1000; @@ -48,6 +49,7 @@ 48| 1| if is_true { 49| 1| in_func(countdown); 50| 1| } + ^0 51| | 52| 1| let mut val = InStruct { 53| 1| in_struct_field: 101, diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.lazy_boolean.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.lazy_boolean.txt index ded4369751587..eff74aa3b82f6 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.lazy_boolean.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.lazy_boolean.txt @@ -12,6 +12,7 @@ 12| 1| b = 10; 13| 1| c = 100; 14| 1| } + ^0 15| | let 16| 1| somebool 17| | = @@ -26,19 +27,38 @@ 26| | || 27| 1| b < c 28| | ; - 29| | let - 30| 1| somebool - 31| | = - 32| 1| a < b - 33| | && - 34| 1| b < c - 35| | ; - 36| | let - 37| 1| somebool - 38| | = - 39| 1| b < a - 40| | && - 41| 0| b < c - 42| | ; - 43| 1|} + 29| 1| let somebool = a < b && b < c; + 30| 1| let somebool = b < a && b < c; + ^0 + 31| | + 32| | if + 33| 1| ! + 34| 1| is_true + 35| 0| { + 36| 0| a = 2 + 37| 0| ; + 38| 1| } + 39| | + 40| | if + 41| 1| is_true + 42| 1| { + 43| 1| b = 30 + 44| 1| ; + 45| 1| } + 46| | else + 47| 0| { + 48| 0| c = 400 + 49| 0| ; + 50| 0| } + 51| | + 52| 1| if !is_true { + 53| 0| a = 2; + 54| 1| } + 55| | + 56| 1| if is_true { + 57| 1| b = 30; + 58| 1| } else { + 59| 0| c = 400; + 60| 0| } + 61| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_loop.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_loop.txt index f1acb7c545940..064930dd75c93 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_loop.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_loop.txt @@ -16,6 +16,7 @@ 16| 1| 10 17| 1| ; 18| 1| } + ^0 19| | 20| | loop 21| | { @@ -31,6 +32,6 @@ 31| 10| -= 32| 10| 1 33| | ; - 34| | } + 34| 1| } 35| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_match.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_match.txt index d40acc40ace3e..55b23dd882035 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_match.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_match.txt @@ -10,15 +10,16 @@ 10| 1| if is_true { 11| 1| countdown = 0; 12| 1| } + ^0 13| | 14| | for 15| 2| _ 16| | in 17| 3| 0..2 - 18| | { - 19| | let z - 20| | ; - 21| | match + 18| 0| { + 19| 0| let z + 20| 0| ; + 21| 0| match 22| 2| countdown 23| | { 24| 1| x @@ -26,6 +27,7 @@ 26| 2| x 27| 2| < 28| 2| 1 + ^1 29| | => 30| 1| { 31| 1| z = countdown @@ -39,6 +41,6 @@ 39| | => 40| 1| {} 41| | } - 42| | } + 42| 3| } 43| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.try_error_result.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.try_error_result.txt index 0714b8d44892d..489a5324a4446 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.try_error_result.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.try_error_result.txt @@ -17,7 +17,7 @@ 17| 6| _ 18| | in 19| 6| 0..10 - 20| | { + 20| 0| { 21| 6| countdown 22| 6| -= 1 23| 6| ; @@ -30,8 +30,8 @@ 30| | { 31| 5| call(/*return_error=*/ false)?; ^0 - 32| | } - 33| | } + 32| 5| } + 33| 5| } 34| 0| Ok(()) - 35| 1|} + 35| 2|} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.various_conditions.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.various_conditions.txt index 173ff4aa4c481..9f2deae8a9e55 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.various_conditions.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.various_conditions.txt @@ -27,7 +27,8 @@ 27| | 28| 1| if countdown > 7 { 29| 1| countdown -= 4; - 30| 0| } else if countdown > 2 { + 30| 1| } else if countdown > 2 { + ^0 31| 0| if countdown < 1 || countdown > 5 || countdown != 9 { 32| 0| countdown = 0; 33| 0| } @@ -65,5 +66,5 @@ 64| | } else { 65| 0| return; 66| | }; - 67| 1|} + 67| 2|} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while_early_return.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while_early_return.txt index 26041136d2f4c..e5c5b05a6fc80 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while_early_return.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while_early_return.txt @@ -33,7 +33,7 @@ 33| | ; 34| | } 35| 0| Ok(()) - 36| 1|} + 36| 2|} 37| | 38| |// ISSUE(77553): Originally, this test had `Err(1)` on line 22 (instead of `Ok(())`) and 39| |// `std::process::exit(2)` on line 26 (instead of `Err(1)`); and this worked as expected on Linux diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.closure.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.closure.txt new file mode 100644 index 0000000000000..d443ba0aba3a0 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.closure.txt @@ -0,0 +1,95 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/closure.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/closure +Counter in file 0 20:21 -> 20:38, #1 +Counter in file 0 21:20 -> 21:28, (#1 + 0) +Counter in file 0 21:29 -> 23:18, #2 +Counter in file 0 23:18 -> 23:19, (#1 - #2) +Counter in file 0 24:17 -> 25:14, (#2 + (#1 - #2)) +Counter in file 0 3:11 -> 18:13, #1 +Counter in file 0 25:14 -> 33:9, (#1 + 0) +Counter in file 0 40:6 -> 60:13, (#1 + 0) +Counter in file 0 67:14 -> 75:9, (#1 + 0) +Counter in file 0 82:6 -> 93:2, (#1 + 0) +Counter in file 0 77:13 -> 77:30, #1 +Counter in file 0 78:12 -> 78:20, (#1 + 0) +Counter in file 0 78:21 -> 80:10, #2 +Counter in file 0 80:10 -> 80:11, (#1 - #2) +Counter in file 0 81:9 -> 82:6, (#2 + (#1 - #2)) +Counter in file 0 62:21 -> 62:38, #1 +Counter in file 0 63:20 -> 63:28, (#1 + 0) +Counter in file 0 63:29 -> 65:18, #2 +Counter in file 0 65:18 -> 65:19, (#1 - #2) +Counter in file 0 66:17 -> 67:14, (#2 + (#1 - #2)) +Counter in file 0 35:13 -> 35:30, #1 +Counter in file 0 36:12 -> 36:20, (#1 + 0) +Counter in file 0 36:21 -> 38:10, #2 +Counter in file 0 38:10 -> 38:11, (#1 - #2) +Counter in file 0 39:9 -> 40:6, (#2 + (#1 - #2)) +Emitting segments for file: ../coverage/closure.rs +Combined regions: + 3:11 -> 18:13 (count=1) + 20:21 -> 20:38 (count=0) + 21:20 -> 21:28 (count=0) + 21:29 -> 23:18 (count=0) + 23:18 -> 23:19 (count=0) + 24:17 -> 25:14 (count=0) + 25:14 -> 33:9 (count=1) + 35:13 -> 35:30 (count=0) + 36:12 -> 36:20 (count=0) + 36:21 -> 38:10 (count=0) + 38:10 -> 38:11 (count=0) + 39:9 -> 40:6 (count=0) + 40:6 -> 60:13 (count=1) + 62:21 -> 62:38 (count=1) + 63:20 -> 63:28 (count=1) + 63:29 -> 65:18 (count=0) + 65:18 -> 65:19 (count=1) + 66:17 -> 67:14 (count=1) + 67:14 -> 75:9 (count=1) + 77:13 -> 77:30 (count=1) + 78:12 -> 78:20 (count=1) + 78:21 -> 80:10 (count=0) + 80:10 -> 80:11 (count=1) + 81:9 -> 82:6 (count=1) + 82:6 -> 93:2 (count=1) +Segment at 3:11 (count = 1), RegionEntry +Segment at 18:13 (count = 0), Skipped +Segment at 20:21 (count = 0), RegionEntry +Segment at 20:38 (count = 0), Skipped +Segment at 21:20 (count = 0), RegionEntry +Segment at 21:28 (count = 0), Skipped +Segment at 21:29 (count = 0), RegionEntry +Segment at 23:18 (count = 0), RegionEntry +Segment at 23:19 (count = 0), Skipped +Segment at 24:17 (count = 0), RegionEntry +Segment at 25:14 (count = 1), RegionEntry +Segment at 33:9 (count = 0), Skipped +Segment at 35:13 (count = 0), RegionEntry +Segment at 35:30 (count = 0), Skipped +Segment at 36:12 (count = 0), RegionEntry +Segment at 36:20 (count = 0), Skipped +Segment at 36:21 (count = 0), RegionEntry +Segment at 38:10 (count = 0), RegionEntry +Segment at 38:11 (count = 0), Skipped +Segment at 39:9 (count = 0), RegionEntry +Segment at 40:6 (count = 1), RegionEntry +Segment at 60:13 (count = 0), Skipped +Segment at 62:21 (count = 1), RegionEntry +Segment at 62:38 (count = 0), Skipped +Segment at 63:20 (count = 1), RegionEntry +Segment at 63:28 (count = 0), Skipped +Segment at 63:29 (count = 0), RegionEntry +Segment at 65:18 (count = 1), RegionEntry +Segment at 65:19 (count = 0), Skipped +Segment at 66:17 (count = 1), RegionEntry +Segment at 67:14 (count = 1), RegionEntry +Segment at 75:9 (count = 0), Skipped +Segment at 77:13 (count = 1), RegionEntry +Segment at 77:30 (count = 0), Skipped +Segment at 78:12 (count = 1), RegionEntry +Segment at 78:20 (count = 0), Skipped +Segment at 78:21 (count = 0), RegionEntry +Segment at 80:10 (count = 1), RegionEntry +Segment at 80:11 (count = 0), Skipped +Segment at 81:9 (count = 1), RegionEntry +Segment at 82:6 (count = 1), RegionEntry +Segment at 93:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.drop_trait.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.drop_trait.txt new file mode 100644 index 0000000000000..08fbbd13adbe1 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.drop_trait.txt @@ -0,0 +1,23 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/drop_trait.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/drop_trait +Counter in file 0 9:24 -> 11:6, #1 +Counter in file 0 15:9 -> 17:42, #1 +Counter in file 0 19:8 -> 19:12, (#1 + 0) +Counter in file 0 20:9 -> 21:22, #2 +Counter in file 0 27:1 -> 27:2, (#2 + 0) +Emitting segments for file: ../coverage/drop_trait.rs +Combined regions: + 9:24 -> 11:6 (count=2) + 15:9 -> 17:42 (count=1) + 19:8 -> 19:12 (count=1) + 20:9 -> 21:22 (count=1) + 27:1 -> 27:2 (count=1) +Segment at 9:24 (count = 2), RegionEntry +Segment at 11:6 (count = 0), Skipped +Segment at 15:9 (count = 1), RegionEntry +Segment at 17:42 (count = 0), Skipped +Segment at 19:8 (count = 1), RegionEntry +Segment at 19:12 (count = 0), Skipped +Segment at 20:9 (count = 1), RegionEntry +Segment at 21:22 (count = 0), Skipped +Segment at 27:1 (count = 1), RegionEntry +Segment at 27:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.generics.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.generics.txt new file mode 100644 index 0000000000000..eb707c5e7acc2 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.generics.txt @@ -0,0 +1,49 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/generics.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/generics +Counter in file 0 17:24 -> 19:6, #1 +Counter in file 0 17:24 -> 19:6, #1 +Counter in file 0 23:9 -> 28:28, #1 +Counter in file 0 30:8 -> 30:12, (#1 + 0) +Counter in file 0 31:9 -> 32:22, #2 +Counter in file 0 38:1 -> 38:2, (#2 + 0) +Counter in file 0 10:49 -> 12:6, #1 +Counter in file 0 10:49 -> 12:6, #1 +Emitting segments for file: ../coverage/generics.rs +Combined regions: + 10:49 -> 12:6 (count=3) + 17:24 -> 19:6 (count=2) + 23:9 -> 28:28 (count=1) + 30:8 -> 30:12 (count=1) + 31:9 -> 32:22 (count=1) + 38:1 -> 38:2 (count=1) +Segment at 10:49 (count = 3), RegionEntry +Segment at 12:6 (count = 0), Skipped +Segment at 17:24 (count = 2), RegionEntry +Segment at 19:6 (count = 0), Skipped +Segment at 23:9 (count = 1), RegionEntry +Segment at 28:28 (count = 0), Skipped +Segment at 30:8 (count = 1), RegionEntry +Segment at 30:12 (count = 0), Skipped +Segment at 31:9 (count = 1), RegionEntry +Segment at 32:22 (count = 0), Skipped +Segment at 38:1 (count = 1), RegionEntry +Segment at 38:2 (count = 0), Skipped +Emitting segments for function: _RNvMCs4fqI2P2rA04_8genericsINtB2_8FireworkdE12set_strengthB2_ +Combined regions: + 10:49 -> 12:6 (count=2) +Segment at 10:49 (count = 2), RegionEntry +Segment at 12:6 (count = 0), Skipped +Emitting segments for function: _RNvMCs4fqI2P2rA04_8genericsINtB2_8FireworklE12set_strengthB2_ +Combined regions: + 10:49 -> 12:6 (count=1) +Segment at 10:49 (count = 1), RegionEntry +Segment at 12:6 (count = 0), Skipped +Emitting segments for function: _RNvXs_Cs4fqI2P2rA04_8genericsINtB4_8FireworklENtNtNtCs7f2nZg1zwMz_4core3ops4drop4Drop4dropB4_ +Combined regions: + 17:24 -> 19:6 (count=1) +Segment at 17:24 (count = 1), RegionEntry +Segment at 19:6 (count = 0), Skipped +Emitting segments for function: _RNvXs_Cs4fqI2P2rA04_8genericsINtB4_8FireworkdENtNtNtCs7f2nZg1zwMz_4core3ops4drop4Drop4dropB4_ +Combined regions: + 17:24 -> 19:6 (count=1) +Segment at 17:24 (count = 1), RegionEntry +Segment at 19:6 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.if.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.if.txt new file mode 100644 index 0000000000000..d186f440ddb1f --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.if.txt @@ -0,0 +1,22 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/if.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/if +Counter in file 0 8:5 -> 18:10, #1 +Counter in file 0 21:9 -> 21:16, (#1 + 0) +Counter in file 0 22:5 -> 27:6, #2 +Counter in file 0 27:6 -> 27:7, (#1 - #2) +Counter in file 0 28:1 -> 28:2, (#2 + (#1 - #2)) +Emitting segments for file: ../coverage/if.rs +Combined regions: + 8:5 -> 18:10 (count=1) + 21:9 -> 21:16 (count=1) + 22:5 -> 27:6 (count=1) + 27:6 -> 27:7 (count=0) + 28:1 -> 28:2 (count=1) +Segment at 8:5 (count = 1), RegionEntry +Segment at 18:10 (count = 0), Skipped +Segment at 21:9 (count = 1), RegionEntry +Segment at 21:16 (count = 0), Skipped +Segment at 22:5 (count = 1), RegionEntry +Segment at 27:6 (count = 0), RegionEntry +Segment at 27:7 (count = 0), Skipped +Segment at 28:1 (count = 1), RegionEntry +Segment at 28:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.if_else.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.if_else.txt new file mode 100644 index 0000000000000..8220060f26ff2 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.if_else.txt @@ -0,0 +1,31 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/if_else.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/if_else +Counter in file 0 7:9 -> 11:16, #1 +Counter in file 0 12:5 -> 17:6, #2 +Counter in file 0 20:9 -> 22:16, (#1 - #2) +Counter in file 0 26:9 -> 26:16, (#2 + (#1 - #2)) +Counter in file 0 27:5 -> 32:6, #3 +Counter in file 0 34:5 -> 39:6, ((#2 + (#1 - #2)) - #3) +Counter in file 0 40:1 -> 40:2, (#3 + ((#2 + (#1 - #2)) - #3)) +Emitting segments for file: ../coverage/if_else.rs +Combined regions: + 7:9 -> 11:16 (count=1) + 12:5 -> 17:6 (count=1) + 20:9 -> 22:16 (count=0) + 26:9 -> 26:16 (count=1) + 27:5 -> 32:6 (count=1) + 34:5 -> 39:6 (count=0) + 40:1 -> 40:2 (count=1) +Segment at 7:9 (count = 1), RegionEntry +Segment at 11:16 (count = 0), Skipped +Segment at 12:5 (count = 1), RegionEntry +Segment at 17:6 (count = 0), Skipped +Segment at 20:9 (count = 0), RegionEntry +Segment at 22:16 (count = 0), Skipped +Segment at 26:9 (count = 1), RegionEntry +Segment at 26:16 (count = 0), Skipped +Segment at 27:5 (count = 1), RegionEntry +Segment at 32:6 (count = 0), Skipped +Segment at 34:5 (count = 0), RegionEntry +Segment at 39:6 (count = 0), Skipped +Segment at 40:1 (count = 1), RegionEntry +Segment at 40:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.inner_items.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.inner_items.txt new file mode 100644 index 0000000000000..c173cc20f815a --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.inner_items.txt @@ -0,0 +1,61 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/inner_items.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/inner_items +Counter in file 0 19:13 -> 19:18, #1 +Counter in file 0 20:13 -> 20:14, #2 +Counter in file 0 20:17 -> 20:22, (#1 + 0) +Counter in file 0 21:9 -> 22:6, (#2 + 0) +Counter in file 0 7:9 -> 9:26, #1 +Counter in file 0 10:8 -> 10:15, (#1 + 0) +Counter in file 0 10:16 -> 12:6, #2 +Counter in file 0 12:6 -> 12:7, (#1 - #2) +Counter in file 0 48:8 -> 48:15, (#2 + (#1 - #2)) +Counter in file 0 48:16 -> 50:6, #3 +Counter in file 0 50:6 -> 50:7, ((#2 + (#1 - #2)) - #3) +Counter in file 0 52:9 -> 57:2, (#3 + ((#2 + (#1 - #2)) - #3)) +Counter in file 0 33:42 -> 36:10, #1 +Counter in file 0 41:37 -> 41:41, #1 +Counter in file 0 42:13 -> 43:10, #2 +Emitting segments for file: ../coverage/inner_items.rs +Combined regions: + 7:9 -> 9:26 (count=1) + 10:8 -> 10:15 (count=1) + 10:16 -> 12:6 (count=1) + 12:6 -> 12:7 (count=0) + 19:13 -> 19:18 (count=3) + 20:13 -> 20:14 (count=3) + 20:17 -> 20:22 (count=3) + 21:9 -> 22:6 (count=3) + 33:42 -> 36:10 (count=1) + 41:37 -> 41:41 (count=1) + 42:13 -> 43:10 (count=1) + 48:8 -> 48:15 (count=1) + 48:16 -> 50:6 (count=1) + 50:6 -> 50:7 (count=0) + 52:9 -> 57:2 (count=1) +Segment at 7:9 (count = 1), RegionEntry +Segment at 9:26 (count = 0), Skipped +Segment at 10:8 (count = 1), RegionEntry +Segment at 10:15 (count = 0), Skipped +Segment at 10:16 (count = 1), RegionEntry +Segment at 12:6 (count = 0), RegionEntry +Segment at 12:7 (count = 0), Skipped +Segment at 19:13 (count = 3), RegionEntry +Segment at 19:18 (count = 0), Skipped +Segment at 20:13 (count = 3), RegionEntry +Segment at 20:14 (count = 0), Skipped +Segment at 20:17 (count = 3), RegionEntry +Segment at 20:22 (count = 0), Skipped +Segment at 21:9 (count = 3), RegionEntry +Segment at 22:6 (count = 0), Skipped +Segment at 33:42 (count = 1), RegionEntry +Segment at 36:10 (count = 0), Skipped +Segment at 41:37 (count = 1), RegionEntry +Segment at 41:41 (count = 0), Skipped +Segment at 42:13 (count = 1), RegionEntry +Segment at 43:10 (count = 0), Skipped +Segment at 48:8 (count = 1), RegionEntry +Segment at 48:15 (count = 0), Skipped +Segment at 48:16 (count = 1), RegionEntry +Segment at 50:6 (count = 0), RegionEntry +Segment at 50:7 (count = 0), Skipped +Segment at 52:9 (count = 1), RegionEntry +Segment at 57:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.lazy_boolean.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.lazy_boolean.txt new file mode 100644 index 0000000000000..39753bab2857a --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.lazy_boolean.txt @@ -0,0 +1,142 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/lazy_boolean.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/lazy_boolean +Counter in file 0 7:9 -> 9:42, #1 +Counter in file 0 10:8 -> 10:15, (#1 + 0) +Counter in file 0 10:16 -> 14:6, #2 +Counter in file 0 14:6 -> 14:7, (#1 - #2) +Counter in file 0 16:9 -> 16:17, ((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) +Counter in file 0 18:13 -> 18:18, (#2 + (#1 - #2)) +Counter in file 0 20:13 -> 20:18, ((#2 + (#1 - #2)) - #3) +Counter in file 0 20:18 -> 20:19, (((#2 + (#1 - #2)) - #3) - #4) +Counter in file 0 20:18 -> 20:19, (#3 + #4) +Counter in file 0 20:18 -> 20:18, #3 +Counter in file 0 20:18 -> 20:18, #4 +Counter in file 0 23:9 -> 23:17, ((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) +Counter in file 0 25:13 -> 25:18, (((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) + 0) +Counter in file 0 27:13 -> 27:18, (((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) +Counter in file 0 27:18 -> 27:19, (#5 + #6) +Counter in file 0 27:18 -> 27:19, ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6) +Counter in file 0 27:18 -> 27:18, #5 +Counter in file 0 27:18 -> 27:18, #6 +Counter in file 0 29:9 -> 29:17, ((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) +Counter in file 0 29:20 -> 29:25, (((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) + 0) +Counter in file 0 29:29 -> 29:34, #7 +Counter in file 0 29:34 -> 29:35, ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8) +Counter in file 0 29:34 -> 29:35, (#7 - #8) +Counter in file 0 29:34 -> 29:34, #8 +Counter in file 0 30:9 -> 30:17, ((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) +Counter in file 0 30:20 -> 30:25, (((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) + 0) +Counter in file 0 30:29 -> 30:34, #9 +Counter in file 0 30:34 -> 30:35, ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10) +Counter in file 0 30:34 -> 30:35, (#9 - #10) +Counter in file 0 30:34 -> 30:34, #10 +Counter in file 0 33:9 -> 34:16, (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) + 0) +Counter in file 0 35:5 -> 38:6, #11 +Counter in file 0 38:6 -> 38:7, (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11) +Counter in file 0 41:9 -> 41:16, (#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) +Counter in file 0 42:5 -> 45:6, #12 +Counter in file 0 47:5 -> 50:6, ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12) +Counter in file 0 52:8 -> 52:16, (#12 + ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12)) +Counter in file 0 52:17 -> 54:6, #13 +Counter in file 0 54:6 -> 54:7, ((#12 + ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12)) - #13) +Counter in file 0 56:8 -> 56:15, (#13 + ((#12 + ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12)) - #13)) +Counter in file 0 56:16 -> 58:6, #14 +Counter in file 0 58:12 -> 60:6, ((#13 + ((#12 + ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12)) - #13)) - #14) +Counter in file 0 61:1 -> 61:2, (#14 + ((#13 + ((#12 + ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12)) - #13)) - #14)) +Emitting segments for file: ../coverage/lazy_boolean.rs +Combined regions: + 7:9 -> 9:42 (count=1) + 10:8 -> 10:15 (count=1) + 10:16 -> 14:6 (count=1) + 14:6 -> 14:7 (count=0) + 16:9 -> 16:17 (count=1) + 18:13 -> 18:18 (count=1) + 20:13 -> 20:18 (count=0) + 20:18 -> 20:19 (count=1) + 20:18 -> 20:18 (count=1) + 23:9 -> 23:17 (count=1) + 25:13 -> 25:18 (count=1) + 27:13 -> 27:18 (count=1) + 27:18 -> 27:19 (count=1) + 27:18 -> 27:18 (count=1) + 29:9 -> 29:17 (count=1) + 29:20 -> 29:25 (count=1) + 29:29 -> 29:34 (count=1) + 29:34 -> 29:35 (count=1) + 29:34 -> 29:34 (count=0) + 30:9 -> 30:17 (count=1) + 30:20 -> 30:25 (count=1) + 30:29 -> 30:34 (count=0) + 30:34 -> 30:35 (count=1) + 30:34 -> 30:34 (count=0) + 33:9 -> 34:16 (count=1) + 35:5 -> 38:6 (count=0) + 38:6 -> 38:7 (count=1) + 41:9 -> 41:16 (count=1) + 42:5 -> 45:6 (count=1) + 47:5 -> 50:6 (count=0) + 52:8 -> 52:16 (count=1) + 52:17 -> 54:6 (count=0) + 54:6 -> 54:7 (count=1) + 56:8 -> 56:15 (count=1) + 56:16 -> 58:6 (count=1) + 58:12 -> 60:6 (count=0) + 61:1 -> 61:2 (count=1) +Segment at 7:9 (count = 1), RegionEntry +Segment at 9:42 (count = 0), Skipped +Segment at 10:8 (count = 1), RegionEntry +Segment at 10:15 (count = 0), Skipped +Segment at 10:16 (count = 1), RegionEntry +Segment at 14:6 (count = 0), RegionEntry +Segment at 14:7 (count = 0), Skipped +Segment at 16:9 (count = 1), RegionEntry +Segment at 16:17 (count = 0), Skipped +Segment at 18:13 (count = 1), RegionEntry +Segment at 18:18 (count = 0), Skipped +Segment at 20:13 (count = 0), RegionEntry +Segment at 20:18 (count = 1) +Segment at 20:19 (count = 0), Skipped +Segment at 23:9 (count = 1), RegionEntry +Segment at 23:17 (count = 0), Skipped +Segment at 25:13 (count = 1), RegionEntry +Segment at 25:18 (count = 0), Skipped +Segment at 27:13 (count = 1), RegionEntry +Segment at 27:18 (count = 1) +Segment at 27:19 (count = 0), Skipped +Segment at 29:9 (count = 1), RegionEntry +Segment at 29:17 (count = 0), Skipped +Segment at 29:20 (count = 1), RegionEntry +Segment at 29:25 (count = 0), Skipped +Segment at 29:29 (count = 1), RegionEntry +Segment at 29:34 (count = 1) +Segment at 29:35 (count = 0), Skipped +Segment at 30:9 (count = 1), RegionEntry +Segment at 30:17 (count = 0), Skipped +Segment at 30:20 (count = 1), RegionEntry +Segment at 30:25 (count = 0), Skipped +Segment at 30:29 (count = 0), RegionEntry +Segment at 30:34 (count = 1) +Segment at 30:35 (count = 0), Skipped +Segment at 33:9 (count = 1), RegionEntry +Segment at 34:16 (count = 0), Skipped +Segment at 35:5 (count = 0), RegionEntry +Segment at 38:6 (count = 1), RegionEntry +Segment at 38:7 (count = 0), Skipped +Segment at 41:9 (count = 1), RegionEntry +Segment at 41:16 (count = 0), Skipped +Segment at 42:5 (count = 1), RegionEntry +Segment at 45:6 (count = 0), Skipped +Segment at 47:5 (count = 0), RegionEntry +Segment at 50:6 (count = 0), Skipped +Segment at 52:8 (count = 1), RegionEntry +Segment at 52:16 (count = 0), Skipped +Segment at 52:17 (count = 0), RegionEntry +Segment at 54:6 (count = 1), RegionEntry +Segment at 54:7 (count = 0), Skipped +Segment at 56:8 (count = 1), RegionEntry +Segment at 56:15 (count = 0), Skipped +Segment at 56:16 (count = 1), RegionEntry +Segment at 58:6 (count = 0), Skipped +Segment at 58:12 (count = 0), RegionEntry +Segment at 60:6 (count = 0), Skipped +Segment at 61:1 (count = 1), RegionEntry +Segment at 61:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.loop_break_value.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.loop_break_value.txt new file mode 100644 index 0000000000000..3f5fc4a440164 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.loop_break_value.txt @@ -0,0 +1,7 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/loop_break_value.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/loop_break_value +Counter in file 0 3:11 -> 13:2, #1 +Emitting segments for file: ../coverage/loop_break_value.rs +Combined regions: + 3:11 -> 13:2 (count=1) +Segment at 3:11 (count = 1), RegionEntry +Segment at 13:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.simple_loop.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.simple_loop.txt new file mode 100644 index 0000000000000..60d861e9d2e15 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.simple_loop.txt @@ -0,0 +1,38 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/simple_loop.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/simple_loop +Counter in file 0 7:9 -> 9:26, #1 +Counter in file 0 12:9 -> 12:16, (#1 + 0) +Counter in file 0 13:5 -> 18:6, #2 +Counter in file 0 18:6 -> 18:7, (#1 - #2) +Counter in file 0 23:13 -> 25:14, ((#2 + (#1 - #2)) + #3) +Counter in file 0 27:13 -> 27:18, #4 +Counter in file 0 30:9 -> 32:10, #3 +Counter in file 0 34:6 -> 34:7, (#2 + (#1 - #2)) +Counter in file 0 35:1 -> 35:2, (#4 + 0) +Emitting segments for file: ../coverage/simple_loop.rs +Combined regions: + 7:9 -> 9:26 (count=1) + 12:9 -> 12:16 (count=1) + 13:5 -> 18:6 (count=1) + 18:6 -> 18:7 (count=0) + 23:13 -> 25:14 (count=11) + 27:13 -> 27:18 (count=1) + 30:9 -> 32:10 (count=10) + 34:6 -> 34:7 (count=1) + 35:1 -> 35:2 (count=1) +Segment at 7:9 (count = 1), RegionEntry +Segment at 9:26 (count = 0), Skipped +Segment at 12:9 (count = 1), RegionEntry +Segment at 12:16 (count = 0), Skipped +Segment at 13:5 (count = 1), RegionEntry +Segment at 18:6 (count = 0), RegionEntry +Segment at 18:7 (count = 0), Skipped +Segment at 23:13 (count = 11), RegionEntry +Segment at 25:14 (count = 0), Skipped +Segment at 27:13 (count = 1), RegionEntry +Segment at 27:18 (count = 0), Skipped +Segment at 30:9 (count = 10), RegionEntry +Segment at 32:10 (count = 0), Skipped +Segment at 34:6 (count = 1), RegionEntry +Segment at 34:7 (count = 0), Skipped +Segment at 35:1 (count = 1), RegionEntry +Segment at 35:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.simple_match.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.simple_match.txt new file mode 100644 index 0000000000000..11b6667df0838 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.simple_match.txt @@ -0,0 +1,60 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/simple_match.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/simple_match +Counter in file 0 7:9 -> 9:26, #1 +Counter in file 0 10:8 -> 10:15, (#1 + 0) +Counter in file 0 10:16 -> 12:6, #2 +Counter in file 0 12:6 -> 12:7, (#1 - #2) +Counter in file 0 15:9 -> 15:10, (((#2 + (#1 - #2)) + (#3 + #4)) - (#6 + #5)) +Counter in file 0 17:9 -> 17:13, ((#2 + (#1 - #2)) + (#3 + #4)) +Counter in file 0 17:13 -> 17:13, #6 +Counter in file 0 22:13 -> 22:22, ((((#2 + (#1 - #2)) + (#3 + #4)) - (#6 + #5)) + 0) +Counter in file 0 24:13 -> 24:14, #3 +Counter in file 0 26:17 -> 28:18, ((((#2 + (#1 - #2)) + (#3 + #4)) - (#6 + #5)) + 0) +Counter in file 0 28:18 -> 28:19, ((((#2 + (#1 - #2)) + (#3 + #4)) - (#6 + #5)) - #3) +Counter in file 0 30:13 -> 37:14, (#3 + 0) +Counter in file 0 40:13 -> 40:15, #4 +Counter in file 0 42:6 -> 42:7, (#2 + (#1 - #2)) +Counter in file 0 42:6 -> 42:7, (#3 + #4) +Counter in file 0 43:1 -> 43:2, #5 +Emitting segments for file: ../coverage/simple_match.rs +Combined regions: + 7:9 -> 9:26 (count=1) + 10:8 -> 10:15 (count=1) + 10:16 -> 12:6 (count=1) + 12:6 -> 12:7 (count=0) + 15:9 -> 15:10 (count=2) + 17:9 -> 17:13 (count=3) + 17:13 -> 17:13 (count=0) + 22:13 -> 22:22 (count=2) + 24:13 -> 24:14 (count=1) + 26:17 -> 28:18 (count=2) + 28:18 -> 28:19 (count=1) + 30:13 -> 37:14 (count=1) + 40:13 -> 40:15 (count=1) + 42:6 -> 42:7 (count=3) + 43:1 -> 43:2 (count=1) +Segment at 7:9 (count = 1), RegionEntry +Segment at 9:26 (count = 0), Skipped +Segment at 10:8 (count = 1), RegionEntry +Segment at 10:15 (count = 0), Skipped +Segment at 10:16 (count = 1), RegionEntry +Segment at 12:6 (count = 0), RegionEntry +Segment at 12:7 (count = 0), Skipped +Segment at 15:9 (count = 2), RegionEntry +Segment at 15:10 (count = 0), Skipped +Segment at 17:9 (count = 3), RegionEntry +Segment at 17:13 (count = 0), Gap +Segment at 22:13 (count = 2), RegionEntry +Segment at 22:22 (count = 0), Skipped +Segment at 24:13 (count = 1), RegionEntry +Segment at 24:14 (count = 0), Skipped +Segment at 26:17 (count = 2), RegionEntry +Segment at 28:18 (count = 1), RegionEntry +Segment at 28:19 (count = 0), Skipped +Segment at 30:13 (count = 1), RegionEntry +Segment at 37:14 (count = 0), Skipped +Segment at 40:13 (count = 1), RegionEntry +Segment at 40:15 (count = 0), Skipped +Segment at 42:6 (count = 3), RegionEntry +Segment at 42:7 (count = 0), Skipped +Segment at 43:1 (count = 1), RegionEntry +Segment at 43:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.try_error_result.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.try_error_result.txt new file mode 100644 index 0000000000000..f0cdf8976ba3b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.try_error_result.txt @@ -0,0 +1,69 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/try_error_result.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/try_error_result +Counter in file 0 13:9 -> 14:23, #1 +Counter in file 0 17:9 -> 17:10, ((#1 + (#2 + #3)) - (#5 + #4)) +Counter in file 0 19:9 -> 19:14, (#1 + (#2 + #3)) +Counter in file 0 19:14 -> 19:14, #5 +Counter in file 0 21:9 -> 25:26, #8 +Counter in file 0 27:13 -> 27:41, #9 +Counter in file 0 27:41 -> 27:42, #6 +Counter in file 0 31:13 -> 31:42, (#8 - #9) +Counter in file 0 31:42 -> 31:43, #7 +Counter in file 0 32:10 -> 32:11, #2 +Counter in file 0 32:10 -> 32:11, #3 +Counter in file 0 33:6 -> 33:7, (#2 + #3) +Counter in file 0 34:5 -> 34:11, #4 +Counter in file 0 35:1 -> 35:2, (#6 + #7) +Counter in file 0 35:1 -> 35:2, ((#6 + #7) + #4) +Counter in file 0 5:8 -> 5:20, #1 +Counter in file 0 6:9 -> 6:16, #2 +Counter in file 0 8:9 -> 8:15, (#1 - #2) +Counter in file 0 10:1 -> 10:2, (#2 + (#1 - #2)) +Emitting segments for file: ../coverage/try_error_result.rs +Combined regions: + 5:8 -> 5:20 (count=6) + 6:9 -> 6:16 (count=1) + 8:9 -> 8:15 (count=5) + 10:1 -> 10:2 (count=6) + 13:9 -> 14:23 (count=1) + 17:9 -> 17:10 (count=6) + 19:9 -> 19:14 (count=6) + 19:14 -> 19:14 (count=0) + 21:9 -> 25:26 (count=6) + 27:13 -> 27:41 (count=1) + 27:41 -> 27:42 (count=1) + 31:13 -> 31:42 (count=5) + 31:42 -> 31:43 (count=0) + 32:10 -> 32:11 (count=5) + 33:6 -> 33:7 (count=5) + 34:5 -> 34:11 (count=0) + 35:1 -> 35:2 (count=2) +Segment at 5:8 (count = 6), RegionEntry +Segment at 5:20 (count = 0), Skipped +Segment at 6:9 (count = 1), RegionEntry +Segment at 6:16 (count = 0), Skipped +Segment at 8:9 (count = 5), RegionEntry +Segment at 8:15 (count = 0), Skipped +Segment at 10:1 (count = 6), RegionEntry +Segment at 10:2 (count = 0), Skipped +Segment at 13:9 (count = 1), RegionEntry +Segment at 14:23 (count = 0), Skipped +Segment at 17:9 (count = 6), RegionEntry +Segment at 17:10 (count = 0), Skipped +Segment at 19:9 (count = 6), RegionEntry +Segment at 19:14 (count = 0), Gap +Segment at 21:9 (count = 6), RegionEntry +Segment at 25:26 (count = 0), Skipped +Segment at 27:13 (count = 1), RegionEntry +Segment at 27:41 (count = 1), RegionEntry +Segment at 27:42 (count = 0), Skipped +Segment at 31:13 (count = 5), RegionEntry +Segment at 31:42 (count = 0), RegionEntry +Segment at 31:43 (count = 0), Skipped +Segment at 32:10 (count = 5), RegionEntry +Segment at 32:11 (count = 0), Skipped +Segment at 33:6 (count = 5), RegionEntry +Segment at 33:7 (count = 0), Skipped +Segment at 34:5 (count = 0), RegionEntry +Segment at 34:11 (count = 0), Skipped +Segment at 35:1 (count = 2), RegionEntry +Segment at 35:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.various_conditions.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.various_conditions.txt new file mode 100644 index 0000000000000..99c6a1a2e9aa2 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.various_conditions.txt @@ -0,0 +1,273 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/various_conditions.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/various_conditions +Counter in file 0 4:9 -> 4:26, #1 +Counter in file 0 5:8 -> 5:12, (#1 + 0) +Counter in file 0 5:13 -> 7:6, #2 +Counter in file 0 10:9 -> 10:10, (#4 + #11) +Counter in file 0 10:16 -> 10:29, (#2 + 0) +Counter in file 0 11:9 -> 12:10, #4 +Counter in file 0 11:23 -> 11:23, #3 +Counter in file 0 13:15 -> 13:28, ((#2 + 0) - #3) +Counter in file 0 14:12 -> 14:25, #5 +Counter in file 0 14:29 -> 14:42, (#5 - #13) +Counter in file 0 14:42 -> 14:43, ((#5 - #13) - #14) +Counter in file 0 14:42 -> 14:43, (#13 + #14) +Counter in file 0 14:42 -> 14:42, #13 +Counter in file 0 14:42 -> 14:42, #14 +Counter in file 0 14:46 -> 14:60, (((#13 + #14) + ((#5 - #13) - #14)) - #17) +Counter in file 0 14:60 -> 14:61, ((((#13 + #14) + ((#5 - #13) - #14)) - #17) - #18) +Counter in file 0 14:60 -> 14:61, (#17 + #18) +Counter in file 0 14:60 -> 14:60, #17 +Counter in file 0 14:60 -> 14:60, #18 +Counter in file 0 14:61 -> 16:10, #21 +Counter in file 0 16:10 -> 16:11, (((#17 + #18) + ((((#13 + #14) + ((#5 - #13) - #14)) - #17) - #18)) - #21) +Counter in file 0 17:9 -> 18:18, #11 +Counter in file 0 20:9 -> 20:15, (((#2 + 0) - #3) - #5) +Counter in file 0 23:9 -> 23:26, ((#4 + #11) + 0) +Counter in file 0 24:8 -> 24:12, ((#4 + #11) + 0) +Counter in file 0 24:13 -> 26:6, #12 +Counter in file 0 28:8 -> 28:21, (#12 + 0) +Counter in file 0 29:9 -> 29:23, #16 +Counter in file 0 29:23 -> 29:23, #15 +Counter in file 0 30:15 -> 30:28, ((#12 + 0) - #15) +Counter in file 0 31:12 -> 31:25, (((#12 + 0) - #15) - #8) +Counter in file 0 31:29 -> 31:42, ((((#12 + 0) - #15) - #8) - #22) +Counter in file 0 31:42 -> 31:43, (#22 + #23) +Counter in file 0 31:42 -> 31:43, (((((#12 + 0) - #15) - #8) - #22) - #23) +Counter in file 0 31:42 -> 31:42, #22 +Counter in file 0 31:42 -> 31:42, #23 +Counter in file 0 31:46 -> 31:60, (((#22 + #23) + (((((#12 + 0) - #15) - #8) - #22) - #23)) - #26) +Counter in file 0 31:60 -> 31:61, (#26 + #27) +Counter in file 0 31:60 -> 31:61, ((((#22 + #23) + (((((#12 + 0) - #15) - #8) - #22) - #23)) - #26) - #27) +Counter in file 0 31:60 -> 31:60, #27 +Counter in file 0 31:60 -> 31:60, #26 +Counter in file 0 31:61 -> 33:10, #30 +Counter in file 0 33:10 -> 33:11, (((#26 + #27) + ((((#22 + #23) + (((((#12 + 0) - #15) - #8) - #22) - #23)) - #26) - #27)) - #30) +Counter in file 0 34:9 -> 34:23, #19 +Counter in file 0 36:9 -> 36:15, #8 +Counter in file 0 39:9 -> 39:26, (#16 + #19) +Counter in file 0 40:8 -> 40:12, ((#16 + #19) + 0) +Counter in file 0 40:13 -> 42:6, #20 +Counter in file 0 44:9 -> 44:10, (#25 + #28) +Counter in file 0 44:16 -> 44:29, (#20 + 0) +Counter in file 0 45:9 -> 45:23, #25 +Counter in file 0 45:23 -> 45:23, #24 +Counter in file 0 46:15 -> 46:28, ((#20 + 0) - #24) +Counter in file 0 47:12 -> 47:25, (((#20 + 0) - #24) - #7) +Counter in file 0 47:29 -> 47:42, ((((#20 + 0) - #24) - #7) - #31) +Counter in file 0 47:42 -> 47:43, (((((#20 + 0) - #24) - #7) - #31) - #32) +Counter in file 0 47:42 -> 47:43, (#31 + #32) +Counter in file 0 47:42 -> 47:42, #32 +Counter in file 0 47:42 -> 47:42, #31 +Counter in file 0 47:46 -> 47:60, (((#31 + #32) + (((((#20 + 0) - #24) - #7) - #31) - #32)) - #33) +Counter in file 0 47:60 -> 47:61, (#33 + #34) +Counter in file 0 47:60 -> 47:61, ((((#31 + #32) + (((((#20 + 0) - #24) - #7) - #31) - #32)) - #33) - #34) +Counter in file 0 47:60 -> 47:60, #34 +Counter in file 0 47:60 -> 47:60, #33 +Counter in file 0 47:61 -> 49:10, #37 +Counter in file 0 49:10 -> 49:11, (((#33 + #34) + ((((#31 + #32) + (((((#20 + 0) - #24) - #7) - #31) - #32)) - #33) - #34)) - #37) +Counter in file 0 50:9 -> 50:23, #28 +Counter in file 0 52:13 -> 54:15, #7 +Counter in file 0 57:9 -> 57:10, (#9 + #10) +Counter in file 0 57:16 -> 57:29, ((#25 + #28) + 0) +Counter in file 0 58:9 -> 58:23, #9 +Counter in file 0 58:23 -> 58:23, #29 +Counter in file 0 59:15 -> 59:28, ((#25 + #28) - #29) +Counter in file 0 60:12 -> 60:25, (((#25 + #28) - #29) - #6) +Counter in file 0 60:29 -> 60:42, ((((#25 + #28) - #29) - #6) - #35) +Counter in file 0 60:42 -> 60:43, (#35 + #36) +Counter in file 0 60:42 -> 60:43, (((((#25 + #28) - #29) - #6) - #35) - #36) +Counter in file 0 60:42 -> 60:42, #35 +Counter in file 0 60:42 -> 60:42, #36 +Counter in file 0 60:46 -> 60:60, (((#35 + #36) + (((((#25 + #28) - #29) - #6) - #35) - #36)) - #38) +Counter in file 0 60:60 -> 60:61, (#38 + #39) +Counter in file 0 60:60 -> 60:61, ((((#35 + #36) + (((((#25 + #28) - #29) - #6) - #35) - #36)) - #38) - #39) +Counter in file 0 60:60 -> 60:60, #38 +Counter in file 0 60:60 -> 60:60, #39 +Counter in file 0 60:61 -> 62:10, #40 +Counter in file 0 62:10 -> 62:11, (((#38 + #39) + ((((#35 + #36) + (((((#25 + #28) - #29) - #6) - #35) - #36)) - #38) - #39)) - #40) +Counter in file 0 63:9 -> 63:23, #10 +Counter in file 0 65:9 -> 65:15, #6 +Counter in file 0 67:1 -> 67:2, ((#9 + #10) + (((#6 + #7) + #8) + (((#2 + 0) - #3) - #5))) +Counter in file 0 67:1 -> 67:2, (((#6 + #7) + #8) + (((#2 + 0) - #3) - #5)) +Emitting segments for file: ../coverage/various_conditions.rs +Combined regions: + 4:9 -> 4:26 (count=1) + 5:8 -> 5:12 (count=1) + 5:13 -> 7:6 (count=1) + 10:9 -> 10:10 (count=1) + 10:16 -> 10:29 (count=1) + 11:9 -> 12:10 (count=1) + 11:23 -> 11:23 (count=1) + 13:15 -> 13:28 (count=0) + 14:12 -> 14:25 (count=0) + 14:29 -> 14:42 (count=0) + 14:42 -> 14:43 (count=0) + 14:42 -> 14:42 (count=0) + 14:46 -> 14:60 (count=0) + 14:60 -> 14:61 (count=0) + 14:60 -> 14:60 (count=0) + 14:61 -> 16:10 (count=0) + 16:10 -> 16:11 (count=0) + 17:9 -> 18:18 (count=0) + 20:9 -> 20:15 (count=0) + 23:9 -> 23:26 (count=1) + 24:8 -> 24:12 (count=1) + 24:13 -> 26:6 (count=1) + 28:8 -> 28:21 (count=1) + 29:9 -> 29:23 (count=1) + 29:23 -> 29:23 (count=1) + 30:15 -> 30:28 (count=0) + 31:12 -> 31:25 (count=0) + 31:29 -> 31:42 (count=0) + 31:42 -> 31:43 (count=0) + 31:42 -> 31:42 (count=0) + 31:46 -> 31:60 (count=0) + 31:60 -> 31:61 (count=0) + 31:60 -> 31:60 (count=0) + 31:61 -> 33:10 (count=0) + 33:10 -> 33:11 (count=0) + 34:9 -> 34:23 (count=0) + 36:9 -> 36:15 (count=0) + 39:9 -> 39:26 (count=1) + 40:8 -> 40:12 (count=1) + 40:13 -> 42:6 (count=1) + 44:9 -> 44:10 (count=0) + 44:16 -> 44:29 (count=1) + 45:9 -> 45:23 (count=0) + 45:23 -> 45:23 (count=0) + 46:15 -> 46:28 (count=1) + 47:12 -> 47:25 (count=0) + 47:29 -> 47:42 (count=0) + 47:42 -> 47:43 (count=0) + 47:42 -> 47:42 (count=0) + 47:46 -> 47:60 (count=0) + 47:60 -> 47:61 (count=0) + 47:60 -> 47:60 (count=0) + 47:61 -> 49:10 (count=0) + 49:10 -> 49:11 (count=0) + 50:9 -> 50:23 (count=0) + 52:13 -> 54:15 (count=1) + 57:9 -> 57:10 (count=0) + 57:16 -> 57:29 (count=0) + 58:9 -> 58:23 (count=0) + 58:23 -> 58:23 (count=0) + 59:15 -> 59:28 (count=0) + 60:12 -> 60:25 (count=0) + 60:29 -> 60:42 (count=0) + 60:42 -> 60:43 (count=0) + 60:42 -> 60:42 (count=0) + 60:46 -> 60:60 (count=0) + 60:60 -> 60:61 (count=0) + 60:60 -> 60:60 (count=0) + 60:61 -> 62:10 (count=0) + 62:10 -> 62:11 (count=0) + 63:9 -> 63:23 (count=0) + 65:9 -> 65:15 (count=0) + 67:1 -> 67:2 (count=2) +Segment at 4:9 (count = 1), RegionEntry +Segment at 4:26 (count = 0), Skipped +Segment at 5:8 (count = 1), RegionEntry +Segment at 5:12 (count = 0), Skipped +Segment at 5:13 (count = 1), RegionEntry +Segment at 7:6 (count = 0), Skipped +Segment at 10:9 (count = 1), RegionEntry +Segment at 10:10 (count = 0), Skipped +Segment at 10:16 (count = 1), RegionEntry +Segment at 10:29 (count = 0), Skipped +Segment at 11:9 (count = 1), RegionEntry +Segment at 11:23 (count = 1) +Segment at 12:10 (count = 0), Skipped +Segment at 13:15 (count = 0), RegionEntry +Segment at 13:28 (count = 0), Skipped +Segment at 14:12 (count = 0), RegionEntry +Segment at 14:25 (count = 0), Skipped +Segment at 14:29 (count = 0), RegionEntry +Segment at 14:42 (count = 0) +Segment at 14:43 (count = 0), Skipped +Segment at 14:46 (count = 0), RegionEntry +Segment at 14:60 (count = 0) +Segment at 14:61 (count = 0), RegionEntry +Segment at 16:10 (count = 0), RegionEntry +Segment at 16:11 (count = 0), Skipped +Segment at 17:9 (count = 0), RegionEntry +Segment at 18:18 (count = 0), Skipped +Segment at 20:9 (count = 0), RegionEntry +Segment at 20:15 (count = 0), Skipped +Segment at 23:9 (count = 1), RegionEntry +Segment at 23:26 (count = 0), Skipped +Segment at 24:8 (count = 1), RegionEntry +Segment at 24:12 (count = 0), Skipped +Segment at 24:13 (count = 1), RegionEntry +Segment at 26:6 (count = 0), Skipped +Segment at 28:8 (count = 1), RegionEntry +Segment at 28:21 (count = 0), Skipped +Segment at 29:9 (count = 1), RegionEntry +Segment at 29:23 (count = 1), Gap +Segment at 30:15 (count = 0), RegionEntry +Segment at 30:28 (count = 0), Skipped +Segment at 31:12 (count = 0), RegionEntry +Segment at 31:25 (count = 0), Skipped +Segment at 31:29 (count = 0), RegionEntry +Segment at 31:42 (count = 0) +Segment at 31:43 (count = 0), Skipped +Segment at 31:46 (count = 0), RegionEntry +Segment at 31:60 (count = 0) +Segment at 31:61 (count = 0), RegionEntry +Segment at 33:10 (count = 0), RegionEntry +Segment at 33:11 (count = 0), Skipped +Segment at 34:9 (count = 0), RegionEntry +Segment at 34:23 (count = 0), Skipped +Segment at 36:9 (count = 0), RegionEntry +Segment at 36:15 (count = 0), Skipped +Segment at 39:9 (count = 1), RegionEntry +Segment at 39:26 (count = 0), Skipped +Segment at 40:8 (count = 1), RegionEntry +Segment at 40:12 (count = 0), Skipped +Segment at 40:13 (count = 1), RegionEntry +Segment at 42:6 (count = 0), Skipped +Segment at 44:9 (count = 0), RegionEntry +Segment at 44:10 (count = 0), Skipped +Segment at 44:16 (count = 1), RegionEntry +Segment at 44:29 (count = 0), Skipped +Segment at 45:9 (count = 0), RegionEntry +Segment at 45:23 (count = 0), Gap +Segment at 46:15 (count = 1), RegionEntry +Segment at 46:28 (count = 0), Skipped +Segment at 47:12 (count = 0), RegionEntry +Segment at 47:25 (count = 0), Skipped +Segment at 47:29 (count = 0), RegionEntry +Segment at 47:42 (count = 0) +Segment at 47:43 (count = 0), Skipped +Segment at 47:46 (count = 0), RegionEntry +Segment at 47:60 (count = 0) +Segment at 47:61 (count = 0), RegionEntry +Segment at 49:10 (count = 0), RegionEntry +Segment at 49:11 (count = 0), Skipped +Segment at 50:9 (count = 0), RegionEntry +Segment at 50:23 (count = 0), Skipped +Segment at 52:13 (count = 1), RegionEntry +Segment at 54:15 (count = 0), Skipped +Segment at 57:9 (count = 0), RegionEntry +Segment at 57:10 (count = 0), Skipped +Segment at 57:16 (count = 0), RegionEntry +Segment at 57:29 (count = 0), Skipped +Segment at 58:9 (count = 0), RegionEntry +Segment at 58:23 (count = 0), Gap +Segment at 59:15 (count = 0), RegionEntry +Segment at 59:28 (count = 0), Skipped +Segment at 60:12 (count = 0), RegionEntry +Segment at 60:25 (count = 0), Skipped +Segment at 60:29 (count = 0), RegionEntry +Segment at 60:42 (count = 0) +Segment at 60:43 (count = 0), Skipped +Segment at 60:46 (count = 0), RegionEntry +Segment at 60:60 (count = 0) +Segment at 60:61 (count = 0), RegionEntry +Segment at 62:10 (count = 0), RegionEntry +Segment at 62:11 (count = 0), Skipped +Segment at 63:9 (count = 0), RegionEntry +Segment at 63:23 (count = 0), Skipped +Segment at 65:9 (count = 0), RegionEntry +Segment at 65:15 (count = 0), Skipped +Segment at 67:1 (count = 2), RegionEntry +Segment at 67:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while_early_return.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while_early_return.txt new file mode 100644 index 0000000000000..42f1dbdba75ff --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while_early_return.txt @@ -0,0 +1,40 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/while_early_return.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/while_early_return +Counter in file 0 5:9 -> 5:27, #1 +Counter in file 0 7:9 -> 9:10, (#1 + #2) +Counter in file 0 12:13 -> 14:14, ((#1 + #2) - #3) +Counter in file 0 18:21 -> 20:22, #6 +Counter in file 0 22:21 -> 22:27, #4 +Counter in file 0 26:21 -> 26:27, #5 +Counter in file 0 30:9 -> 32:10, #2 +Counter in file 0 35:5 -> 35:11, #3 +Counter in file 0 36:1 -> 36:2, (#4 + #5) +Counter in file 0 36:1 -> 36:2, ((#4 + #5) + #3) +Emitting segments for file: ../coverage/while_early_return.rs +Combined regions: + 5:9 -> 5:27 (count=1) + 7:9 -> 9:10 (count=7) + 12:13 -> 14:14 (count=7) + 18:21 -> 20:22 (count=1) + 22:21 -> 22:27 (count=0) + 26:21 -> 26:27 (count=1) + 30:9 -> 32:10 (count=6) + 35:5 -> 35:11 (count=0) + 36:1 -> 36:2 (count=2) +Segment at 5:9 (count = 1), RegionEntry +Segment at 5:27 (count = 0), Skipped +Segment at 7:9 (count = 7), RegionEntry +Segment at 9:10 (count = 0), Skipped +Segment at 12:13 (count = 7), RegionEntry +Segment at 14:14 (count = 0), Skipped +Segment at 18:21 (count = 1), RegionEntry +Segment at 20:22 (count = 0), Skipped +Segment at 22:21 (count = 0), RegionEntry +Segment at 22:27 (count = 0), Skipped +Segment at 26:21 (count = 1), RegionEntry +Segment at 26:27 (count = 0), Skipped +Segment at 30:9 (count = 6), RegionEntry +Segment at 32:10 (count = 0), Skipped +Segment at 35:5 (count = 0), RegionEntry +Segment at 35:11 (count = 0), Skipped +Segment at 36:1 (count = 2), RegionEntry +Segment at 36:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html index 43f75c574d0ee..0f076a93c0977 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html @@ -65,16 +65,19 @@ 35:13-35:26: @0[2]: FakeRead(ForLet, _2)">@0⦊mut countdown = 0⦉@0
;
if @0⦊is_false⦉@0 @1,3⦊{ +36:21-38:10: @3[1]: _3 = const () +38:10-38:10: @3.Goto: goto -> bb4">@1,3⦊{
countdown = 10; +36:21-38:10: @3[1]: _3 = const () +38:10-38:10: @3.Goto: goto -> bb4"> countdown = 10; }⦉@1,3 - }⦉@1,3@2⦊‸⦉@2 + @4,5⦊"alt string 2".to_owned() - }⦉@4,5
diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html index 8f07ec5fcde66..bc78a604e311e 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html @@ -65,16 +65,19 @@ 77:13-77:26: @0[2]: FakeRead(ForLet, _2)">@0⦊mut countdown = 0⦉@0
;
if @0⦊is_false⦉@0 @1,3⦊{ +78:21-80:10: @3[1]: _3 = const () +80:10-80:10: @3.Goto: goto -> bb4">@1,3⦊{
countdown = 10; +78:21-80:10: @3[1]: _3 = const () +80:10-80:10: @3.Goto: goto -> bb4"> countdown = 10;
}⦉@1,3
- }⦉@1,3@2⦊‸⦉@2 + @4,5⦊"alt string 4".to_owned() - }⦉@4,5 diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html index ca9031a1094a4..b0db2311730b0 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html @@ -65,16 +65,19 @@ 20:21-20:34: @0[2]: FakeRead(ForLet, _2)">@0⦊mut countdown = 0⦉@0
;
if @0⦊is_false⦉@0 @1,3⦊{ +21:29-23:18: @3[1]: _3 = const () +23:18-23:18: @3.Goto: goto -> bb4">@1,3⦊{
countdown = 10; +21:29-23:18: @3[1]: _3 = const () +23:18-23:18: @3.Goto: goto -> bb4"> countdown = 10; }⦉@1,3 - }⦉@1,3@2⦊‸⦉@2 + @4,5⦊"alt string 1".to_owned() - }⦉@4,5 diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html index 820f8d9c6cf8f..ca07a8d3ce5b8 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html @@ -65,16 +65,19 @@ 62:21-62:34: @0[2]: FakeRead(ForLet, _2)">@0⦊mut countdown = 0⦉@0; if @0⦊is_false⦉@0 @1,3⦊{ +63:29-65:18: @3[1]: _3 = const () +65:18-65:18: @3.Goto: goto -> bb4">@1,3⦊{ countdown = 10; +63:29-65:18: @3[1]: _3 = const () +65:18-65:18: @3.Goto: goto -> bb4"> countdown = 10; }⦉@1,3 - }⦉@1,3@2⦊‸⦉@2 + @4,5⦊"alt string 3".to_owned() - }⦉@4,5 diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.if/if.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.if/if.main.-------.InstrumentCoverage.0.html index 0379d900e9409..0d4b940214e39 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.if/if.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.if/if.main.-------.InstrumentCoverage.0.html @@ -146,17 +146,23 @@ @0,1,2,3⦊is_true⦉@0,1,2,3 @4,6⦊{ +22:5-27:6: @6[1]: _0 = const () +27:6-27:6: @6.Goto: goto -> bb7">@4,6⦊{ countdown +22:5-27:6: @6[1]: _0 = const () +27:6-27:6: @6.Goto: goto -> bb7"> countdown = +22:5-27:6: @6[1]: _0 = const () +27:6-27:6: @6.Goto: goto -> bb7"> = 10 +22:5-27:6: @6[1]: _0 = const () +27:6-27:6: @6.Goto: goto -> bb7"> 10 ; +22:5-27:6: @6[1]: _0 = const () +27:6-27:6: @6.Goto: goto -> bb7"> ; }⦉@4,6 -}@7⦊‸⦉@7 +22:5-27:6: @6[1]: _0 = const () +27:6-27:6: @6.Goto: goto -> bb7"> }⦉@4,6@5⦊‸⦉@5 +}@7⦊‸⦉@7 diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main.-------.InstrumentCoverage.0.html index 56557b8ef955e..ec3517ec9eddd 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main.-------.InstrumentCoverage.0.html @@ -86,11 +86,14 @@ 9:9-9:22: @3[3]: FakeRead(ForLet, _5)"> let mut countdown = 0⦉@0,1,2,3; if @0,1,2,3⦊is_true⦉@0,1,2,3 @4,6⦊{ +10:16-12:6: @6[1]: _6 = const () +12:6-12:6: @6.Goto: goto -> bb7">@4,6⦊{ countdown = 10; +10:16-12:6: @6[1]: _6 = const () +12:6-12:6: @6.Goto: goto -> bb7"> countdown = 10; }⦉@4,6 +10:16-12:6: @6[1]: _6 = const () +12:6-12:6: @6.Goto: goto -> bb7"> }⦉@4,6@5⦊‸⦉@5 mod in_mod { const IN_MOD_CONST: u32 = 1000; @@ -126,16 +129,19 @@ type InType = String; - if @7⦊is_true⦉@7 if @7⦊is_true⦉@7 @8,10,11⦊{ -@8,10,11⦊{ + in_func(countdown); - in_func(countdown); + }⦉@8,10,11 +48:16-50:6: @11[2]: _8 = const () +50:6-50:6: @11.Goto: goto -> bb12"> }⦉@8,10,11@9⦊‸⦉@9 let // Initialize test constants in a way that cannot be determined at compile time, to ensure // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from // dependent conditions. - let let @0,1,2,3⦊is_true = std::env::args().len() == 1; - -@0,1,2,3⦊is_true⦉@0,1,2,3 @4,6⦊{ +10:16-14:6: @6[3]: _9 = const () +14:6-14:6: @6.Goto: goto -> bb7">@4,6⦊{ a = 1; +10:16-14:6: @6[3]: _9 = const () +14:6-14:6: @6.Goto: goto -> bb7"> a = 1; b = 10; +10:16-14:6: @6[3]: _9 = const () +14:6-14:6: @6.Goto: goto -> bb7"> b = 10; c = 100; +10:16-14:6: @6[3]: _9 = const () +14:6-14:6: @6.Goto: goto -> bb7"> c = 100; }⦉@4,6 +10:16-14:6: @6[3]: _9 = const () +14:6-14:6: @6.Goto: goto -> bb7"> }⦉@4,6@5⦊‸⦉@5 let - @11⦊somebool⦉@11 + @11⦊somebool⦉@11 = - @7⦊a < b⦉@7 || - @10⦊b < c⦉@10 +20:13-20:18: @10[5]: _15 = Lt(move _16, move _17)">@10⦊b < c⦉@10@8⦊‸⦉@8@9⦊‸⦉@9 ; let - @15⦊somebool⦉@15 + @15⦊somebool⦉@15 = - @11⦊b < a⦉@11 || - @14⦊b < c⦉@14 +27:13-27:18: @14[5]: _22 = Lt(move _23, move _24)">@14⦊b < c⦉@14@12⦊‸⦉@12@13⦊‸⦉@13 ; - let - @19⦊somebool⦉@19 - = - @15⦊a < b⦉@15 - && - @18⦊b < c⦉@18 - ; - let - @23⦊somebool⦉@23 - = - @19⦊b < a⦉@19 - && - @22⦊b < c⦉@22 - ; -}@23⦊‸⦉@23 + let @19⦊somebool⦉@19 = @15⦊a < b⦉@15 && @18⦊b < c⦉@18@16⦊‸⦉@16@17⦊‸⦉@17; + let @23⦊somebool⦉@23 = @19⦊b < a⦉@19 && @22⦊b < c⦉@22@20⦊‸⦉@20@21⦊‸⦉@21; + + if + @23⦊! + is_true⦉@23 + @24,26⦊{ + a = 2 + ; + }⦉@24,26@25⦊‸⦉@25 + + if + @27⦊is_true⦉@27 + @28,30⦊{ + b = 30 + ; + }⦉@28,30 + else + @29⦊{ + c = 400 + ; + }⦉@29 + + if @31⦊!is_true⦉@31 @32,34⦊{ + a = 2; + }⦉@32,34@33⦊‸⦉@33 + + if @35⦊is_true⦉@35 @36,38⦊{ + b = 30; + }⦉@36,38 else @37⦊{ + c = 400; + }⦉@37 +}@39⦊‸⦉@39 diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html index 7dd22ee6e179f..618f84513e908 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html @@ -89,39 +89,45 @@ @0,1,2,3⦊is_true⦉@0,1,2,3 @4,6⦊{ +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7">@4,6⦊{ countdown +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7"> countdown = +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7"> = 10 +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7"> 10 ; +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7"> ; }⦉@4,6 +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7"> }⦉@4,6@5⦊‸⦉@5 loop { if - @8,9⦊countdown - == - 0⦉@8,9 { - @10,12⦊break⦉@10,12 + @10,12⦊break⦉@10,12 ; } - @13⦊countdown - -= - 1⦉@13 + @13⦊countdown + -= + 1⦉@13 ; - } + }@7⦊‸⦉@7 }@10,12⦊‸⦉@10,12 diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html index abcf30e93c04e..66885d0612bfc 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html @@ -86,19 +86,22 @@ 9:9-9:22: @3[3]: FakeRead(ForLet, _5)"> let mut countdown = 1⦉@0,1,2,3; if @0,1,2,3⦊is_true⦉@0,1,2,3 @4,6⦊{ +10:16-12:6: @6[1]: _6 = const () +12:6-12:6: @6.Goto: goto -> bb7">@4,6⦊{ countdown = 0; +10:16-12:6: @6[1]: _6 = const () +12:6-12:6: @6.Goto: goto -> bb7"> countdown = 0; }⦉@4,6 +10:16-12:6: @6[1]: _6 = const () +12:6-12:6: @6.Goto: goto -> bb7"> }⦉@4,6@5⦊‸⦉@5 for - @13,15,17⦊_⦉@13,15,17 in - @9,10,11⦊0..2⦉@9,10,11 @@ -106,16 +109,16 @@ let z ; match - @13,15,17⦊countdown⦉@13,15,17 + @13,15,17⦊countdown⦉@13,15,17 { - @18⦊x⦉@18 + @18⦊x⦉@18 if - @13,15,17⦊x - < - 1⦉@13,15,17 + 1⦉@13,15,17@19⦊‸⦉@19 => => @16⦊{}⦉@16 } - } + }@7,8⦊‸⦉@7,8@20⦊‸⦉@20 }@12⦊‸⦉@12 diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html index cb3017b0a108d..cc0e7abb1f3ad 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html @@ -108,10 +108,10 @@ 31:42-31:43: @26[4]: _36 = _33 31:42-31:43: @26.Call: _35 = <() as From<()>>::from(move _36) -> [return: bb27, unwind: bb32] 31:42-31:43: @27.Call: _0 = <std::result::Result<(), ()> as Try>::from_error(move _35) -> [return: bb28, unwind: bb32]">@24,26,27,28⦊?⦉@24,26,27,28; - } - } - }@15⦊‸⦉@15@23⦊‸⦉@23 + }@29⦊‸⦉@29 + @5⦊Ok(())⦉@5 -}@31⦊‸⦉@31 +}@30⦊‸⦉@30@31⦊‸⦉@31 diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.various_conditions/various_conditions.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.various_conditions/various_conditions.main.-------.InstrumentCoverage.0.html index a0daddb2e88d4..b9f6f4d7832b6 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.various_conditions/various_conditions.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.various_conditions/various_conditions.main.-------.InstrumentCoverage.0.html @@ -64,32 +64,38 @@ 4:9-4:22: @0[2]: FakeRead(ForLet, _1)">@0⦊mut countdown = 0⦉@0; if @0⦊true⦉@0 @1,3⦊{ +5:13-7:6: @3[1]: _2 = const () +7:6-7:6: @3.Goto: goto -> bb4">@1,3⦊{ countdown = 10; +5:13-7:6: @3[1]: _2 = const () +7:6-7:6: @3.Goto: goto -> bb4"> countdown = 10; }⦉@1,3 +5:13-7:6: @3[1]: _2 = const () +7:6-7:6: @3.Goto: goto -> bb4"> }⦉@1,3@2⦊‸⦉@2 const B: u32 = 100; - let @25⦊x⦉@25 = if let @25⦊x⦉@25 = if @4⦊countdown > 7⦉@4 { - @8⦊countdown -= 4; - B⦉@8 - } else if } else if @6⦊countdown > 2⦉@6 { - if @9,11⦊countdown < 1⦉@9,11 || @18⦊countdown > 5⦉@18 || @14⦊countdown != 9⦉@14 @20,22⦊{ - countdown = 0; - }⦉@20,22 + if @9,11⦊countdown < 1⦉@9,11 || @18⦊countdown > 5⦉@18@16⦊‸⦉@16@17⦊‸⦉@17 || @14⦊countdown != 9⦉@14@12⦊‸⦉@12@13⦊‸⦉@13 @20,22⦊{ + countdown = 0; + }⦉@20,22@21⦊‸⦉@21 @24⦊countdown -= 5; @25⦊mut countdown = 0⦉@25; if @25⦊true⦉@25 @26,28⦊{ +24:13-26:6: @28[1]: _22 = const () +26:6-26:6: @28.Goto: goto -> bb29">@26,28⦊{ countdown = 10; +24:13-26:6: @28[1]: _22 = const () +26:6-26:6: @28.Goto: goto -> bb29"> countdown = 10; }⦉@26,28 +24:13-26:6: @28[1]: _22 = const () +26:6-26:6: @28.Goto: goto -> bb29"> }⦉@26,28@27⦊‸⦉@27 - if if @29⦊countdown > 7⦉@29 { - @33⦊countdown -= 4⦉@33; - } else if @33⦊countdown -= 4⦉@33; + } else if @31⦊countdown > 2⦉@31 { - if @34,36⦊countdown < 1⦉@34,36 || @43⦊countdown > 5⦉@43 || @39⦊countdown != 9⦉@39 @45,47⦊{ - countdown = 0; - }⦉@45,47 + if @34,36⦊countdown < 1⦉@34,36 || @43⦊countdown > 5⦉@43@41⦊‸⦉@41@42⦊‸⦉@42 || @39⦊countdown != 9⦉@39@37⦊‸⦉@37@38⦊‸⦉@38 @45,47⦊{ + countdown = 0; + }⦉@45,47@46⦊‸⦉@46 @49⦊countdown -= 5⦉@49; } else { @35⦊return⦉@35; @@ -133,28 +145,34 @@ 39:9-39:22: @50[4]: FakeRead(ForLet, _41)">@50⦊mut countdown = 0⦉@50; if @50⦊true⦉@50 @51,53⦊{ +40:13-42:6: @53[1]: _42 = const () +42:6-42:6: @53.Goto: goto -> bb54">@51,53⦊{ countdown = 1; +40:13-42:6: @53[1]: _42 = const () +42:6-42:6: @53.Goto: goto -> bb54"> countdown = 1; }⦉@51,53 +40:13-42:6: @53[1]: _42 = const () +42:6-42:6: @53.Goto: goto -> bb54"> }⦉@51,53@52⦊‸⦉@52 - let @77⦊z⦉@77 = if let @77⦊z⦉@77 = if @54⦊countdown > 7⦉@54 { - @58⦊countdown -= 4⦉@58; - } else if @58⦊countdown -= 4⦉@58; + } else if @56⦊countdown > 2⦉@56 { - if @59,61⦊countdown < 1⦉@59,61 || @68⦊countdown > 5⦉@68 || @64⦊countdown != 9⦉@64 @70,72⦊{ - countdown = 0; - }⦉@70,72 + if @59,61⦊countdown < 1⦉@59,61 || @68⦊countdown > 5⦉@68@66⦊‸⦉@66@67⦊‸⦉@67 || @64⦊countdown != 9⦉@64@62⦊‸⦉@62@63⦊‸⦉@63 @70,72⦊{ + countdown = 0; + }⦉@70,72@71⦊‸⦉@71 @74⦊countdown -= 5⦉@74; } else { let @79⦊countdown > 2⦉@79 { if @82,84⦊countdown < 1⦉@82,84 || @91⦊countdown > 5⦉@91 || @87⦊countdown != 9⦉@87 @93,95⦊{ +60:29-60:42: @91[3]: _85 = Gt(move _86, const 5_i32)">@91⦊countdown > 5⦉@91@89⦊‸⦉@89@90⦊‸⦉@90 || @87⦊countdown != 9⦉@87@85⦊‸⦉@85@86⦊‸⦉@86 @93,95⦊{ countdown = 0; +60:61-62:10: @95[1]: _80 = const () +62:10-62:10: @95.Goto: goto -> bb96"> countdown = 0; }⦉@93,95 - @97⦊countdown -= 5⦉@97; +60:61-62:10: @95[1]: _80 = const () +62:10-62:10: @95.Goto: goto -> bb96"> }⦉@93,95@94⦊‸⦉@94 + @97⦊countdown -= 5⦉@97; } else { - @83⦊return⦉@83; + @83⦊return⦉@83; }; -}@102⦊‸⦉@102 +}@101⦊‸⦉@101@102⦊‸⦉@102 diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html index 7053319a817c7..3d75e61b80055 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html @@ -112,6 +112,6 @@ } @4⦊Ok(())⦉@4 -}@14⦊‸⦉@14 +}@13⦊‸⦉@13@14⦊‸⦉@14 diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html index 43f75c574d0ee..0f076a93c0977 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html @@ -65,16 +65,19 @@ 35:13-35:26: @0[2]: FakeRead(ForLet, _2)">@0⦊mut countdown = 0⦉@0; if @0⦊is_false⦉@0 @1,3⦊{ +36:21-38:10: @3[1]: _3 = const () +38:10-38:10: @3.Goto: goto -> bb4">@1,3⦊{ countdown = 10; +36:21-38:10: @3[1]: _3 = const () +38:10-38:10: @3.Goto: goto -> bb4"> countdown = 10; }⦉@1,3 - }⦉@1,3@2⦊‸⦉@2 + @4,5⦊"alt string 2".to_owned() - }⦉@4,5 diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html index 8f07ec5fcde66..bc78a604e311e 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html @@ -65,16 +65,19 @@ 77:13-77:26: @0[2]: FakeRead(ForLet, _2)">@0⦊mut countdown = 0⦉@0; if @0⦊is_false⦉@0 @1,3⦊{ +78:21-80:10: @3[1]: _3 = const () +80:10-80:10: @3.Goto: goto -> bb4">@1,3⦊{ countdown = 10; +78:21-80:10: @3[1]: _3 = const () +80:10-80:10: @3.Goto: goto -> bb4"> countdown = 10; }⦉@1,3 - }⦉@1,3@2⦊‸⦉@2 + @4,5⦊"alt string 4".to_owned() - }⦉@4,5 diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html index ca9031a1094a4..b0db2311730b0 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html @@ -65,16 +65,19 @@ 20:21-20:34: @0[2]: FakeRead(ForLet, _2)">@0⦊mut countdown = 0⦉@0; if @0⦊is_false⦉@0 @1,3⦊{ +21:29-23:18: @3[1]: _3 = const () +23:18-23:18: @3.Goto: goto -> bb4">@1,3⦊{ countdown = 10; +21:29-23:18: @3[1]: _3 = const () +23:18-23:18: @3.Goto: goto -> bb4"> countdown = 10; }⦉@1,3 - }⦉@1,3@2⦊‸⦉@2 + @4,5⦊"alt string 1".to_owned() - }⦉@4,5 diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html index 820f8d9c6cf8f..ca07a8d3ce5b8 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html @@ -65,16 +65,19 @@ 62:21-62:34: @0[2]: FakeRead(ForLet, _2)">@0⦊mut countdown = 0⦉@0; if @0⦊is_false⦉@0 @1,3⦊{ +63:29-65:18: @3[1]: _3 = const () +65:18-65:18: @3.Goto: goto -> bb4">@1,3⦊{ countdown = 10; +63:29-65:18: @3[1]: _3 = const () +65:18-65:18: @3.Goto: goto -> bb4"> countdown = 10; }⦉@1,3 - }⦉@1,3@2⦊‸⦉@2 + @4,5⦊"alt string 3".to_owned() - }⦉@4,5 diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.if/if.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.if/if.main.-------.InstrumentCoverage.0.html index 0379d900e9409..0d4b940214e39 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.if/if.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.if/if.main.-------.InstrumentCoverage.0.html @@ -146,17 +146,23 @@ @0,1,2,3⦊is_true⦉@0,1,2,3 @4,6⦊{ +22:5-27:6: @6[1]: _0 = const () +27:6-27:6: @6.Goto: goto -> bb7">@4,6⦊{ countdown +22:5-27:6: @6[1]: _0 = const () +27:6-27:6: @6.Goto: goto -> bb7"> countdown = +22:5-27:6: @6[1]: _0 = const () +27:6-27:6: @6.Goto: goto -> bb7"> = 10 +22:5-27:6: @6[1]: _0 = const () +27:6-27:6: @6.Goto: goto -> bb7"> 10 ; +22:5-27:6: @6[1]: _0 = const () +27:6-27:6: @6.Goto: goto -> bb7"> ; }⦉@4,6 -}@7⦊‸⦉@7 +22:5-27:6: @6[1]: _0 = const () +27:6-27:6: @6.Goto: goto -> bb7"> }⦉@4,6@5⦊‸⦉@5 +}@7⦊‸⦉@7 diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.inner_items/inner_items.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.inner_items/inner_items.main.-------.InstrumentCoverage.0.html index 56557b8ef955e..ec3517ec9eddd 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.inner_items/inner_items.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.inner_items/inner_items.main.-------.InstrumentCoverage.0.html @@ -86,11 +86,14 @@ 9:9-9:22: @3[3]: FakeRead(ForLet, _5)"> let mut countdown = 0⦉@0,1,2,3; if @0,1,2,3⦊is_true⦉@0,1,2,3 @4,6⦊{ +10:16-12:6: @6[1]: _6 = const () +12:6-12:6: @6.Goto: goto -> bb7">@4,6⦊{ countdown = 10; +10:16-12:6: @6[1]: _6 = const () +12:6-12:6: @6.Goto: goto -> bb7"> countdown = 10; }⦉@4,6 +10:16-12:6: @6[1]: _6 = const () +12:6-12:6: @6.Goto: goto -> bb7"> }⦉@4,6@5⦊‸⦉@5 mod in_mod { const IN_MOD_CONST: u32 = 1000; @@ -126,16 +129,19 @@ type InType = String; - if @7⦊is_true⦉@7 if @7⦊is_true⦉@7 @8,10,11⦊{ -@8,10,11⦊{ + in_func(countdown); - in_func(countdown); + }⦉@8,10,11 +48:16-50:6: @11[2]: _8 = const () +50:6-50:6: @11.Goto: goto -> bb12"> }⦉@8,10,11@9⦊‸⦉@9 let // Initialize test constants in a way that cannot be determined at compile time, to ensure // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from // dependent conditions. - let let @0,1,2,3⦊is_true = std::env::args().len() == 1; - -@0,1,2,3⦊is_true⦉@0,1,2,3 @4,6⦊{ +10:16-14:6: @6[3]: _9 = const () +14:6-14:6: @6.Goto: goto -> bb7">@4,6⦊{ a = 1; +10:16-14:6: @6[3]: _9 = const () +14:6-14:6: @6.Goto: goto -> bb7"> a = 1; b = 10; +10:16-14:6: @6[3]: _9 = const () +14:6-14:6: @6.Goto: goto -> bb7"> b = 10; c = 100; +10:16-14:6: @6[3]: _9 = const () +14:6-14:6: @6.Goto: goto -> bb7"> c = 100; }⦉@4,6 +10:16-14:6: @6[3]: _9 = const () +14:6-14:6: @6.Goto: goto -> bb7"> }⦉@4,6@5⦊‸⦉@5 let - @11⦊somebool⦉@11 + @11⦊somebool⦉@11 = - @7⦊a < b⦉@7 || - @10⦊b < c⦉@10 +20:13-20:18: @10[5]: _15 = Lt(move _16, move _17)">@10⦊b < c⦉@10@8⦊‸⦉@8@9⦊‸⦉@9 ; let - @15⦊somebool⦉@15 + @15⦊somebool⦉@15 = - @11⦊b < a⦉@11 || - @14⦊b < c⦉@14 +27:13-27:18: @14[5]: _22 = Lt(move _23, move _24)">@14⦊b < c⦉@14@12⦊‸⦉@12@13⦊‸⦉@13 ; - let - @19⦊somebool⦉@19 - = - @15⦊a < b⦉@15 - && - @18⦊b < c⦉@18 - ; - let - @23⦊somebool⦉@23 - = - @19⦊b < a⦉@19 - && - @22⦊b < c⦉@22 - ; -}@23⦊‸⦉@23 + let @19⦊somebool⦉@19 = @15⦊a < b⦉@15 && @18⦊b < c⦉@18@16⦊‸⦉@16@17⦊‸⦉@17; + let @23⦊somebool⦉@23 = @19⦊b < a⦉@19 && @22⦊b < c⦉@22@20⦊‸⦉@20@21⦊‸⦉@21; + + if + @23⦊! + is_true⦉@23 + @24,26⦊{ + a = 2 + ; + }⦉@24,26@25⦊‸⦉@25 + + if + @27⦊is_true⦉@27 + @28,30⦊{ + b = 30 + ; + }⦉@28,30 + else + @29⦊{ + c = 400 + ; + }⦉@29 + + if @31⦊!is_true⦉@31 @32,34⦊{ + a = 2; + }⦉@32,34@33⦊‸⦉@33 + + if @35⦊is_true⦉@35 @36,38⦊{ + b = 30; + }⦉@36,38 else @37⦊{ + c = 400; + }⦉@37 +}@39⦊‸⦉@39 diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html index 7dd22ee6e179f..618f84513e908 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html @@ -89,39 +89,45 @@ @0,1,2,3⦊is_true⦉@0,1,2,3 @4,6⦊{ +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7">@4,6⦊{ countdown +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7"> countdown = +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7"> = 10 +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7"> 10 ; +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7"> ; }⦉@4,6 +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7"> }⦉@4,6@5⦊‸⦉@5 loop { if - @8,9⦊countdown - == - 0⦉@8,9 { - @10,12⦊break⦉@10,12 + @10,12⦊break⦉@10,12 ; } - @13⦊countdown - -= - 1⦉@13 + @13⦊countdown + -= + 1⦉@13 ; - } + }@7⦊‸⦉@7 }@10,12⦊‸⦉@10,12 diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html index abcf30e93c04e..66885d0612bfc 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html @@ -86,19 +86,22 @@ 9:9-9:22: @3[3]: FakeRead(ForLet, _5)"> let mut countdown = 1⦉@0,1,2,3; if @0,1,2,3⦊is_true⦉@0,1,2,3 @4,6⦊{ +10:16-12:6: @6[1]: _6 = const () +12:6-12:6: @6.Goto: goto -> bb7">@4,6⦊{ countdown = 0; +10:16-12:6: @6[1]: _6 = const () +12:6-12:6: @6.Goto: goto -> bb7"> countdown = 0; }⦉@4,6 +10:16-12:6: @6[1]: _6 = const () +12:6-12:6: @6.Goto: goto -> bb7"> }⦉@4,6@5⦊‸⦉@5 for - @13,15,17⦊_⦉@13,15,17 in - @9,10,11⦊0..2⦉@9,10,11 @@ -106,16 +109,16 @@ let z ; match - @13,15,17⦊countdown⦉@13,15,17 + @13,15,17⦊countdown⦉@13,15,17 { - @18⦊x⦉@18 + @18⦊x⦉@18 if - @13,15,17⦊x - < - 1⦉@13,15,17 + 1⦉@13,15,17@19⦊‸⦉@19 => => @16⦊{}⦉@16 } - } + }@7,8⦊‸⦉@7,8@20⦊‸⦉@20 }@12⦊‸⦉@12 diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html index cb3017b0a108d..cc0e7abb1f3ad 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html @@ -108,10 +108,10 @@ 31:42-31:43: @26[4]: _36 = _33 31:42-31:43: @26.Call: _35 = <() as From<()>>::from(move _36) -> [return: bb27, unwind: bb32] 31:42-31:43: @27.Call: _0 = <std::result::Result<(), ()> as Try>::from_error(move _35) -> [return: bb28, unwind: bb32]">@24,26,27,28⦊?⦉@24,26,27,28; - } - } - }@15⦊‸⦉@15@23⦊‸⦉@23 + }@29⦊‸⦉@29 + @5⦊Ok(())⦉@5 -}@31⦊‸⦉@31 +}@30⦊‸⦉@30@31⦊‸⦉@31 diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.various_conditions/various_conditions.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.various_conditions/various_conditions.main.-------.InstrumentCoverage.0.html index a0daddb2e88d4..b9f6f4d7832b6 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.various_conditions/various_conditions.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.various_conditions/various_conditions.main.-------.InstrumentCoverage.0.html @@ -64,32 +64,38 @@ 4:9-4:22: @0[2]: FakeRead(ForLet, _1)">@0⦊mut countdown = 0⦉@0; if @0⦊true⦉@0 @1,3⦊{ +5:13-7:6: @3[1]: _2 = const () +7:6-7:6: @3.Goto: goto -> bb4">@1,3⦊{ countdown = 10; +5:13-7:6: @3[1]: _2 = const () +7:6-7:6: @3.Goto: goto -> bb4"> countdown = 10; }⦉@1,3 +5:13-7:6: @3[1]: _2 = const () +7:6-7:6: @3.Goto: goto -> bb4"> }⦉@1,3@2⦊‸⦉@2 const B: u32 = 100; - let @25⦊x⦉@25 = if let @25⦊x⦉@25 = if @4⦊countdown > 7⦉@4 { - @8⦊countdown -= 4; - B⦉@8 - } else if } else if @6⦊countdown > 2⦉@6 { - if @9,11⦊countdown < 1⦉@9,11 || @18⦊countdown > 5⦉@18 || @14⦊countdown != 9⦉@14 @20,22⦊{ - countdown = 0; - }⦉@20,22 + if @9,11⦊countdown < 1⦉@9,11 || @18⦊countdown > 5⦉@18@16⦊‸⦉@16@17⦊‸⦉@17 || @14⦊countdown != 9⦉@14@12⦊‸⦉@12@13⦊‸⦉@13 @20,22⦊{ + countdown = 0; + }⦉@20,22@21⦊‸⦉@21 @24⦊countdown -= 5; @25⦊mut countdown = 0⦉@25; if @25⦊true⦉@25 @26,28⦊{ +24:13-26:6: @28[1]: _22 = const () +26:6-26:6: @28.Goto: goto -> bb29">@26,28⦊{ countdown = 10; +24:13-26:6: @28[1]: _22 = const () +26:6-26:6: @28.Goto: goto -> bb29"> countdown = 10; }⦉@26,28 +24:13-26:6: @28[1]: _22 = const () +26:6-26:6: @28.Goto: goto -> bb29"> }⦉@26,28@27⦊‸⦉@27 - if if @29⦊countdown > 7⦉@29 { - @33⦊countdown -= 4⦉@33; - } else if @33⦊countdown -= 4⦉@33; + } else if @31⦊countdown > 2⦉@31 { - if @34,36⦊countdown < 1⦉@34,36 || @43⦊countdown > 5⦉@43 || @39⦊countdown != 9⦉@39 @45,47⦊{ - countdown = 0; - }⦉@45,47 + if @34,36⦊countdown < 1⦉@34,36 || @43⦊countdown > 5⦉@43@41⦊‸⦉@41@42⦊‸⦉@42 || @39⦊countdown != 9⦉@39@37⦊‸⦉@37@38⦊‸⦉@38 @45,47⦊{ + countdown = 0; + }⦉@45,47@46⦊‸⦉@46 @49⦊countdown -= 5⦉@49; } else { @35⦊return⦉@35; @@ -133,28 +145,34 @@ 39:9-39:22: @50[4]: FakeRead(ForLet, _41)">@50⦊mut countdown = 0⦉@50; if @50⦊true⦉@50 @51,53⦊{ +40:13-42:6: @53[1]: _42 = const () +42:6-42:6: @53.Goto: goto -> bb54">@51,53⦊{ countdown = 1; +40:13-42:6: @53[1]: _42 = const () +42:6-42:6: @53.Goto: goto -> bb54"> countdown = 1; }⦉@51,53 +40:13-42:6: @53[1]: _42 = const () +42:6-42:6: @53.Goto: goto -> bb54"> }⦉@51,53@52⦊‸⦉@52 - let @77⦊z⦉@77 = if let @77⦊z⦉@77 = if @54⦊countdown > 7⦉@54 { - @58⦊countdown -= 4⦉@58; - } else if @58⦊countdown -= 4⦉@58; + } else if @56⦊countdown > 2⦉@56 { - if @59,61⦊countdown < 1⦉@59,61 || @68⦊countdown > 5⦉@68 || @64⦊countdown != 9⦉@64 @70,72⦊{ - countdown = 0; - }⦉@70,72 + if @59,61⦊countdown < 1⦉@59,61 || @68⦊countdown > 5⦉@68@66⦊‸⦉@66@67⦊‸⦉@67 || @64⦊countdown != 9⦉@64@62⦊‸⦉@62@63⦊‸⦉@63 @70,72⦊{ + countdown = 0; + }⦉@70,72@71⦊‸⦉@71 @74⦊countdown -= 5⦉@74; } else { let @79⦊countdown > 2⦉@79 { if @82,84⦊countdown < 1⦉@82,84 || @91⦊countdown > 5⦉@91 || @87⦊countdown != 9⦉@87 @93,95⦊{ +60:29-60:42: @91[3]: _85 = Gt(move _86, const 5_i32)">@91⦊countdown > 5⦉@91@89⦊‸⦉@89@90⦊‸⦉@90 || @87⦊countdown != 9⦉@87@85⦊‸⦉@85@86⦊‸⦉@86 @93,95⦊{ countdown = 0; +60:61-62:10: @95[1]: _80 = const () +62:10-62:10: @95.Goto: goto -> bb96"> countdown = 0; }⦉@93,95 - @97⦊countdown -= 5⦉@97; +60:61-62:10: @95[1]: _80 = const () +62:10-62:10: @95.Goto: goto -> bb96"> }⦉@93,95@94⦊‸⦉@94 + @97⦊countdown -= 5⦉@97; } else { - @83⦊return⦉@83; + @83⦊return⦉@83; }; -}@102⦊‸⦉@102 +}@101⦊‸⦉@101@102⦊‸⦉@102 diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html index 7053319a817c7..3d75e61b80055 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html @@ -112,6 +112,6 @@ } @4⦊Ok(())⦉@4 -}@14⦊‸⦉@14 +}@13⦊‸⦉@13@14⦊‸⦉@14 From 3aef9e0ad899bf6f04403f7b6f3ec37611cee8a8 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Fri, 16 Oct 2020 12:14:17 -0700 Subject: [PATCH 07/18] Fixed coverage issues that I didn't like. * Avoid adding coverage to unreachable blocks. * Special case for Goto at the end of the body. Make it non-reportable. --- .../src/transform/instrument_coverage.rs | 88 +++++++------------ ...expected_export_coverage.simple_match.json | 8 +- ...cted_export_coverage.try_error_result.json | 20 ++--- ...ed_export_coverage.various_conditions.json | 12 +-- ...ed_export_coverage.while_early_return.json | 12 +-- .../expected_show_coverage.simple_match.txt | 8 +- ...xpected_show_coverage.try_error_result.txt | 4 +- ...ected_show_coverage.various_conditions.txt | 2 +- ...ected_show_coverage.while_early_return.txt | 2 +- ...ed_show_coverage_counters.simple_match.txt | 12 ++- ...how_coverage_counters.try_error_result.txt | 25 +++--- ...w_coverage_counters.various_conditions.txt | 7 +- ...w_coverage_counters.while_early_return.txt | 7 +- ...expected_export_coverage.simple_match.json | 8 +- ...cted_export_coverage.try_error_result.json | 20 ++--- ...ed_export_coverage.various_conditions.json | 12 +-- ...ed_export_coverage.while_early_return.json | 12 +-- .../expected_show_coverage.simple_match.txt | 8 +- ...xpected_show_coverage.try_error_result.txt | 4 +- ...ected_show_coverage.various_conditions.txt | 2 +- ...ected_show_coverage.while_early_return.txt | 2 +- ...ed_show_coverage_counters.simple_match.txt | 12 ++- ...how_coverage_counters.try_error_result.txt | 25 +++--- ...w_coverage_counters.various_conditions.txt | 7 +- ...w_coverage_counters.while_early_return.txt | 7 +- 25 files changed, 148 insertions(+), 178 deletions(-) diff --git a/compiler/rustc_mir/src/transform/instrument_coverage.rs b/compiler/rustc_mir/src/transform/instrument_coverage.rs index 8f152f9de387b..8aab8b179352f 100644 --- a/compiler/rustc_mir/src/transform/instrument_coverage.rs +++ b/compiler/rustc_mir/src/transform/instrument_coverage.rs @@ -381,7 +381,7 @@ impl BasicCoverageBlocks { let successors = IndexVec::from_fn_n( |bcb| { let bcb_data = &bcbs[bcb]; - let bcb_successors = bcb_filtered_successors(&bcb_data.terminator(mir_body).kind) + let bcb_successors = bcb_filtered_successors(&mir_body, &bcb_data.terminator(mir_body).kind) // TODO(richkadel): // MAKE SURE WE ONLY RETURN THE SAME SUCCESSORS USED WHEN CREATING THE BCB (THE FIRST SUCCESSOR ONLY, // UNLESS ITS A SWITCHINT).) @@ -425,7 +425,7 @@ impl BasicCoverageBlocks { // each block terminator's `successors()`. Coverage spans must map to actual source code, // so compiler generated blocks and paths can be ignored. To that end, the CFG traversal // intentionally omits unwind paths. - let mir_cfg_without_unwind = ShortCircuitPreorder::new(mir_body, bcb_filtered_successors); + let mir_cfg_without_unwind = ShortCircuitPreorder::new(&mir_body, bcb_filtered_successors); let mut basic_blocks = Vec::new(); for (bb, data) in mir_cfg_without_unwind { @@ -1078,7 +1078,17 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { if let Some(bcb_to_coverage_spans_with_counters) = debug_bcb_to_coverage_spans_with_counters.as_mut() { bcb_to_coverage_spans_with_counters.entry(bcb).or_insert_with(|| Vec::new()).push((covspan.clone(), counter_kind.clone())); } - self.inject_statement(counter_kind, self.bcb_last_bb(bcb), make_code_region(file_name, &source_file, span, body_span)); + let mut code_region = None; + if span.hi() == body_span.hi() { + // TODO(richkadel): add a comment if this works + if let TerminatorKind::Goto { .. } = self.bcb_terminator(bcb).kind { + code_region = Some(make_non_reportable_code_region(file_name, &source_file, span)); + } + } + if code_region.is_none() { + code_region = Some(make_code_region(file_name, &source_file, span, body_span)); + }; + self.inject_statement(counter_kind, self.bcb_last_bb(bcb), code_region.unwrap()); } // The previous step looped through the `CoverageSpan`s and injected the counter from the @@ -1218,7 +1228,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { }; let edge_labels = |from_bcb| { let from_terminator = self.bcb_terminator(from_bcb); - let edge_labels = from_terminator.kind.fmt_successor_labels(); + let mut edge_labels = from_terminator.kind.fmt_successor_labels(); + edge_labels.retain(|label| label.to_string() != "unreachable"); let edge_counters = from_terminator.successors().map(|&successor| { edge_to_counter.get(&(from_bcb, successor)) }); @@ -2663,34 +2674,34 @@ fn hash( stable_hasher.finish() } -fn bcb_filtered_successors<'tcx>(term_kind: &'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx> { +fn bcb_filtered_successors<'a, 'tcx>(body: &'tcx &'a mir::Body<'tcx>, term_kind: &'tcx TerminatorKind<'tcx>) -> Box + 'a> { let mut successors = term_kind.successors(); - match &term_kind { + box match &term_kind { // SwitchInt successors are never unwind, and all of them should be traversed. TerminatorKind::SwitchInt { .. } => successors, // For all other kinds, return only the first successor, if any, and ignore unwinds. // NOTE: `chain(&[])` is required to coerce the `option::iter` (from // `next().into_iter()`) into the `mir::Successors` aliased type. _ => successors.next().into_iter().chain(&[]), - } + }.filter(move |&&successor| body[successor].terminator().kind != TerminatorKind::Unreachable) } pub struct ShortCircuitPreorder< 'a, 'tcx, - F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>, + F: Fn(&'tcx &'a mir::Body<'tcx>, &'tcx TerminatorKind<'tcx>) -> Box + 'a>, > { - body: &'a mir::Body<'tcx>, + body: &'tcx &'a mir::Body<'tcx>, visited: BitSet, worklist: Vec, filtered_successors: F, } -impl<'a, 'tcx, F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>> +impl<'a, 'tcx, F: Fn(&'tcx &'a mir::Body<'tcx>, &'tcx TerminatorKind<'tcx>) -> Box + 'a>> ShortCircuitPreorder<'a, 'tcx, F> { pub fn new( - body: &'a mir::Body<'tcx>, + body: &'tcx &'a mir::Body<'tcx>, filtered_successors: F, ) -> ShortCircuitPreorder<'a, 'tcx, F> { let worklist = vec![mir::START_BLOCK]; @@ -2704,7 +2715,7 @@ impl<'a, 'tcx, F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>> } } -impl<'a: 'tcx, 'tcx, F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>> Iterator +impl<'a: 'tcx, 'tcx, F: Fn(&'tcx &'a mir::Body<'tcx>, &'tcx TerminatorKind<'tcx>) -> Box + 'a>> Iterator for ShortCircuitPreorder<'a, 'tcx, F> { type Item = (BasicBlock, &'a BasicBlockData<'tcx>); @@ -2718,7 +2729,7 @@ impl<'a: 'tcx, 'tcx, F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>> let data = &self.body[idx]; if let Some(ref term) = data.terminator { - self.worklist.extend((self.filtered_successors)(&term.kind)); + self.worklist.extend((self.filtered_successors)(&self.body, &term.kind)); } return Some((idx, data)); @@ -2733,49 +2744,10 @@ impl<'a: 'tcx, 'tcx, F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>> } } -// NOTE: Regarding past efforts and revelations when trying to identify `Unreachable` coverage spans -// from the MIR: -// -// TerminatorKind::FalseEdge targets from SwitchInt don't appear to be helpful in identifying -// unreachable code. I did test the theory, but the following changes were not beneficial. (I -// assumed that replacing some constants with non-deterministic variables might effect which blocks -// were targeted by a `FalseEdge` `imaginary_target`. It did not.) -// -// Also note that, if there is a way to identify BasicBlocks that are part of the MIR CFG, but not -// actually reachable, here are some other things to consider: -// -// Injecting unreachable code regions will probably require computing the set difference between the -// basic blocks found without filtering out unreachable blocks, and the basic blocks found with a -// filter (similar to or as an extension of the `filter_unwind_paths` filter); then computing the -// `CoverageSpans` without the filter; and then injecting `Counter`s or `Expression`s for -// blocks that are not unreachable, or injecting `Unreachable` code regions otherwise. This seems -// straightforward, but not trivial. -// -// Alternatively, we might instead want to leave the unreachable blocks in (bypass the filter here), -// and inject the counters. This will result in counter values of zero (0) for unreachable code -// (and, notably, the code will be displayed with a red background by `llvm-cov show`). -// -// ```rust -// TerminatorKind::SwitchInt { .. } => { -// let some_imaginary_target = successors.clone().find_map(|&successor| { -// let term = mir_body[successor].terminator(); -// if let TerminatorKind::FalseEdge { imaginary_target, .. } = term.kind { -// if mir_body.predecessors()[imaginary_target].len() == 1 { -// return Some(imaginary_target); -// } -// } -// None -// }); -// if let Some(imaginary_target) = some_imaginary_target { -// box successors.filter(move |&&successor| successor != imaginary_target) -// } else { -// box successors -// } -// } -// ``` -// -// Note this also required changing the closure signature for the `ShortCurcuitPreorder` to: +// TODO(richkadel): try_error_result.rs +// When executing the Result as Try>::from_error() returns to one or more +// Goto that then targets Return. +// Both the Goto (after error) and the Return have coverage at the last +// bytepos, 0-length Span. // -// ```rust -// F: Fn(&'tcx TerminatorKind<'tcx>) -> Box + 'a>, -// ``` +// How should I eliminate one, and which one, to avoid counting both. diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_match.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_match.json index 2ae9ad9c4ebd4..63d1ae74c5f5d 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_match.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_match.json @@ -16,9 +16,9 @@ "percent": 100 }, "lines": { - "count": 28, + "count": 24, "covered": 24, - "percent": 85.71428571428571 + "percent": 100 }, "regions": { "count": 15, @@ -41,9 +41,9 @@ "percent": 100 }, "lines": { - "count": 28, + "count": 24, "covered": 24, - "percent": 85.71428571428571 + "percent": 100 }, "regions": { "count": 15, diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.try_error_result.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.try_error_result.json index bcefa927e8fae..929e769b50a98 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.try_error_result.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.try_error_result.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 20, + "count": 19, "covered": 18, - "percent": 90 + "percent": 94.73684210526315 }, "regions": { - "count": 18, - "covered": 15, + "count": 17, + "covered": 14, "notcovered": 3, - "percent": 83.33333333333334 + "percent": 82.35294117647058 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 20, + "count": 19, "covered": 18, - "percent": 90 + "percent": 94.73684210526315 }, "regions": { - "count": 18, - "covered": 15, + "count": 17, + "covered": 14, "notcovered": 3, - "percent": 83.33333333333334 + "percent": 82.35294117647058 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.various_conditions.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.various_conditions.json index d5f954b4f7e07..1d239867b801a 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.various_conditions.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.various_conditions.json @@ -21,10 +21,10 @@ "percent": 48.97959183673469 }, "regions": { - "count": 70, - "covered": 19, + "count": 69, + "covered": 18, "notcovered": 51, - "percent": 27.142857142857142 + "percent": 26.08695652173913 } } } @@ -46,10 +46,10 @@ "percent": 48.97959183673469 }, "regions": { - "count": 70, - "covered": 19, + "count": 69, + "covered": 18, "notcovered": 51, - "percent": 27.142857142857142 + "percent": 26.08695652173913 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while_early_return.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while_early_return.json index a116a91a60aae..a9e01604ccd5b 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while_early_return.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while_early_return.json @@ -21,10 +21,10 @@ "percent": 88.23529411764706 }, "regions": { - "count": 10, - "covered": 8, + "count": 9, + "covered": 7, "notcovered": 2, - "percent": 80 + "percent": 77.77777777777779 } } } @@ -46,10 +46,10 @@ "percent": 88.23529411764706 }, "regions": { - "count": 10, - "covered": 8, + "count": 9, + "covered": 7, "notcovered": 2, - "percent": 80 + "percent": 77.77777777777779 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_match.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_match.txt index 55b23dd882035..1f7e71d3eb0e7 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_match.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_match.txt @@ -16,10 +16,10 @@ 15| 2| _ 16| | in 17| 3| 0..2 - 18| 0| { - 19| 0| let z - 20| 0| ; - 21| 0| match + 18| | { + 19| | let z + 20| | ; + 21| | match 22| 2| countdown 23| | { 24| 1| x diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.try_error_result.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.try_error_result.txt index 489a5324a4446..94c63c9a2b906 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.try_error_result.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.try_error_result.txt @@ -17,7 +17,7 @@ 17| 6| _ 18| | in 19| 6| 0..10 - 20| 0| { + 20| | { 21| 6| countdown 22| 6| -= 1 23| 6| ; @@ -33,5 +33,5 @@ 32| 5| } 33| 5| } 34| 0| Ok(()) - 35| 2|} + 35| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.various_conditions.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.various_conditions.txt index 9f2deae8a9e55..bd52c52a66840 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.various_conditions.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.various_conditions.txt @@ -66,5 +66,5 @@ 64| | } else { 65| 0| return; 66| | }; - 67| 2|} + 67| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while_early_return.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while_early_return.txt index e5c5b05a6fc80..26041136d2f4c 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while_early_return.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while_early_return.txt @@ -33,7 +33,7 @@ 33| | ; 34| | } 35| 0| Ok(()) - 36| 2|} + 36| 1|} 37| | 38| |// ISSUE(77553): Originally, this test had `Err(1)` on line 22 (instead of `Ok(())`) and 39| |// `std::process::exit(2)` on line 26 (instead of `Err(1)`); and this worked as expected on Linux diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.simple_match.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.simple_match.txt index a21a26b7089b9..2a3c004fa7abe 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.simple_match.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.simple_match.txt @@ -3,13 +3,12 @@ Counter in file 0 7:9 -> 9:26, #1 Counter in file 0 10:8 -> 10:15, (#1 + 0) Counter in file 0 10:16 -> 12:6, #2 Counter in file 0 12:6 -> 12:7, (#1 - #2) -Counter in file 0 15:9 -> 15:10, (((#2 + (#1 - #2)) + (#3 + #4)) - (#6 + #5)) +Counter in file 0 15:9 -> 15:10, (((#2 + (#1 - #2)) + (#3 + #4)) - #5) Counter in file 0 17:9 -> 17:13, ((#2 + (#1 - #2)) + (#3 + #4)) -Counter in file 0 17:13 -> 17:13, #6 -Counter in file 0 22:13 -> 22:22, ((((#2 + (#1 - #2)) + (#3 + #4)) - (#6 + #5)) + 0) +Counter in file 0 22:13 -> 22:22, ((((#2 + (#1 - #2)) + (#3 + #4)) - #5) + 0) Counter in file 0 24:13 -> 24:14, #3 -Counter in file 0 26:17 -> 28:18, ((((#2 + (#1 - #2)) + (#3 + #4)) - (#6 + #5)) + 0) -Counter in file 0 28:18 -> 28:19, ((((#2 + (#1 - #2)) + (#3 + #4)) - (#6 + #5)) - #3) +Counter in file 0 26:17 -> 28:18, ((((#2 + (#1 - #2)) + (#3 + #4)) - #5) + 0) +Counter in file 0 28:18 -> 28:19, ((((#2 + (#1 - #2)) + (#3 + #4)) - #5) - #3) Counter in file 0 30:13 -> 37:14, (#3 + 0) Counter in file 0 40:13 -> 40:15, #4 Counter in file 0 42:6 -> 42:7, (#2 + (#1 - #2)) @@ -23,7 +22,6 @@ Combined regions: 12:6 -> 12:7 (count=0) 15:9 -> 15:10 (count=2) 17:9 -> 17:13 (count=3) - 17:13 -> 17:13 (count=0) 22:13 -> 22:22 (count=2) 24:13 -> 24:14 (count=1) 26:17 -> 28:18 (count=2) @@ -42,7 +40,7 @@ Segment at 12:7 (count = 0), Skipped Segment at 15:9 (count = 2), RegionEntry Segment at 15:10 (count = 0), Skipped Segment at 17:9 (count = 3), RegionEntry -Segment at 17:13 (count = 0), Gap +Segment at 17:13 (count = 0), Skipped Segment at 22:13 (count = 2), RegionEntry Segment at 22:22 (count = 0), Skipped Segment at 24:13 (count = 1), RegionEntry diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.try_error_result.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.try_error_result.txt index ea1d99623e80a..ede836997ea17 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.try_error_result.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.try_error_result.txt @@ -1,19 +1,18 @@ Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/try_error_result.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/try_error_result Counter in file 0 13:9 -> 14:23, #1 -Counter in file 0 17:9 -> 17:10, ((#1 + (#2 + #3)) - (#5 + #4)) +Counter in file 0 17:9 -> 17:10, ((#1 + (#2 + #3)) - #4) Counter in file 0 19:9 -> 19:14, (#1 + (#2 + #3)) -Counter in file 0 19:14 -> 19:14, #5 -Counter in file 0 21:9 -> 25:26, #8 -Counter in file 0 27:13 -> 27:41, #9 -Counter in file 0 27:41 -> 27:42, #6 -Counter in file 0 31:13 -> 31:42, (#8 - #9) -Counter in file 0 31:42 -> 31:43, #7 +Counter in file 0 21:9 -> 25:26, #7 +Counter in file 0 27:13 -> 27:41, #8 +Counter in file 0 27:41 -> 27:42, #5 +Counter in file 0 31:13 -> 31:42, (#7 - #8) +Counter in file 0 31:42 -> 31:43, #6 Counter in file 0 32:10 -> 32:11, #2 Counter in file 0 32:10 -> 32:11, #3 Counter in file 0 33:6 -> 33:7, (#2 + #3) Counter in file 0 34:5 -> 34:11, #4 -Counter in file 0 35:1 -> 35:2, (#6 + #7) -Counter in file 0 35:1 -> 35:2, ((#6 + #7) + #4) +Counter in file 0 35:1 -> 35:2, ((#5 + #6) + #4) +Counter in file 0 35:2 -> 35:2, (#5 + #6) Counter in file 0 5:8 -> 5:20, #1 Counter in file 0 6:9 -> 6:16, #2 Counter in file 0 8:9 -> 8:15, (#1 - #2) @@ -27,7 +26,6 @@ Combined regions: 13:9 -> 14:23 (count=1) 17:9 -> 17:10 (count=6) 19:9 -> 19:14 (count=6) - 19:14 -> 19:14 (count=0) 21:9 -> 25:26 (count=6) 27:13 -> 27:41 (count=1) 27:41 -> 27:42 (count=1) @@ -36,7 +34,8 @@ Combined regions: 32:10 -> 32:11 (count=5) 33:6 -> 33:7 (count=5) 34:5 -> 34:11 (count=0) - 35:1 -> 35:2 (count=2) + 35:1 -> 35:2 (count=1) + 35:2 -> 35:2 (count=1) Segment at 5:8 (count = 6), RegionEntry Segment at 5:20 (count = 0), Skipped Segment at 6:9 (count = 1), RegionEntry @@ -50,7 +49,7 @@ Segment at 14:23 (count = 0), Skipped Segment at 17:9 (count = 6), RegionEntry Segment at 17:10 (count = 0), Skipped Segment at 19:9 (count = 6), RegionEntry -Segment at 19:14 (count = 0), Gap +Segment at 19:14 (count = 0), Skipped Segment at 21:9 (count = 6), RegionEntry Segment at 25:26 (count = 0), Skipped Segment at 27:13 (count = 1), RegionEntry @@ -65,5 +64,5 @@ Segment at 33:6 (count = 5), RegionEntry Segment at 33:7 (count = 0), Skipped Segment at 34:5 (count = 0), RegionEntry Segment at 34:11 (count = 0), Skipped -Segment at 35:1 (count = 2), RegionEntry +Segment at 35:1 (count = 1), RegionEntry Segment at 35:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.various_conditions.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.various_conditions.txt index ddc0acb78fad2..b11a2f1677eaf 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.various_conditions.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.various_conditions.txt @@ -88,7 +88,7 @@ Counter in file 0 62:10 -> 62:11, (((#38 + #39) + ((((#35 + #36) + (((((#25 + #2 Counter in file 0 63:9 -> 63:23, #10 Counter in file 0 65:9 -> 65:15, #6 Counter in file 0 67:1 -> 67:2, ((#9 + #10) + (((#6 + #7) + #8) + (((#2 + 0) - #3) - #5))) -Counter in file 0 67:1 -> 67:2, (((#6 + #7) + #8) + (((#2 + 0) - #3) - #5)) +Counter in file 0 67:2 -> 67:2, (((#6 + #7) + #8) + (((#2 + 0) - #3) - #5)) Emitting segments for file: ../coverage/various_conditions.rs Combined regions: 4:9 -> 4:26 (count=1) @@ -163,7 +163,8 @@ Combined regions: 62:10 -> 62:11 (count=0) 63:9 -> 63:23 (count=0) 65:9 -> 65:15 (count=0) - 67:1 -> 67:2 (count=2) + 67:1 -> 67:2 (count=1) + 67:2 -> 67:2 (count=1) Segment at 4:9 (count = 1), RegionEntry Segment at 4:26 (count = 0), Skipped Segment at 5:8 (count = 1), RegionEntry @@ -269,5 +270,5 @@ Segment at 63:9 (count = 0), RegionEntry Segment at 63:23 (count = 0), Skipped Segment at 65:9 (count = 0), RegionEntry Segment at 65:15 (count = 0), Skipped -Segment at 67:1 (count = 2), RegionEntry +Segment at 67:1 (count = 1), RegionEntry Segment at 67:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while_early_return.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while_early_return.txt index 2ca90e5eb2fd5..0225b167746cc 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while_early_return.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while_early_return.txt @@ -7,8 +7,8 @@ Counter in file 0 22:21 -> 22:27, #4 Counter in file 0 26:21 -> 26:27, #5 Counter in file 0 30:9 -> 32:10, #2 Counter in file 0 35:5 -> 35:11, #3 -Counter in file 0 36:1 -> 36:2, (#4 + #5) Counter in file 0 36:1 -> 36:2, ((#4 + #5) + #3) +Counter in file 0 36:2 -> 36:2, (#4 + #5) Emitting segments for file: ../coverage/while_early_return.rs Combined regions: 5:9 -> 5:27 (count=1) @@ -19,7 +19,8 @@ Combined regions: 26:21 -> 26:27 (count=1) 30:9 -> 32:10 (count=6) 35:5 -> 35:11 (count=0) - 36:1 -> 36:2 (count=2) + 36:1 -> 36:2 (count=1) + 36:2 -> 36:2 (count=1) Segment at 5:9 (count = 1), RegionEntry Segment at 5:27 (count = 0), Skipped Segment at 7:9 (count = 7), RegionEntry @@ -36,5 +37,5 @@ Segment at 30:9 (count = 6), RegionEntry Segment at 32:10 (count = 0), Skipped Segment at 35:5 (count = 0), RegionEntry Segment at 35:11 (count = 0), Skipped -Segment at 36:1 (count = 2), RegionEntry +Segment at 36:1 (count = 1), RegionEntry Segment at 36:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_match.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_match.json index 2ae9ad9c4ebd4..63d1ae74c5f5d 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_match.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_match.json @@ -16,9 +16,9 @@ "percent": 100 }, "lines": { - "count": 28, + "count": 24, "covered": 24, - "percent": 85.71428571428571 + "percent": 100 }, "regions": { "count": 15, @@ -41,9 +41,9 @@ "percent": 100 }, "lines": { - "count": 28, + "count": 24, "covered": 24, - "percent": 85.71428571428571 + "percent": 100 }, "regions": { "count": 15, diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.try_error_result.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.try_error_result.json index bcefa927e8fae..929e769b50a98 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.try_error_result.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.try_error_result.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 20, + "count": 19, "covered": 18, - "percent": 90 + "percent": 94.73684210526315 }, "regions": { - "count": 18, - "covered": 15, + "count": 17, + "covered": 14, "notcovered": 3, - "percent": 83.33333333333334 + "percent": 82.35294117647058 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 20, + "count": 19, "covered": 18, - "percent": 90 + "percent": 94.73684210526315 }, "regions": { - "count": 18, - "covered": 15, + "count": 17, + "covered": 14, "notcovered": 3, - "percent": 83.33333333333334 + "percent": 82.35294117647058 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.various_conditions.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.various_conditions.json index d5f954b4f7e07..1d239867b801a 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.various_conditions.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.various_conditions.json @@ -21,10 +21,10 @@ "percent": 48.97959183673469 }, "regions": { - "count": 70, - "covered": 19, + "count": 69, + "covered": 18, "notcovered": 51, - "percent": 27.142857142857142 + "percent": 26.08695652173913 } } } @@ -46,10 +46,10 @@ "percent": 48.97959183673469 }, "regions": { - "count": 70, - "covered": 19, + "count": 69, + "covered": 18, "notcovered": 51, - "percent": 27.142857142857142 + "percent": 26.08695652173913 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while_early_return.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while_early_return.json index a116a91a60aae..a9e01604ccd5b 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while_early_return.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while_early_return.json @@ -21,10 +21,10 @@ "percent": 88.23529411764706 }, "regions": { - "count": 10, - "covered": 8, + "count": 9, + "covered": 7, "notcovered": 2, - "percent": 80 + "percent": 77.77777777777779 } } } @@ -46,10 +46,10 @@ "percent": 88.23529411764706 }, "regions": { - "count": 10, - "covered": 8, + "count": 9, + "covered": 7, "notcovered": 2, - "percent": 80 + "percent": 77.77777777777779 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_match.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_match.txt index 55b23dd882035..1f7e71d3eb0e7 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_match.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_match.txt @@ -16,10 +16,10 @@ 15| 2| _ 16| | in 17| 3| 0..2 - 18| 0| { - 19| 0| let z - 20| 0| ; - 21| 0| match + 18| | { + 19| | let z + 20| | ; + 21| | match 22| 2| countdown 23| | { 24| 1| x diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.try_error_result.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.try_error_result.txt index 489a5324a4446..94c63c9a2b906 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.try_error_result.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.try_error_result.txt @@ -17,7 +17,7 @@ 17| 6| _ 18| | in 19| 6| 0..10 - 20| 0| { + 20| | { 21| 6| countdown 22| 6| -= 1 23| 6| ; @@ -33,5 +33,5 @@ 32| 5| } 33| 5| } 34| 0| Ok(()) - 35| 2|} + 35| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.various_conditions.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.various_conditions.txt index 9f2deae8a9e55..bd52c52a66840 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.various_conditions.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.various_conditions.txt @@ -66,5 +66,5 @@ 64| | } else { 65| 0| return; 66| | }; - 67| 2|} + 67| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while_early_return.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while_early_return.txt index e5c5b05a6fc80..26041136d2f4c 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while_early_return.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while_early_return.txt @@ -33,7 +33,7 @@ 33| | ; 34| | } 35| 0| Ok(()) - 36| 2|} + 36| 1|} 37| | 38| |// ISSUE(77553): Originally, this test had `Err(1)` on line 22 (instead of `Ok(())`) and 39| |// `std::process::exit(2)` on line 26 (instead of `Err(1)`); and this worked as expected on Linux diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.simple_match.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.simple_match.txt index 11b6667df0838..1683b68e96641 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.simple_match.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.simple_match.txt @@ -3,13 +3,12 @@ Counter in file 0 7:9 -> 9:26, #1 Counter in file 0 10:8 -> 10:15, (#1 + 0) Counter in file 0 10:16 -> 12:6, #2 Counter in file 0 12:6 -> 12:7, (#1 - #2) -Counter in file 0 15:9 -> 15:10, (((#2 + (#1 - #2)) + (#3 + #4)) - (#6 + #5)) +Counter in file 0 15:9 -> 15:10, (((#2 + (#1 - #2)) + (#3 + #4)) - #5) Counter in file 0 17:9 -> 17:13, ((#2 + (#1 - #2)) + (#3 + #4)) -Counter in file 0 17:13 -> 17:13, #6 -Counter in file 0 22:13 -> 22:22, ((((#2 + (#1 - #2)) + (#3 + #4)) - (#6 + #5)) + 0) +Counter in file 0 22:13 -> 22:22, ((((#2 + (#1 - #2)) + (#3 + #4)) - #5) + 0) Counter in file 0 24:13 -> 24:14, #3 -Counter in file 0 26:17 -> 28:18, ((((#2 + (#1 - #2)) + (#3 + #4)) - (#6 + #5)) + 0) -Counter in file 0 28:18 -> 28:19, ((((#2 + (#1 - #2)) + (#3 + #4)) - (#6 + #5)) - #3) +Counter in file 0 26:17 -> 28:18, ((((#2 + (#1 - #2)) + (#3 + #4)) - #5) + 0) +Counter in file 0 28:18 -> 28:19, ((((#2 + (#1 - #2)) + (#3 + #4)) - #5) - #3) Counter in file 0 30:13 -> 37:14, (#3 + 0) Counter in file 0 40:13 -> 40:15, #4 Counter in file 0 42:6 -> 42:7, (#2 + (#1 - #2)) @@ -23,7 +22,6 @@ Combined regions: 12:6 -> 12:7 (count=0) 15:9 -> 15:10 (count=2) 17:9 -> 17:13 (count=3) - 17:13 -> 17:13 (count=0) 22:13 -> 22:22 (count=2) 24:13 -> 24:14 (count=1) 26:17 -> 28:18 (count=2) @@ -42,7 +40,7 @@ Segment at 12:7 (count = 0), Skipped Segment at 15:9 (count = 2), RegionEntry Segment at 15:10 (count = 0), Skipped Segment at 17:9 (count = 3), RegionEntry -Segment at 17:13 (count = 0), Gap +Segment at 17:13 (count = 0), Skipped Segment at 22:13 (count = 2), RegionEntry Segment at 22:22 (count = 0), Skipped Segment at 24:13 (count = 1), RegionEntry diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.try_error_result.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.try_error_result.txt index f0cdf8976ba3b..12b880386b7c7 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.try_error_result.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.try_error_result.txt @@ -1,19 +1,18 @@ Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/try_error_result.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/try_error_result Counter in file 0 13:9 -> 14:23, #1 -Counter in file 0 17:9 -> 17:10, ((#1 + (#2 + #3)) - (#5 + #4)) +Counter in file 0 17:9 -> 17:10, ((#1 + (#2 + #3)) - #4) Counter in file 0 19:9 -> 19:14, (#1 + (#2 + #3)) -Counter in file 0 19:14 -> 19:14, #5 -Counter in file 0 21:9 -> 25:26, #8 -Counter in file 0 27:13 -> 27:41, #9 -Counter in file 0 27:41 -> 27:42, #6 -Counter in file 0 31:13 -> 31:42, (#8 - #9) -Counter in file 0 31:42 -> 31:43, #7 +Counter in file 0 21:9 -> 25:26, #7 +Counter in file 0 27:13 -> 27:41, #8 +Counter in file 0 27:41 -> 27:42, #5 +Counter in file 0 31:13 -> 31:42, (#7 - #8) +Counter in file 0 31:42 -> 31:43, #6 Counter in file 0 32:10 -> 32:11, #2 Counter in file 0 32:10 -> 32:11, #3 Counter in file 0 33:6 -> 33:7, (#2 + #3) Counter in file 0 34:5 -> 34:11, #4 -Counter in file 0 35:1 -> 35:2, (#6 + #7) -Counter in file 0 35:1 -> 35:2, ((#6 + #7) + #4) +Counter in file 0 35:1 -> 35:2, ((#5 + #6) + #4) +Counter in file 0 35:2 -> 35:2, (#5 + #6) Counter in file 0 5:8 -> 5:20, #1 Counter in file 0 6:9 -> 6:16, #2 Counter in file 0 8:9 -> 8:15, (#1 - #2) @@ -27,7 +26,6 @@ Combined regions: 13:9 -> 14:23 (count=1) 17:9 -> 17:10 (count=6) 19:9 -> 19:14 (count=6) - 19:14 -> 19:14 (count=0) 21:9 -> 25:26 (count=6) 27:13 -> 27:41 (count=1) 27:41 -> 27:42 (count=1) @@ -36,7 +34,8 @@ Combined regions: 32:10 -> 32:11 (count=5) 33:6 -> 33:7 (count=5) 34:5 -> 34:11 (count=0) - 35:1 -> 35:2 (count=2) + 35:1 -> 35:2 (count=1) + 35:2 -> 35:2 (count=1) Segment at 5:8 (count = 6), RegionEntry Segment at 5:20 (count = 0), Skipped Segment at 6:9 (count = 1), RegionEntry @@ -50,7 +49,7 @@ Segment at 14:23 (count = 0), Skipped Segment at 17:9 (count = 6), RegionEntry Segment at 17:10 (count = 0), Skipped Segment at 19:9 (count = 6), RegionEntry -Segment at 19:14 (count = 0), Gap +Segment at 19:14 (count = 0), Skipped Segment at 21:9 (count = 6), RegionEntry Segment at 25:26 (count = 0), Skipped Segment at 27:13 (count = 1), RegionEntry @@ -65,5 +64,5 @@ Segment at 33:6 (count = 5), RegionEntry Segment at 33:7 (count = 0), Skipped Segment at 34:5 (count = 0), RegionEntry Segment at 34:11 (count = 0), Skipped -Segment at 35:1 (count = 2), RegionEntry +Segment at 35:1 (count = 1), RegionEntry Segment at 35:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.various_conditions.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.various_conditions.txt index 99c6a1a2e9aa2..0c551639a8553 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.various_conditions.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.various_conditions.txt @@ -88,7 +88,7 @@ Counter in file 0 62:10 -> 62:11, (((#38 + #39) + ((((#35 + #36) + (((((#25 + #2 Counter in file 0 63:9 -> 63:23, #10 Counter in file 0 65:9 -> 65:15, #6 Counter in file 0 67:1 -> 67:2, ((#9 + #10) + (((#6 + #7) + #8) + (((#2 + 0) - #3) - #5))) -Counter in file 0 67:1 -> 67:2, (((#6 + #7) + #8) + (((#2 + 0) - #3) - #5)) +Counter in file 0 67:2 -> 67:2, (((#6 + #7) + #8) + (((#2 + 0) - #3) - #5)) Emitting segments for file: ../coverage/various_conditions.rs Combined regions: 4:9 -> 4:26 (count=1) @@ -163,7 +163,8 @@ Combined regions: 62:10 -> 62:11 (count=0) 63:9 -> 63:23 (count=0) 65:9 -> 65:15 (count=0) - 67:1 -> 67:2 (count=2) + 67:1 -> 67:2 (count=1) + 67:2 -> 67:2 (count=1) Segment at 4:9 (count = 1), RegionEntry Segment at 4:26 (count = 0), Skipped Segment at 5:8 (count = 1), RegionEntry @@ -269,5 +270,5 @@ Segment at 63:9 (count = 0), RegionEntry Segment at 63:23 (count = 0), Skipped Segment at 65:9 (count = 0), RegionEntry Segment at 65:15 (count = 0), Skipped -Segment at 67:1 (count = 2), RegionEntry +Segment at 67:1 (count = 1), RegionEntry Segment at 67:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while_early_return.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while_early_return.txt index 42f1dbdba75ff..54938f8dc8284 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while_early_return.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while_early_return.txt @@ -7,8 +7,8 @@ Counter in file 0 22:21 -> 22:27, #4 Counter in file 0 26:21 -> 26:27, #5 Counter in file 0 30:9 -> 32:10, #2 Counter in file 0 35:5 -> 35:11, #3 -Counter in file 0 36:1 -> 36:2, (#4 + #5) Counter in file 0 36:1 -> 36:2, ((#4 + #5) + #3) +Counter in file 0 36:2 -> 36:2, (#4 + #5) Emitting segments for file: ../coverage/while_early_return.rs Combined regions: 5:9 -> 5:27 (count=1) @@ -19,7 +19,8 @@ Combined regions: 26:21 -> 26:27 (count=1) 30:9 -> 32:10 (count=6) 35:5 -> 35:11 (count=0) - 36:1 -> 36:2 (count=2) + 36:1 -> 36:2 (count=1) + 36:2 -> 36:2 (count=1) Segment at 5:9 (count = 1), RegionEntry Segment at 5:27 (count = 0), Skipped Segment at 7:9 (count = 7), RegionEntry @@ -36,5 +37,5 @@ Segment at 30:9 (count = 6), RegionEntry Segment at 32:10 (count = 0), Skipped Segment at 35:5 (count = 0), RegionEntry Segment at 35:11 (count = 0), Skipped -Segment at 36:1 (count = 2), RegionEntry +Segment at 36:1 (count = 1), RegionEntry Segment at 36:2 (count = 0), Skipped From 756b9207952d458ea0683262dd457c618817858c Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Fri, 16 Oct 2020 19:40:57 -0700 Subject: [PATCH 08/18] Improved debugging and formatting options (from env) --- .../src/transform/instrument_coverage.rs | 270 ++++++++++++++---- 1 file changed, 209 insertions(+), 61 deletions(-) diff --git a/compiler/rustc_mir/src/transform/instrument_coverage.rs b/compiler/rustc_mir/src/transform/instrument_coverage.rs index 8aab8b179352f..02f50901a35e4 100644 --- a/compiler/rustc_mir/src/transform/instrument_coverage.rs +++ b/compiler/rustc_mir/src/transform/instrument_coverage.rs @@ -29,9 +29,115 @@ use rustc_span::{BytePos, CharPos, Pos, SourceFile, Span, Symbol, SyntaxContext} use std::cmp::Ordering; use std::ops::{Index, IndexMut}; +use std::lazy::SyncOnceCell; const ID_SEPARATOR: &str = ","; +const RUSTC_COVERAGE_DEBUG_OPTIONS: &str = "RUSTC_COVERAGE_DEBUG_OPTIONS"; + +#[derive(Debug, Clone)] +struct DebugOptions { + allow_unused_expressions: bool, + simplify_expressions: bool, + counter_format: ExpressionFormat, +} + +impl DebugOptions { + fn new() -> Self { + let mut allow_unused_expressions = true; + let mut simplify_expressions = false; + let mut counter_format = ExpressionFormat::default(); + + if let Ok(env_debug_options) = std::env::var(RUSTC_COVERAGE_DEBUG_OPTIONS) { + for setting_str in env_debug_options.replace(" ", "").replace("-", "_").split(",") { + let mut setting = setting_str.splitn(2, "="); + match setting.next() { + Some(option) if option == "allow_unused_expressions" => { + allow_unused_expressions = bool_option_val(option, setting.next()); + debug!("{} env option `allow_unused_expressions` is set to {}", RUSTC_COVERAGE_DEBUG_OPTIONS, allow_unused_expressions); + } + Some(option) if option == "simplify_expressions" => { + simplify_expressions = bool_option_val(option, setting.next()); + debug!("{} env option `simplify_expressions` is set to {}", RUSTC_COVERAGE_DEBUG_OPTIONS, simplify_expressions); + } + Some(option) if option == "counter_format" => { + if let Some(strval) = setting.next() { + counter_format = counter_format_option_val(strval); + debug!("{} env option `counter_format` is set to {:?}", RUSTC_COVERAGE_DEBUG_OPTIONS, counter_format); + } else { + bug!("`{}` option in environment variable {} requires one or more plus-separated choices (a non-empty subset of `id+block+operation`)", option, RUSTC_COVERAGE_DEBUG_OPTIONS); + } + } + Some("") => {}, + Some(invalid) => bug!("Unsupported setting `{}` in environment variable {}", invalid, RUSTC_COVERAGE_DEBUG_OPTIONS), + None => {}, + } + } + } + + Self { + allow_unused_expressions, + simplify_expressions, + counter_format, + } + } +} + +fn debug_options<'a>() -> &'a DebugOptions { + static DEBUG_OPTIONS: SyncOnceCell = SyncOnceCell::new(); + + &DEBUG_OPTIONS.get_or_init(|| DebugOptions::new()) +} + +fn bool_option_val(option: &str, some_strval: Option<&str>) -> bool { + if let Some(val) = some_strval { + if vec!["yes", "y", "on", "true"].contains(&val) { + true + } else if vec!["no", "n", "off", "false"].contains(&val) { + false + } else { + bug!("Unsupported value `{}` for option `{}` in environment variable {}", option, val, RUSTC_COVERAGE_DEBUG_OPTIONS) + } + } else { + true + } +} + +fn counter_format_option_val(strval: &str) -> ExpressionFormat { + let mut counter_format = ExpressionFormat { + id: false, + block: false, + operation: false, + }; + let components = strval.splitn(3, "+"); + for component in components { + match component { + "id" => counter_format.id = true, + "block" => counter_format.block = true, + "operation" => counter_format.operation = true, + _ => bug!("Unsupported counter_format choice `{}` in environment variable {}", component, RUSTC_COVERAGE_DEBUG_OPTIONS), + } + } + counter_format +} + +#[derive(Debug, Clone)] +struct ExpressionFormat { + id: bool, + block: bool, + operation: bool, +} + +impl Default for ExpressionFormat { + fn default() -> Self { + Self { + id: false, + block: true, + operation: false, + } + } +} + /// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected /// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen /// to construct the coverage map. @@ -817,81 +923,114 @@ struct TraversalContext { worklist: Vec, } +#[derive(Debug)] +struct DebugCounter { + counter_kind: CoverageKind, + some_block_label: Option, +} + +impl DebugCounter { + fn new(counter_kind: CoverageKind, some_block_label: Option) -> Self { + Self { + counter_kind, + some_block_label, + } + } +} + struct DebugCounters { - some_expressions: Option>, + some_counters: Option>, } impl DebugCounters { pub fn new() -> Self { Self { - some_expressions: None, + some_counters: None, } } pub fn enable(&mut self) { - self.some_expressions.replace(FxHashMap::default()); + self.some_counters.replace(FxHashMap::default()); } pub fn is_enabled(&mut self) -> bool { - self.some_expressions.is_some() + self.some_counters.is_some() } - pub fn add_expression(&mut self, expression: &CoverageKind, debug_string: String) { - if let Some(expressions) = &mut self.some_expressions { - if let CoverageKind::Expression { id, .. } = *expression { - expressions.insert(id.into(), (expression.clone(), debug_string)).expect_none("attempt to add the same expression to DebugCounters more than once"); - } else { - bug!("the given `CoverageKind` is not an expression: {:?}", expression); - } + pub fn add_counter(&mut self, counter_kind: &CoverageKind, some_block_label: Option) { + if let Some(counters) = &mut self.some_counters { + let id: ExpressionOperandId = match *counter_kind { + CoverageKind::Counter { id, .. } => id.into(), + | CoverageKind::Expression { id, .. } => id.into(), + _ => bug!("the given `CoverageKind` is not an counter or expression: {:?}", counter_kind), + }; + counters.insert(id.into(), DebugCounter::new(counter_kind.clone(), some_block_label)).expect_none("attempt to add the same counter_kind to DebugCounters more than once"); } } - pub fn debug_string(&self, operand: ExpressionOperandId) -> String { - if let Some(expressions) = &self.some_expressions { - if let Some((_, debug_string)) = expressions.get(&operand) { - return debug_string.clone() - } - } - String::new() + pub fn some_block_label(&self, operand: ExpressionOperandId) -> Option<&String> { + self.some_counters.as_ref().map_or(None, |counters| counters.get(&operand).map_or(None, |debug_counter| debug_counter.some_block_label.as_ref())) } pub fn format_counter(&self, counter_kind: &CoverageKind) -> String { match *counter_kind { - CoverageKind::Counter { id, .. } => format!("Counter(#{})", id.index()), - CoverageKind::Expression { .. } => format!("Expression({})", self.format_expression(counter_kind)), + CoverageKind::Counter { .. } => format!("Counter({})", self.format_counter_kind(counter_kind)), + CoverageKind::Expression { .. } => format!("Expression({})", self.format_counter_kind(counter_kind)), CoverageKind::Unreachable { .. } => "Unreachable".to_owned(), } } - fn format_expression(&self, expression: &CoverageKind) -> String { - if let CoverageKind::Expression { id, lhs, op, rhs } = *expression { - format!( - "{}{} {} {}", - if self.some_expressions.is_some() { - String::new() + fn format_counter_kind(&self, counter_kind: &CoverageKind) -> String { + let counter_format = &debug_options().counter_format; + if let CoverageKind::Expression { id, lhs, op, rhs } = *counter_kind { + if counter_format.operation { + return format!( + "{}{} {} {}", + if counter_format.id || self.some_counters.is_none() { + format!("#{} = ", id.index() ) + } else { + String::new() + }, + self.format_operand(lhs), + if op == Op::Add { "+" } else { "-" }, + self.format_operand(rhs), + ); + } + } + + let id: ExpressionOperandId = match *counter_kind { + CoverageKind::Counter { id, .. } => id.into(), + | CoverageKind::Expression { id, .. } => id.into(), + _ => bug!("the given `CoverageKind` is not an counter or expression: {:?}", counter_kind), + }; + if self.some_counters.is_some() && (counter_format.block || !counter_format.id) { + let counters = self.some_counters.as_ref().unwrap(); + if let Some(DebugCounter { some_block_label: Some(block_label), .. }) = counters.get(&id.into()) { + return if counter_format.id { + format!("{}#{}", block_label, id.index()) } else { - format!("#{} = ", id.index() ) - }, - self.format_operand(lhs), - if op == Op::Add { "+" } else { "-" }, - self.format_operand(rhs), - ) - } else { - bug!("format_expression called with {:?}", expression); + format!("{}", block_label) + } + } } + format!("#{}", id.index()) } fn format_operand(&self, operand: ExpressionOperandId) -> String { if operand.index() == 0 { return String::from("0"); } - if let Some(expressions) = &self.some_expressions { - if let Some((expression, debug_string)) = expressions.get(&operand) { - if debug_string.is_empty() { - return format!("({})", self.format_expression(expression)); - } else { - return format!("{}:({})", debug_string, self.format_expression(expression)); + if let Some(counters) = &self.some_counters { + if let Some(DebugCounter { counter_kind, some_block_label }) = counters.get(&operand) { + if let CoverageKind::Expression { .. } = counter_kind { + if let Some(block_label) = some_block_label { + if debug_options().counter_format.block { + return format!("{}:({})", block_label, self.format_counter_kind(counter_kind)); + } + } + return format!("({})", self.format_counter_kind(counter_kind)); } + return format!("{}", self.format_counter_kind(counter_kind)); } } format!("#{}", operand.index().to_string()) @@ -991,9 +1130,10 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { if level_enabled!(tracing::Level::DEBUG) || dump_graphviz { debug_used_expression_operands = Some(FxHashMap::default()); debug_unused_expressions = Some(Vec::new()); - - const SIMPLIFY_EXPRESSIONS: bool = false; - if SIMPLIFY_EXPRESSIONS { +// TODO(richkadel): remove me: +// const SIMPLIFY_EXPRESSIONS: bool = false; +// if SIMPLIFY_EXPRESSIONS { + if debug_options().simplify_expressions { self.debug_expressions_cache.replace(FxHashMap::default()); } // CAUTION! The `SIMPLIFY_EXPRESSIONS` option is only helpful for some debugging @@ -1301,8 +1441,10 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // even when `SIMPLIFY_EXPRESSIONS` is disabled. It's not yet clear what causes this, or // if it should be allowed in the long term, but as long as the coverage capability // still works, generate warning messages only, for now. - const ALLOW_UNUSED_EXPRESSIONS: bool = true; - if self.debug_expressions_cache.is_some() || ALLOW_UNUSED_EXPRESSIONS { +// TODO(richkadel): remove: +// const ALLOW_UNUSED_EXPRESSIONS: bool = true; +// if self.debug_expressions_cache.is_some() || ALLOW_UNUSED_EXPRESSIONS { + if self.debug_expressions_cache.is_some() || debug_options().allow_unused_expressions { // Note, the debugging const `SIMPLIFY_EXPRESSIONS`, which initializes the // `debug_expressions_cache` can cause some counters to become unused, and // is not a bug. @@ -1568,7 +1710,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { branch_counter_operand, Op::Add, sumup_counter_operand, - || String::new(), + || None, ); debug!(" new intermediate expression: {}", self.format_counter(&intermediate_expression)); let intermediate_expression_operand = intermediate_expression.as_operand_id(); @@ -1585,13 +1727,13 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { branching_counter_operand, Op::Subtract, sumup_counter_operand, - || { + || Some( if multiple_incoming_edges { format!("{:?}->{:?}", branching_bcb, expression_branch) } else { format!("{:?}", expression_branch) } - } + ) ); if multiple_incoming_edges { debug!("Edge {:?}->{:?} gets an expression: {}", branching_bcb, expression_branch, self.format_counter(&expression)); @@ -1702,7 +1844,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { sumup_edge_counter_operand, Op::Add, edge_counter_operand, - || String::new(), + || None, ); debug!(" new intermediate expression: {}", self.format_counter(&intermediate_expression)); let intermediate_expression_operand = intermediate_expression.as_operand_id(); @@ -1714,12 +1856,12 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { first_edge_counter_operand, Op::Add, some_sumup_edge_counter_operand.unwrap(), - || format!("{:?}", bcb) + || Some(format!("{:?}", bcb)) ); debug!(" {:?} gets a new counter (sum of predecessor counters): {}", bcb, self.format_counter(&counter_kind)); self.basic_coverage_blocks[bcb].set_counter(counter_kind) } else { - let counter_kind = self.make_counter(); + let counter_kind = self.make_counter(|| Some(format!("{:?}", bcb))); debug!(" {:?} gets a new counter: {}", bcb, self.format_counter(&counter_kind)); self.basic_coverage_blocks[bcb].set_counter(counter_kind) } @@ -1733,7 +1875,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { debug!(" Edge {:?}->{:?} already has a counter: {}", from_bcb, to_bcb, self.format_counter(counter_kind)); counter_kind.as_operand_id() } else { - let counter_kind = self.make_counter(); + let counter_kind = self.make_counter(|| Some(format!("{:?}->{:?}", from_bcb, to_bcb))); debug!(" Edge {:?}->{:?} gets a new counter: {}", from_bcb, to_bcb, self.format_counter(&counter_kind)); self.basic_coverage_blocks[to_bcb].set_edge_counter_from(from_bcb, counter_kind) } @@ -1801,11 +1943,17 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // // What about nexted loops and jumping out of one or more of them at a time? - fn make_counter(&mut self) -> CoverageKind { - CoverageKind::Counter { + fn make_counter(&mut self, block_label_fn: F) -> CoverageKind + where F: Fn() -> Option + { + let counter = CoverageKind::Counter { function_source_hash: self.function_source_hash(), id: self.next_counter(), + }; + if self.debug_counters.is_enabled() { + self.debug_counters.add_counter(&counter, (block_label_fn)()); } + counter } fn make_expression( @@ -1813,9 +1961,9 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { mut lhs: ExpressionOperandId, op: Op, mut rhs: ExpressionOperandId, - debug_string_fn: F, + block_label_fn: F, ) -> CoverageKind - where F: Fn() -> String + where F: Fn() -> Option { if let Some(expressions_cache) = self.debug_expressions_cache.as_ref() { if let Some(CoverageKind::Expression { lhs: lhs_lhs, op, rhs: lhs_rhs, .. } ) = expressions_cache.get(&lhs) { @@ -1853,7 +2001,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { expressions_cache.insert(id.into(), expression.clone()); } if self.debug_counters.is_enabled() { - self.debug_counters.add_expression(&expression, (debug_string_fn)()); + self.debug_counters.add_counter(&expression, (block_label_fn)()); } expression } @@ -1880,12 +2028,12 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { let new_expression = expression.clone(); self.as_duplicate_expression(new_expression) } else { - let debug_string = if self.debug_counters.is_enabled() { - self.debug_counters.debug_string(counter_operand) + let some_block_label = if self.debug_counters.is_enabled() { + self.debug_counters.some_block_label(counter_operand).cloned() } else { - String::new() + None }; - self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO, || debug_string.clone()) + self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO, || some_block_label.clone()) } } From cc00eb0d3a2c90ea35562e49bf9aa749a5942c98 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Fri, 16 Oct 2020 22:21:22 -0700 Subject: [PATCH 09/18] Don't automatically add counters to BCBs without CoverageSpans They may still get counters but only if there are dependencies from other BCBs that have spans, I think. At least one test, "various_conditions.rs", seems to have simplified counters as a result of this change, for whatever reason. --- .../src/transform/instrument_coverage.rs | 266 +++++++++--------- ...w_coverage_counters.various_conditions.txt | 100 +++---- ...w_coverage_counters.various_conditions.txt | 100 +++---- 3 files changed, 236 insertions(+), 230 deletions(-) diff --git a/compiler/rustc_mir/src/transform/instrument_coverage.rs b/compiler/rustc_mir/src/transform/instrument_coverage.rs index 02f50901a35e4..c2117b53f3e2c 100644 --- a/compiler/rustc_mir/src/transform/instrument_coverage.rs +++ b/compiler/rustc_mir/src/transform/instrument_coverage.rs @@ -35,6 +35,11 @@ const ID_SEPARATOR: &str = ","; const RUSTC_COVERAGE_DEBUG_OPTIONS: &str = "RUSTC_COVERAGE_DEBUG_OPTIONS"; +/// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected +/// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen +/// to construct the coverage map. +pub struct InstrumentCoverage; + #[derive(Debug, Clone)] struct DebugOptions { allow_unused_expressions: bool, @@ -138,10 +143,119 @@ impl Default for ExpressionFormat { } } -/// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected -/// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen -/// to construct the coverage map. -pub struct InstrumentCoverage; +#[derive(Debug)] +struct DebugCounter { + counter_kind: CoverageKind, + some_block_label: Option, +} + +impl DebugCounter { + fn new(counter_kind: CoverageKind, some_block_label: Option) -> Self { + Self { + counter_kind, + some_block_label, + } + } +} + +struct DebugCounters { + some_counters: Option>, +} + +impl DebugCounters { + pub fn new() -> Self { + Self { + some_counters: None, + } + } + + pub fn enable(&mut self) { + self.some_counters.replace(FxHashMap::default()); + } + + pub fn is_enabled(&mut self) -> bool { + self.some_counters.is_some() + } + + pub fn add_counter(&mut self, counter_kind: &CoverageKind, some_block_label: Option) { + if let Some(counters) = &mut self.some_counters { + let id: ExpressionOperandId = match *counter_kind { + CoverageKind::Counter { id, .. } => id.into(), + | CoverageKind::Expression { id, .. } => id.into(), + _ => bug!("the given `CoverageKind` is not an counter or expression: {:?}", counter_kind), + }; + counters.insert(id.into(), DebugCounter::new(counter_kind.clone(), some_block_label)).expect_none("attempt to add the same counter_kind to DebugCounters more than once"); + } + } + + pub fn some_block_label(&self, operand: ExpressionOperandId) -> Option<&String> { + self.some_counters.as_ref().map_or(None, |counters| counters.get(&operand).map_or(None, |debug_counter| debug_counter.some_block_label.as_ref())) + } + + pub fn format_counter(&self, counter_kind: &CoverageKind) -> String { + match *counter_kind { + CoverageKind::Counter { .. } => format!("Counter({})", self.format_counter_kind(counter_kind)), + CoverageKind::Expression { .. } => format!("Expression({})", self.format_counter_kind(counter_kind)), + CoverageKind::Unreachable { .. } => "Unreachable".to_owned(), + } + } + + fn format_counter_kind(&self, counter_kind: &CoverageKind) -> String { + let counter_format = &debug_options().counter_format; + if let CoverageKind::Expression { id, lhs, op, rhs } = *counter_kind { + if counter_format.operation { + return format!( + "{}{} {} {}", + if counter_format.id || self.some_counters.is_none() { + format!("#{} = ", id.index() ) + } else { + String::new() + }, + self.format_operand(lhs), + if op == Op::Add { "+" } else { "-" }, + self.format_operand(rhs), + ); + } + } + + let id: ExpressionOperandId = match *counter_kind { + CoverageKind::Counter { id, .. } => id.into(), + | CoverageKind::Expression { id, .. } => id.into(), + _ => bug!("the given `CoverageKind` is not an counter or expression: {:?}", counter_kind), + }; + if self.some_counters.is_some() && (counter_format.block || !counter_format.id) { + let counters = self.some_counters.as_ref().unwrap(); + if let Some(DebugCounter { some_block_label: Some(block_label), .. }) = counters.get(&id.into()) { + return if counter_format.id { + format!("{}#{}", block_label, id.index()) + } else { + format!("{}", block_label) + } + } + } + format!("#{}", id.index()) + } + + fn format_operand(&self, operand: ExpressionOperandId) -> String { + if operand.index() == 0 { + return String::from("0"); + } + if let Some(counters) = &self.some_counters { + if let Some(DebugCounter { counter_kind, some_block_label }) = counters.get(&operand) { + if let CoverageKind::Expression { .. } = counter_kind { + if let Some(block_label) = some_block_label { + if debug_options().counter_format.block { + return format!("{}:({})", block_label, self.format_counter_kind(counter_kind)); + } + } + return format!("({})", self.format_counter_kind(counter_kind)); + } + return format!("{}", self.format_counter_kind(counter_kind)); + } + } + format!("#{}", operand.index().to_string()) + } +} /// The `query` provider for `CoverageInfo`, requested by `codegen_coverage()` (to inject each /// counter) and `FunctionCoverage::new()` (to extract the coverage map metadata from the MIR). @@ -923,120 +1037,6 @@ struct TraversalContext { worklist: Vec, } -#[derive(Debug)] -struct DebugCounter { - counter_kind: CoverageKind, - some_block_label: Option, -} - -impl DebugCounter { - fn new(counter_kind: CoverageKind, some_block_label: Option) -> Self { - Self { - counter_kind, - some_block_label, - } - } -} - -struct DebugCounters { - some_counters: Option>, -} - -impl DebugCounters { - pub fn new() -> Self { - Self { - some_counters: None, - } - } - - pub fn enable(&mut self) { - self.some_counters.replace(FxHashMap::default()); - } - - pub fn is_enabled(&mut self) -> bool { - self.some_counters.is_some() - } - - pub fn add_counter(&mut self, counter_kind: &CoverageKind, some_block_label: Option) { - if let Some(counters) = &mut self.some_counters { - let id: ExpressionOperandId = match *counter_kind { - CoverageKind::Counter { id, .. } => id.into(), - | CoverageKind::Expression { id, .. } => id.into(), - _ => bug!("the given `CoverageKind` is not an counter or expression: {:?}", counter_kind), - }; - counters.insert(id.into(), DebugCounter::new(counter_kind.clone(), some_block_label)).expect_none("attempt to add the same counter_kind to DebugCounters more than once"); - } - } - - pub fn some_block_label(&self, operand: ExpressionOperandId) -> Option<&String> { - self.some_counters.as_ref().map_or(None, |counters| counters.get(&operand).map_or(None, |debug_counter| debug_counter.some_block_label.as_ref())) - } - - pub fn format_counter(&self, counter_kind: &CoverageKind) -> String { - match *counter_kind { - CoverageKind::Counter { .. } => format!("Counter({})", self.format_counter_kind(counter_kind)), - CoverageKind::Expression { .. } => format!("Expression({})", self.format_counter_kind(counter_kind)), - CoverageKind::Unreachable { .. } => "Unreachable".to_owned(), - } - } - - fn format_counter_kind(&self, counter_kind: &CoverageKind) -> String { - let counter_format = &debug_options().counter_format; - if let CoverageKind::Expression { id, lhs, op, rhs } = *counter_kind { - if counter_format.operation { - return format!( - "{}{} {} {}", - if counter_format.id || self.some_counters.is_none() { - format!("#{} = ", id.index() ) - } else { - String::new() - }, - self.format_operand(lhs), - if op == Op::Add { "+" } else { "-" }, - self.format_operand(rhs), - ); - } - } - - let id: ExpressionOperandId = match *counter_kind { - CoverageKind::Counter { id, .. } => id.into(), - | CoverageKind::Expression { id, .. } => id.into(), - _ => bug!("the given `CoverageKind` is not an counter or expression: {:?}", counter_kind), - }; - if self.some_counters.is_some() && (counter_format.block || !counter_format.id) { - let counters = self.some_counters.as_ref().unwrap(); - if let Some(DebugCounter { some_block_label: Some(block_label), .. }) = counters.get(&id.into()) { - return if counter_format.id { - format!("{}#{}", block_label, id.index()) - } else { - format!("{}", block_label) - } - } - } - format!("#{}", id.index()) - } - - fn format_operand(&self, operand: ExpressionOperandId) -> String { - if operand.index() == 0 { - return String::from("0"); - } - if let Some(counters) = &self.some_counters { - if let Some(DebugCounter { counter_kind, some_block_label }) = counters.get(&operand) { - if let CoverageKind::Expression { .. } = counter_kind { - if let Some(block_label) = some_block_label { - if debug_options().counter_format.block { - return format!("{}:({})", block_label, self.format_counter_kind(counter_kind)); - } - } - return format!("({})", self.format_counter_kind(counter_kind)); - } - return format!("{}", self.format_counter_kind(counter_kind)); - } - } - format!("#{}", operand.index().to_string()) - } -} - struct Instrumentor<'a, 'tcx> { pass_name: &'a str, tcx: TyCtxt<'tcx>, @@ -1130,13 +1130,10 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { if level_enabled!(tracing::Level::DEBUG) || dump_graphviz { debug_used_expression_operands = Some(FxHashMap::default()); debug_unused_expressions = Some(Vec::new()); -// TODO(richkadel): remove me: -// const SIMPLIFY_EXPRESSIONS: bool = false; -// if SIMPLIFY_EXPRESSIONS { if debug_options().simplify_expressions { self.debug_expressions_cache.replace(FxHashMap::default()); } - // CAUTION! The `SIMPLIFY_EXPRESSIONS` option is only helpful for some debugging + // CAUTION! The `simplify_expressions` option is only helpful for some debugging // situations and it can change the generated MIR `Coverage` statements (resulting in // differences in behavior when enabled, under `DEBUG`, compared to normal operation and // testing). @@ -1165,10 +1162,14 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { debug_edge_to_counter = Some(FxHashMap::default()); } + let mut bcbs_with_coverage = BitSet::new_empty(self.basic_coverage_blocks.num_nodes()); + for covspan in &coverage_spans { + bcbs_with_coverage.insert(covspan.bcb); + } + // Analyze the coverage graph (aka, BCB control flow graph), and inject expression-optimized // counters. - - self.make_bcb_counters(&mut collect_intermediate_expressions); + self.make_bcb_counters(bcbs_with_coverage, &mut collect_intermediate_expressions); // If debugging, add any intermediate expressions (which are not associated with any BCB) to // the `debug_used_expression_operands` map. @@ -1438,14 +1439,11 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // FIXME(richkadel): Determine if unused expressions can and should be prevented, and // if so, it would be a `bug!` if encountered in the future. At the present time, // however we do sometimes encounter unused expressions (in only a subset of test cases) - // even when `SIMPLIFY_EXPRESSIONS` is disabled. It's not yet clear what causes this, or + // even when `simplify_expressions` is disabled. It's not yet clear what causes this, or // if it should be allowed in the long term, but as long as the coverage capability // still works, generate warning messages only, for now. -// TODO(richkadel): remove: -// const ALLOW_UNUSED_EXPRESSIONS: bool = true; -// if self.debug_expressions_cache.is_some() || ALLOW_UNUSED_EXPRESSIONS { if self.debug_expressions_cache.is_some() || debug_options().allow_unused_expressions { - // Note, the debugging const `SIMPLIFY_EXPRESSIONS`, which initializes the + // Note, the debugging option `simplify_expressions`, which initializes the // `debug_expressions_cache` can cause some counters to become unused, and // is not a bug. // @@ -1518,7 +1516,11 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { /// /// Returns non-code-span expressions created to represent intermediate values (if required), /// such as to add two counters so the result can be subtracted from another counter. - fn make_bcb_counters(&mut self, collect_intermediate_expressions: &mut Vec) { + fn make_bcb_counters( + &mut self, + bcbs_with_coverage: BitSet, + collect_intermediate_expressions: &mut Vec, + ) { debug!("make_bcb_counters(): adding a counter or expression to each BasicCoverageBlock"); let num_bcbs = self.basic_coverage_blocks.num_nodes(); let mut backedges = IndexVec::from_elem_n( @@ -1608,6 +1610,10 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } } + if !bcbs_with_coverage.contains(bcb) { + continue; + } + let bcb_counter_operand = self.get_or_make_counter_operand(bcb, collect_intermediate_expressions); let needs_branch_counters = { diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.various_conditions.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.various_conditions.txt index b11a2f1677eaf..fbc29873312fb 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.various_conditions.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.various_conditions.txt @@ -13,13 +13,13 @@ Counter in file 0 14:42 -> 14:43, ((#5 - #13) - #14) Counter in file 0 14:42 -> 14:43, (#13 + #14) Counter in file 0 14:42 -> 14:42, #13 Counter in file 0 14:42 -> 14:42, #14 -Counter in file 0 14:46 -> 14:60, (((#13 + #14) + ((#5 - #13) - #14)) - #17) -Counter in file 0 14:60 -> 14:61, ((((#13 + #14) + ((#5 - #13) - #14)) - #17) - #18) +Counter in file 0 14:46 -> 14:60, #21 +Counter in file 0 14:60 -> 14:61, (#21 - #18) Counter in file 0 14:60 -> 14:61, (#17 + #18) Counter in file 0 14:60 -> 14:60, #17 Counter in file 0 14:60 -> 14:60, #18 -Counter in file 0 14:61 -> 16:10, #21 -Counter in file 0 16:10 -> 16:11, (((#17 + #18) + ((((#13 + #14) + ((#5 - #13) - #14)) - #17) - #18)) - #21) +Counter in file 0 14:61 -> 16:10, #22 +Counter in file 0 16:10 -> 16:11, #23 Counter in file 0 17:9 -> 18:18, #11 Counter in file 0 20:9 -> 20:15, (((#2 + 0) - #3) - #5) Counter in file 0 23:9 -> 23:26, ((#4 + #11) + 0) @@ -30,61 +30,61 @@ Counter in file 0 29:9 -> 29:23, #16 Counter in file 0 29:23 -> 29:23, #15 Counter in file 0 30:15 -> 30:28, ((#12 + 0) - #15) Counter in file 0 31:12 -> 31:25, (((#12 + 0) - #15) - #8) -Counter in file 0 31:29 -> 31:42, ((((#12 + 0) - #15) - #8) - #22) -Counter in file 0 31:42 -> 31:43, (#22 + #23) -Counter in file 0 31:42 -> 31:43, (((((#12 + 0) - #15) - #8) - #22) - #23) -Counter in file 0 31:42 -> 31:42, #22 -Counter in file 0 31:42 -> 31:42, #23 -Counter in file 0 31:46 -> 31:60, (((#22 + #23) + (((((#12 + 0) - #15) - #8) - #22) - #23)) - #26) -Counter in file 0 31:60 -> 31:61, (#26 + #27) -Counter in file 0 31:60 -> 31:61, ((((#22 + #23) + (((((#12 + 0) - #15) - #8) - #22) - #23)) - #26) - #27) -Counter in file 0 31:60 -> 31:60, #27 -Counter in file 0 31:60 -> 31:60, #26 -Counter in file 0 31:61 -> 33:10, #30 -Counter in file 0 33:10 -> 33:11, (((#26 + #27) + ((((#22 + #23) + (((((#12 + 0) - #15) - #8) - #22) - #23)) - #26) - #27)) - #30) +Counter in file 0 31:29 -> 31:42, ((((#12 + 0) - #15) - #8) - #24) +Counter in file 0 31:42 -> 31:43, (#24 + #25) +Counter in file 0 31:42 -> 31:43, (((((#12 + 0) - #15) - #8) - #24) - #25) +Counter in file 0 31:42 -> 31:42, #25 +Counter in file 0 31:42 -> 31:42, #24 +Counter in file 0 31:46 -> 31:60, #32 +Counter in file 0 31:60 -> 31:61, (#28 + #29) +Counter in file 0 31:60 -> 31:61, (#32 - #29) +Counter in file 0 31:60 -> 31:60, #28 +Counter in file 0 31:60 -> 31:60, #29 +Counter in file 0 31:61 -> 33:10, #33 +Counter in file 0 33:10 -> 33:11, #34 Counter in file 0 34:9 -> 34:23, #19 Counter in file 0 36:9 -> 36:15, #8 Counter in file 0 39:9 -> 39:26, (#16 + #19) Counter in file 0 40:8 -> 40:12, ((#16 + #19) + 0) Counter in file 0 40:13 -> 42:6, #20 -Counter in file 0 44:9 -> 44:10, (#25 + #28) +Counter in file 0 44:9 -> 44:10, (#27 + #30) Counter in file 0 44:16 -> 44:29, (#20 + 0) -Counter in file 0 45:9 -> 45:23, #25 -Counter in file 0 45:23 -> 45:23, #24 -Counter in file 0 46:15 -> 46:28, ((#20 + 0) - #24) -Counter in file 0 47:12 -> 47:25, (((#20 + 0) - #24) - #7) -Counter in file 0 47:29 -> 47:42, ((((#20 + 0) - #24) - #7) - #31) -Counter in file 0 47:42 -> 47:43, (((((#20 + 0) - #24) - #7) - #31) - #32) -Counter in file 0 47:42 -> 47:43, (#31 + #32) -Counter in file 0 47:42 -> 47:42, #32 -Counter in file 0 47:42 -> 47:42, #31 -Counter in file 0 47:46 -> 47:60, (((#31 + #32) + (((((#20 + 0) - #24) - #7) - #31) - #32)) - #33) -Counter in file 0 47:60 -> 47:61, (#33 + #34) -Counter in file 0 47:60 -> 47:61, ((((#31 + #32) + (((((#20 + 0) - #24) - #7) - #31) - #32)) - #33) - #34) -Counter in file 0 47:60 -> 47:60, #34 -Counter in file 0 47:60 -> 47:60, #33 -Counter in file 0 47:61 -> 49:10, #37 -Counter in file 0 49:10 -> 49:11, (((#33 + #34) + ((((#31 + #32) + (((((#20 + 0) - #24) - #7) - #31) - #32)) - #33) - #34)) - #37) -Counter in file 0 50:9 -> 50:23, #28 +Counter in file 0 45:9 -> 45:23, #27 +Counter in file 0 45:23 -> 45:23, #26 +Counter in file 0 46:15 -> 46:28, ((#20 + 0) - #26) +Counter in file 0 47:12 -> 47:25, (((#20 + 0) - #26) - #7) +Counter in file 0 47:29 -> 47:42, ((((#20 + 0) - #26) - #7) - #35) +Counter in file 0 47:42 -> 47:43, (((((#20 + 0) - #26) - #7) - #35) - #36) +Counter in file 0 47:42 -> 47:43, (#35 + #36) +Counter in file 0 47:42 -> 47:42, #35 +Counter in file 0 47:42 -> 47:42, #36 +Counter in file 0 47:46 -> 47:60, #41 +Counter in file 0 47:60 -> 47:61, (#41 - #38) +Counter in file 0 47:60 -> 47:61, (#37 + #38) +Counter in file 0 47:60 -> 47:60, #38 +Counter in file 0 47:60 -> 47:60, #37 +Counter in file 0 47:61 -> 49:10, #42 +Counter in file 0 49:10 -> 49:11, #43 +Counter in file 0 50:9 -> 50:23, #30 Counter in file 0 52:13 -> 54:15, #7 Counter in file 0 57:9 -> 57:10, (#9 + #10) -Counter in file 0 57:16 -> 57:29, ((#25 + #28) + 0) +Counter in file 0 57:16 -> 57:29, ((#27 + #30) + 0) Counter in file 0 58:9 -> 58:23, #9 -Counter in file 0 58:23 -> 58:23, #29 -Counter in file 0 59:15 -> 59:28, ((#25 + #28) - #29) -Counter in file 0 60:12 -> 60:25, (((#25 + #28) - #29) - #6) -Counter in file 0 60:29 -> 60:42, ((((#25 + #28) - #29) - #6) - #35) -Counter in file 0 60:42 -> 60:43, (#35 + #36) -Counter in file 0 60:42 -> 60:43, (((((#25 + #28) - #29) - #6) - #35) - #36) -Counter in file 0 60:42 -> 60:42, #35 -Counter in file 0 60:42 -> 60:42, #36 -Counter in file 0 60:46 -> 60:60, (((#35 + #36) + (((((#25 + #28) - #29) - #6) - #35) - #36)) - #38) -Counter in file 0 60:60 -> 60:61, (#38 + #39) -Counter in file 0 60:60 -> 60:61, ((((#35 + #36) + (((((#25 + #28) - #29) - #6) - #35) - #36)) - #38) - #39) -Counter in file 0 60:60 -> 60:60, #38 -Counter in file 0 60:60 -> 60:60, #39 -Counter in file 0 60:61 -> 62:10, #40 -Counter in file 0 62:10 -> 62:11, (((#38 + #39) + ((((#35 + #36) + (((((#25 + #28) - #29) - #6) - #35) - #36)) - #38) - #39)) - #40) +Counter in file 0 58:23 -> 58:23, #31 +Counter in file 0 59:15 -> 59:28, ((#27 + #30) - #31) +Counter in file 0 60:12 -> 60:25, (((#27 + #30) - #31) - #6) +Counter in file 0 60:29 -> 60:42, ((((#27 + #30) - #31) - #6) - #39) +Counter in file 0 60:42 -> 60:43, (#39 + #40) +Counter in file 0 60:42 -> 60:43, (((((#27 + #30) - #31) - #6) - #39) - #40) +Counter in file 0 60:42 -> 60:42, #40 +Counter in file 0 60:42 -> 60:42, #39 +Counter in file 0 60:46 -> 60:60, #46 +Counter in file 0 60:60 -> 60:61, (#44 + #45) +Counter in file 0 60:60 -> 60:61, (#46 - #45) +Counter in file 0 60:60 -> 60:60, #44 +Counter in file 0 60:60 -> 60:60, #45 +Counter in file 0 60:61 -> 62:10, #47 +Counter in file 0 62:10 -> 62:11, #48 Counter in file 0 63:9 -> 63:23, #10 Counter in file 0 65:9 -> 65:15, #6 Counter in file 0 67:1 -> 67:2, ((#9 + #10) + (((#6 + #7) + #8) + (((#2 + 0) - #3) - #5))) diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.various_conditions.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.various_conditions.txt index 0c551639a8553..5b94dd3a819f3 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.various_conditions.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.various_conditions.txt @@ -13,13 +13,13 @@ Counter in file 0 14:42 -> 14:43, ((#5 - #13) - #14) Counter in file 0 14:42 -> 14:43, (#13 + #14) Counter in file 0 14:42 -> 14:42, #13 Counter in file 0 14:42 -> 14:42, #14 -Counter in file 0 14:46 -> 14:60, (((#13 + #14) + ((#5 - #13) - #14)) - #17) -Counter in file 0 14:60 -> 14:61, ((((#13 + #14) + ((#5 - #13) - #14)) - #17) - #18) +Counter in file 0 14:46 -> 14:60, #21 +Counter in file 0 14:60 -> 14:61, (#21 - #18) Counter in file 0 14:60 -> 14:61, (#17 + #18) Counter in file 0 14:60 -> 14:60, #17 Counter in file 0 14:60 -> 14:60, #18 -Counter in file 0 14:61 -> 16:10, #21 -Counter in file 0 16:10 -> 16:11, (((#17 + #18) + ((((#13 + #14) + ((#5 - #13) - #14)) - #17) - #18)) - #21) +Counter in file 0 14:61 -> 16:10, #22 +Counter in file 0 16:10 -> 16:11, #23 Counter in file 0 17:9 -> 18:18, #11 Counter in file 0 20:9 -> 20:15, (((#2 + 0) - #3) - #5) Counter in file 0 23:9 -> 23:26, ((#4 + #11) + 0) @@ -30,61 +30,61 @@ Counter in file 0 29:9 -> 29:23, #16 Counter in file 0 29:23 -> 29:23, #15 Counter in file 0 30:15 -> 30:28, ((#12 + 0) - #15) Counter in file 0 31:12 -> 31:25, (((#12 + 0) - #15) - #8) -Counter in file 0 31:29 -> 31:42, ((((#12 + 0) - #15) - #8) - #22) -Counter in file 0 31:42 -> 31:43, (#22 + #23) -Counter in file 0 31:42 -> 31:43, (((((#12 + 0) - #15) - #8) - #22) - #23) -Counter in file 0 31:42 -> 31:42, #22 -Counter in file 0 31:42 -> 31:42, #23 -Counter in file 0 31:46 -> 31:60, (((#22 + #23) + (((((#12 + 0) - #15) - #8) - #22) - #23)) - #26) -Counter in file 0 31:60 -> 31:61, (#26 + #27) -Counter in file 0 31:60 -> 31:61, ((((#22 + #23) + (((((#12 + 0) - #15) - #8) - #22) - #23)) - #26) - #27) -Counter in file 0 31:60 -> 31:60, #27 -Counter in file 0 31:60 -> 31:60, #26 -Counter in file 0 31:61 -> 33:10, #30 -Counter in file 0 33:10 -> 33:11, (((#26 + #27) + ((((#22 + #23) + (((((#12 + 0) - #15) - #8) - #22) - #23)) - #26) - #27)) - #30) +Counter in file 0 31:29 -> 31:42, ((((#12 + 0) - #15) - #8) - #24) +Counter in file 0 31:42 -> 31:43, (#24 + #25) +Counter in file 0 31:42 -> 31:43, (((((#12 + 0) - #15) - #8) - #24) - #25) +Counter in file 0 31:42 -> 31:42, #25 +Counter in file 0 31:42 -> 31:42, #24 +Counter in file 0 31:46 -> 31:60, #32 +Counter in file 0 31:60 -> 31:61, (#28 + #29) +Counter in file 0 31:60 -> 31:61, (#32 - #29) +Counter in file 0 31:60 -> 31:60, #28 +Counter in file 0 31:60 -> 31:60, #29 +Counter in file 0 31:61 -> 33:10, #33 +Counter in file 0 33:10 -> 33:11, #34 Counter in file 0 34:9 -> 34:23, #19 Counter in file 0 36:9 -> 36:15, #8 Counter in file 0 39:9 -> 39:26, (#16 + #19) Counter in file 0 40:8 -> 40:12, ((#16 + #19) + 0) Counter in file 0 40:13 -> 42:6, #20 -Counter in file 0 44:9 -> 44:10, (#25 + #28) +Counter in file 0 44:9 -> 44:10, (#27 + #30) Counter in file 0 44:16 -> 44:29, (#20 + 0) -Counter in file 0 45:9 -> 45:23, #25 -Counter in file 0 45:23 -> 45:23, #24 -Counter in file 0 46:15 -> 46:28, ((#20 + 0) - #24) -Counter in file 0 47:12 -> 47:25, (((#20 + 0) - #24) - #7) -Counter in file 0 47:29 -> 47:42, ((((#20 + 0) - #24) - #7) - #31) -Counter in file 0 47:42 -> 47:43, (((((#20 + 0) - #24) - #7) - #31) - #32) -Counter in file 0 47:42 -> 47:43, (#31 + #32) -Counter in file 0 47:42 -> 47:42, #32 -Counter in file 0 47:42 -> 47:42, #31 -Counter in file 0 47:46 -> 47:60, (((#31 + #32) + (((((#20 + 0) - #24) - #7) - #31) - #32)) - #33) -Counter in file 0 47:60 -> 47:61, (#33 + #34) -Counter in file 0 47:60 -> 47:61, ((((#31 + #32) + (((((#20 + 0) - #24) - #7) - #31) - #32)) - #33) - #34) -Counter in file 0 47:60 -> 47:60, #34 -Counter in file 0 47:60 -> 47:60, #33 -Counter in file 0 47:61 -> 49:10, #37 -Counter in file 0 49:10 -> 49:11, (((#33 + #34) + ((((#31 + #32) + (((((#20 + 0) - #24) - #7) - #31) - #32)) - #33) - #34)) - #37) -Counter in file 0 50:9 -> 50:23, #28 +Counter in file 0 45:9 -> 45:23, #27 +Counter in file 0 45:23 -> 45:23, #26 +Counter in file 0 46:15 -> 46:28, ((#20 + 0) - #26) +Counter in file 0 47:12 -> 47:25, (((#20 + 0) - #26) - #7) +Counter in file 0 47:29 -> 47:42, ((((#20 + 0) - #26) - #7) - #35) +Counter in file 0 47:42 -> 47:43, (((((#20 + 0) - #26) - #7) - #35) - #36) +Counter in file 0 47:42 -> 47:43, (#35 + #36) +Counter in file 0 47:42 -> 47:42, #35 +Counter in file 0 47:42 -> 47:42, #36 +Counter in file 0 47:46 -> 47:60, #41 +Counter in file 0 47:60 -> 47:61, (#41 - #38) +Counter in file 0 47:60 -> 47:61, (#37 + #38) +Counter in file 0 47:60 -> 47:60, #38 +Counter in file 0 47:60 -> 47:60, #37 +Counter in file 0 47:61 -> 49:10, #42 +Counter in file 0 49:10 -> 49:11, #43 +Counter in file 0 50:9 -> 50:23, #30 Counter in file 0 52:13 -> 54:15, #7 Counter in file 0 57:9 -> 57:10, (#9 + #10) -Counter in file 0 57:16 -> 57:29, ((#25 + #28) + 0) +Counter in file 0 57:16 -> 57:29, ((#27 + #30) + 0) Counter in file 0 58:9 -> 58:23, #9 -Counter in file 0 58:23 -> 58:23, #29 -Counter in file 0 59:15 -> 59:28, ((#25 + #28) - #29) -Counter in file 0 60:12 -> 60:25, (((#25 + #28) - #29) - #6) -Counter in file 0 60:29 -> 60:42, ((((#25 + #28) - #29) - #6) - #35) -Counter in file 0 60:42 -> 60:43, (#35 + #36) -Counter in file 0 60:42 -> 60:43, (((((#25 + #28) - #29) - #6) - #35) - #36) -Counter in file 0 60:42 -> 60:42, #35 -Counter in file 0 60:42 -> 60:42, #36 -Counter in file 0 60:46 -> 60:60, (((#35 + #36) + (((((#25 + #28) - #29) - #6) - #35) - #36)) - #38) -Counter in file 0 60:60 -> 60:61, (#38 + #39) -Counter in file 0 60:60 -> 60:61, ((((#35 + #36) + (((((#25 + #28) - #29) - #6) - #35) - #36)) - #38) - #39) -Counter in file 0 60:60 -> 60:60, #38 -Counter in file 0 60:60 -> 60:60, #39 -Counter in file 0 60:61 -> 62:10, #40 -Counter in file 0 62:10 -> 62:11, (((#38 + #39) + ((((#35 + #36) + (((((#25 + #28) - #29) - #6) - #35) - #36)) - #38) - #39)) - #40) +Counter in file 0 58:23 -> 58:23, #31 +Counter in file 0 59:15 -> 59:28, ((#27 + #30) - #31) +Counter in file 0 60:12 -> 60:25, (((#27 + #30) - #31) - #6) +Counter in file 0 60:29 -> 60:42, ((((#27 + #30) - #31) - #6) - #39) +Counter in file 0 60:42 -> 60:43, (#39 + #40) +Counter in file 0 60:42 -> 60:43, (((((#27 + #30) - #31) - #6) - #39) - #40) +Counter in file 0 60:42 -> 60:42, #40 +Counter in file 0 60:42 -> 60:42, #39 +Counter in file 0 60:46 -> 60:60, #46 +Counter in file 0 60:60 -> 60:61, (#44 + #45) +Counter in file 0 60:60 -> 60:61, (#46 - #45) +Counter in file 0 60:60 -> 60:60, #44 +Counter in file 0 60:60 -> 60:60, #45 +Counter in file 0 60:61 -> 62:10, #47 +Counter in file 0 62:10 -> 62:11, #48 Counter in file 0 63:9 -> 63:23, #10 Counter in file 0 65:9 -> 65:15, #6 Counter in file 0 67:1 -> 67:2, ((#9 + #10) + (((#6 + #7) + #8) + (((#2 + 0) - #3) - #5))) From 736ddb92c893e8c0794036b557f5d50c92b444b6 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Sat, 17 Oct 2020 00:01:32 -0700 Subject: [PATCH 10/18] Cleaned up formatting, temporary comments, debug messages, and TODOs --- .../rustc_codegen_ssa/src/coverageinfo/map.rs | 20 +- compiler/rustc_middle/src/mir/coverage.rs | 4 +- .../src/transform/instrument_coverage.rs | 1108 +++++++++-------- .../rustc_mir/src/util/generic_graphviz.rs | 64 +- src/test/mir-opt/coverage_graphviz.rs | 2 +- 5 files changed, 635 insertions(+), 563 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs index 1b7e20e15bf1f..61d12dc036871 100644 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs @@ -84,7 +84,10 @@ impl FunctionCoverage { rhs: ExpressionOperandId, region: Option, ) { - debug!("add_counter_expression({:?}, lhs={:?}, op={:?}, rhs={:?} at {:?}", expression_id, lhs, op, rhs, region); + debug!( + "add_counter_expression({:?}, lhs={:?}, op={:?}, rhs={:?} at {:?}", + expression_id, lhs, op, rhs, region + ); let expression_index = self.expression_index(u32::from(expression_id)); self.expressions[expression_index] .replace(Expression { lhs, op, rhs, region }) @@ -134,8 +137,7 @@ impl FunctionCoverage { ) -> (Vec, impl Iterator) { let mut counter_expressions = Vec::with_capacity(self.expressions.len()); let mut expression_regions = Vec::with_capacity(self.expressions.len()); - let mut new_indexes = - IndexVec::from_elem_n(None, self.expressions.len()); + let mut new_indexes = IndexVec::from_elem_n(None, self.expressions.len()); // Note that an `Expression`s at any given index can include other expressions as // operands, but expression operands can only come from the subset of expressions having // `expression_index`s lower than the referencing `Expression`. Therefore, it is @@ -175,8 +177,6 @@ impl FunctionCoverage { entry.as_ref().map(|expression| (original_index, expression)) }) { - // TODO(richkadel): remove this debug: - debug!("Attempting to add {:?} = {:?}", original_index, expression); let optional_region = &expression.region; let Expression { lhs, op, rhs, .. } = *expression; @@ -185,8 +185,14 @@ impl FunctionCoverage { id_to_counter(&new_indexes, rhs).map(|rhs_counter| (lhs_counter, rhs_counter)) }) { - debug_assert!((lhs_counter.id as usize) < usize::max(self.counters.len(), self.expressions.len())); - debug_assert!((rhs_counter.id as usize) < usize::max(self.counters.len(), self.expressions.len())); + debug_assert!( + (lhs_counter.id as usize) + < usize::max(self.counters.len(), self.expressions.len()) + ); + debug_assert!( + (rhs_counter.id as usize) + < usize::max(self.counters.len(), self.expressions.len()) + ); // Both operands exist. `Expression` operands exist in `self.expressions` and have // been assigned a `new_index`. let mapped_expression_index = diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 92ca187632d26..ad0ba292d0241 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -127,9 +127,7 @@ impl CoverageKind { match *self { Counter { id, .. } => ExpressionOperandId::from(id), Expression { id, .. } => ExpressionOperandId::from(id), - Unreachable => { - bug!("Unreachable coverage cannot be part of an expression") - } + Unreachable => bug!("Unreachable coverage cannot be part of an expression"), } } diff --git a/compiler/rustc_mir/src/transform/instrument_coverage.rs b/compiler/rustc_mir/src/transform/instrument_coverage.rs index c2117b53f3e2c..9cb9bb9701d18 100644 --- a/compiler/rustc_mir/src/transform/instrument_coverage.rs +++ b/compiler/rustc_mir/src/transform/instrument_coverage.rs @@ -28,8 +28,8 @@ use rustc_span::source_map::original_sp; use rustc_span::{BytePos, CharPos, Pos, SourceFile, Span, Symbol, SyntaxContext}; use std::cmp::Ordering; -use std::ops::{Index, IndexMut}; use std::lazy::SyncOnceCell; +use std::ops::{Index, IndexMut}; const ID_SEPARATOR: &str = ","; @@ -59,32 +59,47 @@ impl DebugOptions { match setting.next() { Some(option) if option == "allow_unused_expressions" => { allow_unused_expressions = bool_option_val(option, setting.next()); - debug!("{} env option `allow_unused_expressions` is set to {}", RUSTC_COVERAGE_DEBUG_OPTIONS, allow_unused_expressions); + debug!( + "{} env option `allow_unused_expressions` is set to {}", + RUSTC_COVERAGE_DEBUG_OPTIONS, allow_unused_expressions + ); } Some(option) if option == "simplify_expressions" => { simplify_expressions = bool_option_val(option, setting.next()); - debug!("{} env option `simplify_expressions` is set to {}", RUSTC_COVERAGE_DEBUG_OPTIONS, simplify_expressions); + debug!( + "{} env option `simplify_expressions` is set to {}", + RUSTC_COVERAGE_DEBUG_OPTIONS, simplify_expressions + ); } Some(option) if option == "counter_format" => { if let Some(strval) = setting.next() { counter_format = counter_format_option_val(strval); - debug!("{} env option `counter_format` is set to {:?}", RUSTC_COVERAGE_DEBUG_OPTIONS, counter_format); + debug!( + "{} env option `counter_format` is set to {:?}", + RUSTC_COVERAGE_DEBUG_OPTIONS, counter_format + ); } else { - bug!("`{}` option in environment variable {} requires one or more plus-separated choices (a non-empty subset of `id+block+operation`)", option, RUSTC_COVERAGE_DEBUG_OPTIONS); + bug!( + "`{}` option in environment variable {} requires one or more \ + plus-separated choices (a non-empty subset of \ + `id+block+operation`)", + option, + RUSTC_COVERAGE_DEBUG_OPTIONS + ); } } - Some("") => {}, - Some(invalid) => bug!("Unsupported setting `{}` in environment variable {}", invalid, RUSTC_COVERAGE_DEBUG_OPTIONS), - None => {}, + Some("") => {} + Some(invalid) => bug!( + "Unsupported setting `{}` in environment variable {}", + invalid, + RUSTC_COVERAGE_DEBUG_OPTIONS + ), + None => {} } } } - Self { - allow_unused_expressions, - simplify_expressions, - counter_format, - } + Self { allow_unused_expressions, simplify_expressions, counter_format } } } @@ -101,7 +116,12 @@ fn bool_option_val(option: &str, some_strval: Option<&str>) -> bool { } else if vec!["no", "n", "off", "false"].contains(&val) { false } else { - bug!("Unsupported value `{}` for option `{}` in environment variable {}", option, val, RUSTC_COVERAGE_DEBUG_OPTIONS) + bug!( + "Unsupported value `{}` for option `{}` in environment variable {}", + option, + val, + RUSTC_COVERAGE_DEBUG_OPTIONS + ) } } else { true @@ -109,18 +129,18 @@ fn bool_option_val(option: &str, some_strval: Option<&str>) -> bool { } fn counter_format_option_val(strval: &str) -> ExpressionFormat { - let mut counter_format = ExpressionFormat { - id: false, - block: false, - operation: false, - }; + let mut counter_format = ExpressionFormat { id: false, block: false, operation: false }; let components = strval.splitn(3, "+"); for component in components { match component { "id" => counter_format.id = true, "block" => counter_format.block = true, "operation" => counter_format.operation = true, - _ => bug!("Unsupported counter_format choice `{}` in environment variable {}", component, RUSTC_COVERAGE_DEBUG_OPTIONS), + _ => bug!( + "Unsupported counter_format choice `{}` in environment variable {}", + component, + RUSTC_COVERAGE_DEBUG_OPTIONS + ), } } counter_format @@ -135,11 +155,7 @@ struct ExpressionFormat { impl Default for ExpressionFormat { fn default() -> Self { - Self { - id: false, - block: true, - operation: false, - } + Self { id: false, block: true, operation: true } } } @@ -151,10 +167,7 @@ struct DebugCounter { impl DebugCounter { fn new(counter_kind: CoverageKind, some_block_label: Option) -> Self { - Self { - counter_kind, - some_block_label, - } + Self { counter_kind, some_block_label } } } @@ -164,9 +177,7 @@ struct DebugCounters { impl DebugCounters { pub fn new() -> Self { - Self { - some_counters: None, - } + Self { some_counters: None } } pub fn enable(&mut self) { @@ -181,21 +192,36 @@ impl DebugCounters { if let Some(counters) = &mut self.some_counters { let id: ExpressionOperandId = match *counter_kind { CoverageKind::Counter { id, .. } => id.into(), - | CoverageKind::Expression { id, .. } => id.into(), - _ => bug!("the given `CoverageKind` is not an counter or expression: {:?}", counter_kind), + CoverageKind::Expression { id, .. } => id.into(), + _ => bug!( + "the given `CoverageKind` is not an counter or expression: {:?}", + counter_kind + ), }; - counters.insert(id.into(), DebugCounter::new(counter_kind.clone(), some_block_label)).expect_none("attempt to add the same counter_kind to DebugCounters more than once"); + counters + .insert(id.into(), DebugCounter::new(counter_kind.clone(), some_block_label)) + .expect_none( + "attempt to add the same counter_kind to DebugCounters more than once", + ); } } pub fn some_block_label(&self, operand: ExpressionOperandId) -> Option<&String> { - self.some_counters.as_ref().map_or(None, |counters| counters.get(&operand).map_or(None, |debug_counter| debug_counter.some_block_label.as_ref())) + self.some_counters.as_ref().map_or(None, |counters| { + counters + .get(&operand) + .map_or(None, |debug_counter| debug_counter.some_block_label.as_ref()) + }) } pub fn format_counter(&self, counter_kind: &CoverageKind) -> String { match *counter_kind { - CoverageKind::Counter { .. } => format!("Counter({})", self.format_counter_kind(counter_kind)), - CoverageKind::Expression { .. } => format!("Expression({})", self.format_counter_kind(counter_kind)), + CoverageKind::Counter { .. } => { + format!("Counter({})", self.format_counter_kind(counter_kind)) + } + CoverageKind::Expression { .. } => { + format!("Expression({})", self.format_counter_kind(counter_kind)) + } CoverageKind::Unreachable { .. } => "Unreachable".to_owned(), } } @@ -207,7 +233,7 @@ impl DebugCounters { return format!( "{}{} {} {}", if counter_format.id || self.some_counters.is_none() { - format!("#{} = ", id.index() ) + format!("#{} = ", id.index()) } else { String::new() }, @@ -220,17 +246,21 @@ impl DebugCounters { let id: ExpressionOperandId = match *counter_kind { CoverageKind::Counter { id, .. } => id.into(), - | CoverageKind::Expression { id, .. } => id.into(), - _ => bug!("the given `CoverageKind` is not an counter or expression: {:?}", counter_kind), + CoverageKind::Expression { id, .. } => id.into(), + _ => { + bug!("the given `CoverageKind` is not an counter or expression: {:?}", counter_kind) + } }; if self.some_counters.is_some() && (counter_format.block || !counter_format.id) { let counters = self.some_counters.as_ref().unwrap(); - if let Some(DebugCounter { some_block_label: Some(block_label), .. }) = counters.get(&id.into()) { + if let Some(DebugCounter { some_block_label: Some(block_label), .. }) = + counters.get(&id.into()) + { return if counter_format.id { format!("{}#{}", block_label, id.index()) } else { format!("{}", block_label) - } + }; } } format!("#{}", id.index()) @@ -245,7 +275,11 @@ impl DebugCounters { if let CoverageKind::Expression { .. } = counter_kind { if let Some(block_label) = some_block_label { if debug_options().counter_format.block { - return format!("{}:({})", block_label, self.format_counter_kind(counter_kind)); + return format!( + "{}:({})", + block_label, + self.format_counter_kind(counter_kind) + ); } } return format!("({})", self.format_counter_kind(counter_kind)); @@ -460,7 +494,10 @@ impl BasicCoverageBlockData { #[inline(always)] pub fn set_counter(&mut self, counter_kind: CoverageKind) -> ExpressionOperandId { - debug_assert!(self.edge_counter_from_bcbs.is_none() || counter_kind.is_expression(), "attempt to add a `Counter` to a BCB target with existing incoming edge counters"); + debug_assert!( + self.edge_counter_from_bcbs.is_none() || counter_kind.is_expression(), + "attempt to add a `Counter` to a BCB target with existing incoming edge counters" + ); let operand = counter_kind.as_operand_id(); self.counter_kind .replace(counter_kind) @@ -479,11 +516,20 @@ impl BasicCoverageBlockData { } #[inline(always)] - pub fn set_edge_counter_from(&mut self, from_bcb: BasicCoverageBlock, counter_kind: CoverageKind) -> ExpressionOperandId { - debug_assert!(self.counter_kind.as_ref().map_or(true, |c| c.is_expression()), "attempt to add an incoming edge counter from {:?} when the target BCB already has a `Counter`", from_bcb); + pub fn set_edge_counter_from( + &mut self, + from_bcb: BasicCoverageBlock, + counter_kind: CoverageKind, + ) -> ExpressionOperandId { + debug_assert!( + self.counter_kind.as_ref().map_or(true, |c| c.is_expression()), + "attempt to add an incoming edge counter from {:?} when the target BCB already has a \ + `Counter`", + from_bcb + ); let operand = counter_kind.as_operand_id(); self.edge_counter_from_bcbs - .get_or_insert_with(|| FxHashMap::default()) + .get_or_insert_with(|| FxHashMap::default()) .insert(from_bcb, counter_kind) .expect_none("attempt to set an edge counter more than once"); operand @@ -499,8 +545,10 @@ impl BasicCoverageBlockData { } #[inline(always)] - pub fn take_edge_counters(&mut self) -> Option> { - self.edge_counter_from_bcbs.take().map_or(None, |m|Some(m.into_iter())) + pub fn take_edge_counters( + &mut self, + ) -> Option> { + self.edge_counter_from_bcbs.take().map_or(None, |m| Some(m.into_iter())) } pub fn id(&self) -> String { @@ -529,7 +577,9 @@ impl BasicCoverageBlockData { sections.push( collect_intermediate_expressions .iter() - .map(|expression| format!("Intermediate {}", debug_counters.format_counter(expression))) + .map(|expression| { + format!("Intermediate {}", debug_counters.format_counter(expression)) + }) .collect::>() .join("\n"), ); @@ -538,22 +588,26 @@ impl BasicCoverageBlockData { sections.push( coverage_spans_with_counters .iter() - .map(|(covspan, counter)| format!("{} at {}", debug_counters.format_counter(counter), covspan.format(tcx, mir_body))) + .map(|(covspan, counter)| { + format!( + "{} at {}", + debug_counters.format_counter(counter), + covspan.format(tcx, mir_body) + ) + }) .collect::>() .join("\n"), ); } if let Some(dependency_counters) = some_dependency_counters { - sections.push( - format!( - "Non-coverage counters:\n {}", - dependency_counters - .iter() - .map(|counter| debug_counters.format_counter(counter)) - .collect::>() - .join(" \n"), - ) - ); + sections.push(format!( + "Non-coverage counters:\n {}", + dependency_counters + .iter() + .map(|counter| debug_counters.format_counter(counter)) + .collect::>() + .join(" \n"), + )); } if let Some(counter_kind) = &self.counter_kind { sections.push(format!("{:?}", counter_kind)); @@ -601,14 +655,10 @@ impl BasicCoverageBlocks { let successors = IndexVec::from_fn_n( |bcb| { let bcb_data = &bcbs[bcb]; - let bcb_successors = bcb_filtered_successors(&mir_body, &bcb_data.terminator(mir_body).kind) -// TODO(richkadel): -// MAKE SURE WE ONLY RETURN THE SAME SUCCESSORS USED WHEN CREATING THE BCB (THE FIRST SUCCESSOR ONLY, -// UNLESS ITS A SWITCHINT).) - -// THEN BUILD PREDECESSORS FROM THE SUCCESSORS - .filter_map(|&successor_bb| bb_to_bcb[successor_bb]) - .collect::>(); + let bcb_successors = + bcb_filtered_successors(&mir_body, &bcb_data.terminator(mir_body).kind) + .filter_map(|&successor_bb| bb_to_bcb[successor_bb]) + .collect::>(); debug_assert!({ let mut sorted = bcb_successors.clone(); sorted.sort_unstable(); @@ -662,15 +712,14 @@ impl BasicCoverageBlocks { &mut bb_to_bcb, basic_blocks.split_off(0), ); - // TODO(richkadel): uncomment debug! - // debug!( - // " because {}", - // if predecessors.len() > 1 { - // "predecessors.len() > 1".to_owned() - // } else { - // format!("bb {} is not in precessors: {:?}", bb.index(), predecessors) - // } - // ); + debug!( + " because {}", + if predecessors.len() > 1 { + "predecessors.len() > 1".to_owned() + } else { + format!("bb {} is not in precessors: {:?}", bb.index(), predecessors) + } + ); } } basic_blocks.push(bb); @@ -679,28 +728,17 @@ impl BasicCoverageBlocks { match term.kind { TerminatorKind::Return { .. } -// TODO(richkadel): Do we handle Abort like Return? The program doesn't continue -// normally. It's like a failed assert (I assume). + // FIXME(richkadel): Add test(s) for `Abort` coverage. | TerminatorKind::Abort -// TODO(richkadel): I think Assert should be handled like falseUnwind. -// It's just a goto if we assume it does not fail the assert, and if it -// does fail, the unwind caused by failure is hidden code not covered -// in coverage??? -// -// BUT if we take it out, then we can't count code up until the assert. -// -// (That may be OK, not sure. Maybe not.). -// -// How am I handling the try "?" on functions that return Result? - -// TODO(richkadel): comment out? + // FIXME(richkadel): Add test(s) for `Assert` coverage. + // Should `Assert` be handled like `FalseUnwind` instead? Since we filter out unwind + // branches when creating the BCB CFG, aren't `Assert`s (without unwinds) just like + // `FalseUnwinds` (which are kind of like `Goto`s)? | TerminatorKind::Assert { .. } - -// TODO(richkadel): And I don't know what do do with Yield + // FIXME(richkadel): Add test(s) for `Yield` coverage, and confirm coverage is + // sensible for code using the `yield` keyword. | TerminatorKind::Yield { .. } - // FIXME(richkadel): Add coverage test for TerminatorKind::Yield and/or `yield` - // keyword (see "generators" unstable feature). - // FIXME(richkadel): Add tests with async and threading. + // FIXME(richkadel): Also add coverage tests using async/await, and threading. | TerminatorKind::SwitchInt { .. } => { // The `bb` has more than one _outgoing_ edge, or exits the function. Save the @@ -711,8 +749,7 @@ impl BasicCoverageBlocks { &mut bb_to_bcb, basic_blocks.split_off(0), ); - // TODO(richkadel): uncomment debug! - // debug!(" because term.kind = {:?}", term.kind); + debug!(" because term.kind = {:?}", term.kind); // Note that this condition is based on `TerminatorKind`, even though it // theoretically boils down to `successors().len() != 1`; that is, either zero // (e.g., `Return`, `Abort`) or multiple successors (e.g., `SwitchInt`), but @@ -736,8 +773,7 @@ impl BasicCoverageBlocks { if !basic_blocks.is_empty() { // process any remaining basic_blocks into a final `BasicCoverageBlockData` Self::add_basic_coverage_block(&mut bcbs, &mut bb_to_bcb, basic_blocks.split_off(0)); - // TODO(richkadel): uncomment debug! - // debug!(" because the end of the MIR CFG was reached while traversing"); + debug!(" because the end of the MIR CFG was reached while traversing"); } (bcbs, bb_to_bcb) @@ -753,8 +789,7 @@ impl BasicCoverageBlocks { bb_to_bcb[bb] = Some(bcb); } let bcb_data = BasicCoverageBlockData::from(basic_blocks); - // TODO(richkadel): uncomment debug! - // debug!("adding bcb{}: {:?}", bcb.index(), bcb_data); + debug!("adding bcb{}: {:?}", bcb.index(), bcb_data); bcbs.push(bcb_data); } @@ -774,11 +809,7 @@ impl BasicCoverageBlocks { #[inline(always)] pub fn bcb_from_bb(&self, bb: BasicBlock) -> Option { - if bb.index() < self.bb_to_bcb.len() { - self.bb_to_bcb[bb] - } else { - None - } + if bb.index() < self.bb_to_bcb.len() { self.bb_to_bcb[bb] } else { None } } #[inline(always)] @@ -817,7 +848,8 @@ impl graph::WithNumNodes for BasicCoverageBlocks { impl graph::WithStartNode for BasicCoverageBlocks { #[inline] fn start_node(&self) -> Self::Node { - self.bcb_from_bb(mir::START_BLOCK).expect("mir::START_BLOCK should be in a BasicCoverageBlock") + self.bcb_from_bb(mir::START_BLOCK) + .expect("mir::START_BLOCK should be in a BasicCoverageBlock") } } @@ -1121,7 +1153,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { let coverage_spans = self.coverage_spans(); let span_viewables = if dump_spanview { Some(self.span_viewables(&coverage_spans)) } else { None }; - let mut collect_intermediate_expressions = Vec::with_capacity(self.basic_coverage_blocks.num_nodes()); + let mut collect_intermediate_expressions = + Vec::with_capacity(self.basic_coverage_blocks.num_nodes()); // When debug logging, or generating the coverage graphviz output, initialize the following // data structures: @@ -1138,7 +1171,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // differences in behavior when enabled, under `DEBUG`, compared to normal operation and // testing). // - // For debugging purposes, it is sometimes helpful to simplify some expression equations: + // For debugging purposes, it is sometimes helpful to simplify some expression + // equations: // // * `x + (y - x)` becomes just `y` // * `x + (y + 0)` becomes just x + y. @@ -1146,10 +1180,11 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // Expression dependencies can deeply nested expressions, which can look quite long in // printed debug messages and in graphs produced by `-Zdump-graphviz`. In reality, each // referenced/nested expression is only present because that value is necessary to - // compute a counter value for another part of the coverage report. Simplifying expressions - // Does not result in less `Coverage` statements, so there is very little, if any, benefit - // to binary size or runtime to simplifying expressions, and adds additional compile-time - // complexity. Only enable this temporarily, if helpful to parse the debug output. + // compute a counter value for another part of the coverage report. Simplifying + // expressions Does not result in less `Coverage` statements, so there is very little, + // if any, benefit to binary size or runtime to simplifying expressions, and adds + // additional compile-time complexity. Only enable this temporarily, if helpful to parse + // the debug output. } // When debugging with BCB graphviz output, initialize additional data structures. @@ -1209,21 +1244,33 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } else { bug!("Every BasicCoverageBlock should have a Counter or Expression"); }; - // TODO(richkadel): uncomment debug! - // debug!( - // "Injecting {} at: {:?}:\n{}\n==========", - // self.format_counter(counter_kind), - // span, - // source_map.span_to_snippet(span).expect("Error getting source for span"), - // ); - if let Some(bcb_to_coverage_spans_with_counters) = debug_bcb_to_coverage_spans_with_counters.as_mut() { - bcb_to_coverage_spans_with_counters.entry(bcb).or_insert_with(|| Vec::new()).push((covspan.clone(), counter_kind.clone())); + debug!( + "Injecting {} at: {:?}:\n{}\n==========", + self.format_counter(&counter_kind), + span, + source_map.span_to_snippet(span).expect("Error getting source for span"), + ); + if let Some(bcb_to_coverage_spans_with_counters) = + debug_bcb_to_coverage_spans_with_counters.as_mut() + { + bcb_to_coverage_spans_with_counters + .entry(bcb) + .or_insert_with(|| Vec::new()) + .push((covspan.clone(), counter_kind.clone())); } let mut code_region = None; if span.hi() == body_span.hi() { - // TODO(richkadel): add a comment if this works + // All functions execute a `Return`-terminated `BasicBlock`, regardless of how the + // function returns; but only some functions also _can_ return after a `Goto` block + // that ends on the closing brace of the function (with the `Return`). When this + // happens, the last character is counted 2 (or possibly more) times, when we know + // the function returned only once (of course). By giving all `Goto` terminators at + // the end of a function a `non-reportable` code region, they are still counted + // if appropriate, but they don't increment the line counter, as long as their is + // also a `Return` on that last line. if let TerminatorKind::Goto { .. } = self.bcb_terminator(bcb).kind { - code_region = Some(make_non_reportable_code_region(file_name, &source_file, span)); + code_region = + Some(make_non_reportable_code_region(file_name, &source_file, span)); } } if code_region.is_none() { @@ -1247,7 +1294,11 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } if let Some(edge_counters) = target_bcb_data.take_edge_counters() { for (from_bcb, counter_kind) in edge_counters { - bcb_counters_without_direct_coverage_spans.push((Some(from_bcb), target_bcb, counter_kind)); + bcb_counters_without_direct_coverage_spans.push(( + Some(from_bcb), + target_bcb, + counter_kind, + )); } } } @@ -1256,16 +1307,25 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // at least indirectly associated (it is a dependency of a BCB counter that _is_ associated // with a coverage span). if let Some(used_expression_operands) = debug_used_expression_operands.as_mut() { - let mut not_validated = bcb_counters_without_direct_coverage_spans.iter().map(|(_, _, counter_kind)| counter_kind).collect::>(); + let mut not_validated = bcb_counters_without_direct_coverage_spans + .iter() + .map(|(_, _, counter_kind)| counter_kind) + .collect::>(); let mut validating_count = 0; - while not_validated.len() != validating_count { + while not_validated.len() != validating_count { let to_validate = not_validated.split_off(0); validating_count = to_validate.len(); for counter_kind in to_validate { if used_expression_operands.contains_key(&counter_kind.as_operand_id()) { if let CoverageKind::Expression { id, lhs, rhs, .. } = *counter_kind { - used_expression_operands.entry(lhs).or_insert_with(|| Vec::new()).push(id); - used_expression_operands.entry(rhs).or_insert_with(|| Vec::new()).push(id); + used_expression_operands + .entry(lhs) + .or_insert_with(|| Vec::new()) + .push(id); + used_expression_operands + .entry(rhs) + .or_insert_with(|| Vec::new()) + .push(id); } } else { not_validated.push(counter_kind); @@ -1274,38 +1334,46 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } } -// let (last_line, last_col) = source_file.lookup_file_pos(body_span.hi()); - for (edge_counter_from_bcb, target_bcb, counter_kind) in bcb_counters_without_direct_coverage_spans { - if let ( - Some(used_expression_operands), - Some(unused_expressions), - ) = ( - debug_used_expression_operands.as_ref(), - debug_unused_expressions.as_mut(), - ) { + for (edge_counter_from_bcb, target_bcb, counter_kind) in + bcb_counters_without_direct_coverage_spans + { + if let (Some(used_expression_operands), Some(unused_expressions)) = + (debug_used_expression_operands.as_ref(), debug_unused_expressions.as_mut()) + { if !used_expression_operands.contains_key(&counter_kind.as_operand_id()) { - unused_expressions.push((counter_kind.clone(), edge_counter_from_bcb, target_bcb)); + unused_expressions.push(( + counter_kind.clone(), + edge_counter_from_bcb, + target_bcb, + )); } } match counter_kind { CoverageKind::Counter { .. } => { let inject_to_bb = if let Some(from_bcb) = edge_counter_from_bcb { - // The MIR edge starts `from_bb` (the outgoing / last BasicBlock in `from_bcb`) and - // ends at `to_bb` (the incoming / first BasicBlock in the `target_bcb`; also called - // the `leader_bb`). + // The MIR edge starts `from_bb` (the outgoing / last BasicBlock in + // `from_bcb`) and ends at `to_bb` (the incoming / first BasicBlock in the + // `target_bcb`; also called the `leader_bb`). let from_bb = self.bcb_last_bb(from_bcb); let to_bb = self.bcb_leader_bb(target_bcb); debug!( - "Edge {:?} (last {:?}) -> {:?} (leader {:?}) requires a new MIR BasicBlock, for unclaimed edge counter {}", - edge_counter_from_bcb, from_bb, target_bcb, to_bb, self.format_counter(&counter_kind), + "Edge {:?} (last {:?}) -> {:?} (leader {:?}) requires a new MIR \ + BasicBlock, for unclaimed edge counter {}", + edge_counter_from_bcb, + from_bb, + target_bcb, + to_bb, + self.format_counter(&counter_kind), ); debug!( " from_bb {:?} has successors: {:?}", - from_bb, self.mir_body[from_bb].terminator().successors(), + from_bb, + self.mir_body[from_bb].terminator().successors(), ); - let span = self.mir_body[from_bb].terminator().source_info.span.shrink_to_hi(); + let span = + self.mir_body[from_bb].terminator().source_info.span.shrink_to_hi(); let new_bb = self.mir_body.basic_blocks_mut().push(BasicBlockData { statements: vec![], // counter will be injected here terminator: Some(Terminator { @@ -1314,19 +1382,36 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { }), is_cleanup: false, }); - let edge_ref = self.mir_body[from_bb].terminator_mut().successors_mut().find(|successor| **successor == to_bb).expect("from_bb should have a successor for to_bb"); + let edge_ref = self.mir_body[from_bb] + .terminator_mut() + .successors_mut() + .find(|successor| **successor == to_bb) + .expect("from_bb should have a successor for to_bb"); *edge_ref = new_bb; if let Some(edge_to_counter) = debug_edge_to_counter.as_mut() { - debug!("from_bcb={:?} to new_bb={:?} has edge_counter={}", - from_bcb, new_bb, self.format_counter(&counter_kind), + debug!( + "from_bcb={:?} to new_bb={:?} has edge_counter={}", + from_bcb, + new_bb, + self.format_counter(&counter_kind), ); - edge_to_counter.insert((from_bcb, new_bb), counter_kind.clone()).expect_none("invalid attempt to insert more than one edge counter for the same edge"); + edge_to_counter + .insert((from_bcb, new_bb), counter_kind.clone()) + .expect_none( + "invalid attempt to insert more than one edge counter for the \ + same edge", + ); } new_bb } else { - if let Some(bcb_to_dependency_counter) = debug_bcb_to_dependency_counter.as_mut() { - bcb_to_dependency_counter.entry(target_bcb).or_insert_with(|| Vec::new()).push(counter_kind.clone()); + if let Some(bcb_to_dependency_counter) = + debug_bcb_to_dependency_counter.as_mut() + { + bcb_to_dependency_counter + .entry(target_bcb) + .or_insert_with(|| Vec::new()) + .push(counter_kind.clone()); } let target_bb = self.bcb_last_bb(target_bcb); debug!( @@ -1337,23 +1422,34 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { ); target_bb }; -// debug!("make_non_reportable_code_region for {:?} at last_line={:?}, last_col={:?}, counter={}", -// inject_to_bb, last_line, last_col, self.format_counter(&counter_kind)); -// self.inject_statement(counter_kind, inject_to_bb, make_non_reportable_code_region(file_name, last_line, last_col)); + let span = self.mir_body[inject_to_bb].terminator().source_info.span; - debug!("make_non_reportable_code_region for {:?} at span={:?}, counter={}", - inject_to_bb, span, self.format_counter(&counter_kind)); - self.inject_statement(counter_kind, inject_to_bb, make_non_reportable_code_region(file_name, &source_file, span)); + debug!( + "make_non_reportable_code_region for {:?} at span={:?}, counter={}", + inject_to_bb, + span, + self.format_counter(&counter_kind) + ); + self.inject_statement( + counter_kind, + inject_to_bb, + make_non_reportable_code_region(file_name, &source_file, span), + ); + } + CoverageKind::Expression { .. } => { + self.inject_intermediate_expression(counter_kind) } - CoverageKind::Expression { .. } => self.inject_intermediate_expression(counter_kind), _ => bug!("CoverageKind should be a counter"), } } if dump_graphviz { - let bcb_to_coverage_spans_with_counters = debug_bcb_to_coverage_spans_with_counters.expect("graphviz data should exist if dump_graphviz is true"); - let bcb_to_dependency_counter = debug_bcb_to_dependency_counter.expect("graphviz data should exist if dump_graphviz is true"); - let edge_to_counter = debug_edge_to_counter.expect("graphviz data should exist if dump_graphviz is true"); + let bcb_to_coverage_spans_with_counters = debug_bcb_to_coverage_spans_with_counters + .expect("graphviz data should exist if dump_graphviz is true"); + let bcb_to_dependency_counter = debug_bcb_to_dependency_counter + .expect("graphviz data should exist if dump_graphviz is true"); + let edge_to_counter = + debug_edge_to_counter.expect("graphviz data should exist if dump_graphviz is true"); let graphviz_name = format!("Cov_{}_{}", def_id.krate.index(), def_id.index.index()); let node_content = |bcb| { self.bcb_data(bcb).to_string_sections( @@ -1362,25 +1458,29 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { &self.debug_counters, bcb_to_coverage_spans_with_counters.get(&bcb), bcb_to_dependency_counter.get(&bcb), - // collect_intermediate_expressions are injected into the mir::START_BLOCK, so include - // them in the first BCB. - if bcb.index() == 0 { Some(&collect_intermediate_expressions) } else { None } + // collect_intermediate_expressions are injected into the mir::START_BLOCK, so + // include them in the first BCB. + if bcb.index() == 0 { Some(&collect_intermediate_expressions) } else { None }, ) }; let edge_labels = |from_bcb| { let from_terminator = self.bcb_terminator(from_bcb); let mut edge_labels = from_terminator.kind.fmt_successor_labels(); edge_labels.retain(|label| label.to_string() != "unreachable"); - let edge_counters = from_terminator.successors().map(|&successor| { - edge_to_counter.get(&(from_bcb, successor)) - }); - edge_labels.iter().zip(edge_counters).map(|(label, some_counter)| { - if let Some(counter) = some_counter { - format!("{}\n{}", label, self.format_counter(counter)) - } else { - label.to_string() - } - }).collect::>() + let edge_counters = from_terminator + .successors() + .map(|&successor| edge_to_counter.get(&(from_bcb, successor))); + edge_labels + .iter() + .zip(edge_counters) + .map(|(label, some_counter)| { + if let Some(counter) = some_counter { + format!("{}\n{}", label, self.format_counter(counter)) + } else { + label.to_string() + } + }) + .collect::>() }; let mut graphviz_writer = GraphvizWriter::new( &self.basic_coverage_blocks, @@ -1392,22 +1492,27 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { if unused_expressions.len() > 0 { graphviz_writer.set_graph_label(&format!( "Unused expressions:\n {}", - unused_expressions.as_slice().iter().map(|(counter_kind, edge_counter_from_bcb, target_bcb)| { - if let Some(from_bcb) = edge_counter_from_bcb.as_ref() { - format!( - "{:?}->{:?}: {}", - from_bcb, - target_bcb, - self.format_counter(&counter_kind), - ) - } else { - format!( - "{:?}: {}", - target_bcb, - self.format_counter(&counter_kind), - ) - } - }).collect::>().join("\n ") + unused_expressions + .as_slice() + .iter() + .map(|(counter_kind, edge_counter_from_bcb, target_bcb)| { + if let Some(from_bcb) = edge_counter_from_bcb.as_ref() { + format!( + "{:?}->{:?}: {}", + from_bcb, + target_bcb, + self.format_counter(&counter_kind), + ) + } else { + format!( + "{:?}: {}", + target_bcb, + self.format_counter(&counter_kind), + ) + } + }) + .collect::>() + .join("\n ") )); } } @@ -1421,28 +1526,33 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { if let Some(unused_expressions) = debug_unused_expressions.as_ref() { for (counter_kind, edge_counter_from_bcb, target_bcb) in unused_expressions { - let unused_counter_message = if let Some(from_bcb) = edge_counter_from_bcb.as_ref() { + let unused_counter_message = if let Some(from_bcb) = edge_counter_from_bcb.as_ref() + { format!( - "non-coverage edge counter found without a dependent expression, in {:?}->{:?}; counter={}", + "non-coverage edge counter found without a dependent expression, in \ + {:?}->{:?}; counter={}", from_bcb, target_bcb, self.format_counter(&counter_kind), ) } else { format!( - "non-coverage counter found without a dependent expression, in {:?}; counter={}", + "non-coverage counter found without a dependent expression, in {:?}; \ + counter={}", target_bcb, self.format_counter(&counter_kind), ) }; - // FIXME(richkadel): Determine if unused expressions can and should be prevented, and - // if so, it would be a `bug!` if encountered in the future. At the present time, - // however we do sometimes encounter unused expressions (in only a subset of test cases) - // even when `simplify_expressions` is disabled. It's not yet clear what causes this, or - // if it should be allowed in the long term, but as long as the coverage capability - // still works, generate warning messages only, for now. - if self.debug_expressions_cache.is_some() || debug_options().allow_unused_expressions { + // FIXME(richkadel): Determine if unused expressions can and should be prevented, + // and if so, it would be a `bug!` if encountered in the future. At the present + // time, however we do sometimes encounter unused expressions (in only a subset of + // test cases) even when `simplify_expressions` is disabled. It's not yet clear what + // causes this, or if it should be allowed in the long term, but as long as the + // coverage capability still works, generate warning messages only, for now. + if self.debug_expressions_cache.is_some() + || debug_options().allow_unused_expressions + { // Note, the debugging option `simplify_expressions`, which initializes the // `debug_expressions_cache` can cause some counters to become unused, and // is not a bug. @@ -1461,35 +1571,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { self.inject_intermediate_expression(intermediate_expression); } - // TODO(richkadel): - // - // 1. "try" coverage isn't quite right. See `bcb14` in try_error_result.rs. - // The `?` character should have its own coverage span (for bcb14, which is otherwise - // uncovered) indicating when the error result occurred, and the `main()` function - // returned `Err`. It looks like it's because the SwitchInt block includes an Assign - // with the exact same span as the `?` span, and since it dominates the `Err` handling - // code for `?`, it got the span. This seems wrong here, but the `dominator gets equal spans` - // logic was there for the match guard handling. - // - // Maybe I really should reverse that match guard handling. I know it looks weird, but actually - // it may be right. The assignment of the value to the guard variable is (maybe?) to make - // it available to the arm code? Well, actually, not (only) in that case since I use it for - // the comparison. But then, why doesn't the comparison get it? (Or does it? Maybe it would - // get it and that's why it would be counted twice!) - // - // I need to try it again. - // 2. SwitchInt branches should exclude branches to unreachable blocks. - // 3. BCB's without CoverageSpans may (??!!??) not need counters unless they are part - // of another expression. With the current algorithm, I think the only way they can - // be part of an expression is if they are a SwitchInt or a target of a SwitchInt, - // AND I think the SwitchInt counter logic may ensure those counters are added, - // so we can probably just not add the counters during traversal if there are no - // coverage spans. - // 4. If a BCB gets a counter (because it's a SwitchInt or target of SwitchInt), and - // there is no CoverageSpan for that BCB, we still must `inject_statement` the - // counter, ... but with what coverage span??? Maybe an empty span at the terminator's - // span.lo()? - if let Some(span_viewables) = span_viewables { let mut file = pretty::create_dump_file(tcx, "html", None, self.pass_name, &0, mir_source) @@ -1523,10 +1604,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { ) { debug!("make_bcb_counters(): adding a counter or expression to each BasicCoverageBlock"); let num_bcbs = self.basic_coverage_blocks.num_nodes(); - let mut backedges = IndexVec::from_elem_n( - Vec::::new(), - num_bcbs, - ); + let mut backedges = IndexVec::from_elem_n(Vec::::new(), num_bcbs); // Identify loops by their backedges for (bcb, _) in self.basic_coverage_blocks.iter_enumerated() { @@ -1585,13 +1663,15 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { if self.bcb_is_dominated_by(successor, loop_header) { if self.bcb_successors(successor).len() > 1 { debug!( - "Adding branching successor {:?} to the beginning of the worklist of loop headed by {:?}", + "Adding branching successor {:?} to the beginning of the \ + worklist of loop headed by {:?}", successor, loop_header ); context.worklist.insert(0, successor); } else { debug!( - "Adding non-branching successor {:?} to the end of the worklist of loop headed by {:?}", + "Adding non-branching successor {:?} to the end of the \ + worklist of loop headed by {:?}", successor, loop_header ); context.worklist.push(successor); @@ -1600,10 +1680,18 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } } else { if self.bcb_successors(successor).len() > 1 { - debug!("Adding branching successor {:?} to the beginning of the non-loop worklist", successor); + debug!( + "Adding branching successor {:?} to the beginning of the non-loop \ + worklist", + successor + ); context.worklist.insert(0, successor); } else { - debug!("Adding non-branching successor {:?} to the end of the non-loop worklist", successor); + debug!( + "Adding non-branching successor {:?} to the end of the non-loop \ + worklist", + successor + ); context.worklist.push(successor); } } @@ -1614,22 +1702,22 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { continue; } - let bcb_counter_operand = self.get_or_make_counter_operand(bcb, collect_intermediate_expressions); + let bcb_counter_operand = + self.get_or_make_counter_operand(bcb, collect_intermediate_expressions); let needs_branch_counters = { let successors = self.bcb_successors(bcb); - successors.len() > 1 && successors.iter().any(|&successor| self.bcb_data(successor).counter().is_none()) + successors.len() > 1 + && successors + .iter() + .any(|&successor| self.bcb_data(successor).counter().is_none()) }; if needs_branch_counters { let branching_bcb = bcb; let branching_counter_operand = bcb_counter_operand; let branches = self.bcb_successors(branching_bcb).clone(); - debug!( - "{:?} is branching, with branches: {:?}", - branching_bcb, - branches - ); + debug!("{:?} is branching, with branches: {:?}", branching_bcb, branches); // At most one of the branches (or its edge, from the branching_bcb, // if the branch has multiple incoming edges) can have a counter computed by @@ -1663,8 +1751,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { found_loop_exit, some_reloop_branch ); if !found_loop_exit { - // No branches exit a loop, so there is no specific recommended branch for - // an `Expression`. + // No branches exit a loop, so there is no specific recommended branch + // for an `Expression`. break; } if some_reloop_branch.is_some() { @@ -1684,12 +1772,14 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } else { let &branch_without_counter = branches .iter() - .find(|&&branch| { - self.bcb_data(branch).counter().is_none() - }) - .expect("needs_branch_counters was `true` so there should be at least one branch"); + .find(|&&branch| self.bcb_data(branch).counter().is_none()) + .expect( + "needs_branch_counters was `true` so there should be at least one \ + branch", + ); debug!( - "No preferred expression branch. Selected the first branch without a counter. That branch={:?}", + "No preferred expression branch. Selected the first branch without a \ + counter. That branch={:?}", branch_without_counter ); branch_without_counter @@ -1700,15 +1790,28 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { let mut some_sumup_counter_operand = None; for branch in branches { if branch != expression_branch { - let branch_counter_operand = - if self.bcb_has_multiple_incoming_edges(branch) { - debug!("{:?} has multiple incoming edges, so adding an edge counter from {:?}", branch, branching_bcb); - self.get_or_make_edge_counter_operand(branching_bcb, branch, collect_intermediate_expressions) - } else { - debug!("{:?} has only one incoming edge (from {:?}), so adding a counter", branch, branching_bcb); -// TODO(richkadel): IS THIS FOR LOOP DUPLICATING WHAT'S IN get_or_make_counter_operand? - self.get_or_make_counter_operand(branch, collect_intermediate_expressions) - }; + let branch_counter_operand = if self.bcb_has_multiple_incoming_edges(branch) + { + debug!( + "{:?} has multiple incoming edges, so adding an edge counter from \ + {:?}", + branch, branching_bcb + ); + self.get_or_make_edge_counter_operand( + branching_bcb, + branch, + collect_intermediate_expressions, + ) + } else { + debug!( + "{:?} has only one incoming edge (from {:?}), so adding a counter", + branch, branching_bcb + ); + self.get_or_make_counter_operand( + branch, + collect_intermediate_expressions, + ) + }; if let Some(sumup_counter_operand) = some_sumup_counter_operand.replace(branch_counter_operand) { @@ -1718,64 +1821,57 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { sumup_counter_operand, || None, ); - debug!(" new intermediate expression: {}", self.format_counter(&intermediate_expression)); - let intermediate_expression_operand = intermediate_expression.as_operand_id(); + debug!( + " new intermediate expression: {}", + self.format_counter(&intermediate_expression) + ); + let intermediate_expression_operand = + intermediate_expression.as_operand_id(); collect_intermediate_expressions.push(intermediate_expression); some_sumup_counter_operand.replace(intermediate_expression_operand); } } } - let sumup_counter_operand = some_sumup_counter_operand.expect("sumup_counter_operand should have a value"); - let multiple_incoming_edges = self.bcb_has_multiple_incoming_edges(expression_branch); - debug!("expression_branch is {:?}, multiple_incoming_edges={}, expression_branch predecessors: {:?}", - expression_branch, multiple_incoming_edges, self.bcb_predecessors(expression_branch)); + let sumup_counter_operand = + some_sumup_counter_operand.expect("sumup_counter_operand should have a value"); + let multiple_incoming_edges = + self.bcb_has_multiple_incoming_edges(expression_branch); + debug!( + "expression_branch is {:?}, multiple_incoming_edges={}, expression_branch \ + predecessors: {:?}", + expression_branch, + multiple_incoming_edges, + self.bcb_predecessors(expression_branch) + ); let expression = self.make_expression( branching_counter_operand, Op::Subtract, sumup_counter_operand, - || Some( - if multiple_incoming_edges { + || { + Some(if multiple_incoming_edges { format!("{:?}->{:?}", branching_bcb, expression_branch) } else { format!("{:?}", expression_branch) - } - ) + }) + }, ); if multiple_incoming_edges { - debug!("Edge {:?}->{:?} gets an expression: {}", branching_bcb, expression_branch, self.format_counter(&expression)); - self.bcb_data_mut(expression_branch).set_edge_counter_from(branching_bcb, expression); + debug!( + "Edge {:?}->{:?} gets an expression: {}", + branching_bcb, + expression_branch, + self.format_counter(&expression) + ); + self.bcb_data_mut(expression_branch) + .set_edge_counter_from(branching_bcb, expression); } else { - debug!("{:?} gets an expression: {}", expression_branch, self.format_counter(&expression)); + debug!( + "{:?} gets an expression: {}", + expression_branch, + self.format_counter(&expression) + ); self.bcb_data_mut(expression_branch).set_counter(expression); } - -// TODO(richkadel): -// Who would use this edge counter? -// Since this isn't the bcb counter, what will the bcb counter be? -// (I think the bcb counter will be the sum of all edge counters) -// -// Does this mean that we should assert that a bcb ONLY has either a single bcb counter, or 2 or more edge counters, but not both? -// WELL... not exactly. -// 1. A BCB with edge counters always has an Expression -// 2. A BCB with multiple predecessors always gets an Expression. -// -// ??? -// 3. If NO predecessor comes from a branching BCB, then the BCB Expression can be the sum of all predecessor BCB counters -// ??? -// -// 4. If (any?) predecessor comes from a branching BCB, assume the incoming edges will -// ?? all ?? -// need edge counters, and the BCB Expression is the sum of all incoming edge counters. -// -// THIS (#4) DOESN'T SOUND RIGHT -// -// 5. An incoming edge from a source BCB (from_bcb) with only one successor (so, from_bcb is not a branching BCB) does not -// need a separate outgoing edge counter. In this case, when summing up incoming edge counts for the successor BCB, for -// each edge, use the incoming edge counter if present, or use the counter from the edge SOURCE (and assert the source has -// only one successor) - - - } } @@ -1832,17 +1928,29 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { self.bcb_dominators.is_dominated_by(node, dom) } - fn get_or_make_counter_operand(&mut self, bcb: BasicCoverageBlock, collect_intermediate_expressions: &mut Vec) -> ExpressionOperandId { + fn get_or_make_counter_operand( + &mut self, + bcb: BasicCoverageBlock, + collect_intermediate_expressions: &mut Vec, + ) -> ExpressionOperandId { if let Some(counter_kind) = self.basic_coverage_blocks[bcb].counter() { debug!(" {:?} already has a counter: {}", bcb, self.format_counter(counter_kind)); counter_kind.as_operand_id() } else { if self.bcb_has_multiple_incoming_edges(bcb) { let mut predecessors = self.bcb_predecessors(bcb).clone().into_iter(); - let first_edge_counter_operand = self.get_or_make_edge_counter_operand(predecessors.next().unwrap(), bcb, collect_intermediate_expressions); + let first_edge_counter_operand = self.get_or_make_edge_counter_operand( + predecessors.next().unwrap(), + bcb, + collect_intermediate_expressions, + ); let mut some_sumup_edge_counter_operand = None; for predecessor in predecessors { - let edge_counter_operand = self.get_or_make_edge_counter_operand(predecessor, bcb, collect_intermediate_expressions); + let edge_counter_operand = self.get_or_make_edge_counter_operand( + predecessor, + bcb, + collect_intermediate_expressions, + ); if let Some(sumup_edge_counter_operand) = some_sumup_edge_counter_operand.replace(edge_counter_operand) { @@ -1852,8 +1960,12 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { edge_counter_operand, || None, ); - debug!(" new intermediate expression: {}", self.format_counter(&intermediate_expression)); - let intermediate_expression_operand = intermediate_expression.as_operand_id(); + debug!( + " new intermediate expression: {}", + self.format_counter(&intermediate_expression) + ); + let intermediate_expression_operand = + intermediate_expression.as_operand_id(); collect_intermediate_expressions.push(intermediate_expression); some_sumup_edge_counter_operand.replace(intermediate_expression_operand); } @@ -1862,9 +1974,13 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { first_edge_counter_operand, Op::Add, some_sumup_edge_counter_operand.unwrap(), - || Some(format!("{:?}", bcb)) + || Some(format!("{:?}", bcb)), + ); + debug!( + " {:?} gets a new counter (sum of predecessor counters): {}", + bcb, + self.format_counter(&counter_kind) ); - debug!(" {:?} gets a new counter (sum of predecessor counters): {}", bcb, self.format_counter(&counter_kind)); self.basic_coverage_blocks[bcb].set_counter(counter_kind) } else { let counter_kind = self.make_counter(|| Some(format!("{:?}", bcb))); @@ -1874,15 +1990,33 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } } - fn get_or_make_edge_counter_operand(&mut self, from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock, collect_intermediate_expressions: &mut Vec) -> ExpressionOperandId { + fn get_or_make_edge_counter_operand( + &mut self, + from_bcb: BasicCoverageBlock, + to_bcb: BasicCoverageBlock, + collect_intermediate_expressions: &mut Vec, + ) -> ExpressionOperandId { let successors = self.bcb_successors(from_bcb).iter(); if successors.len() > 1 { - if let Some(counter_kind) = self.basic_coverage_blocks[to_bcb].edge_counter_from(from_bcb) { - debug!(" Edge {:?}->{:?} already has a counter: {}", from_bcb, to_bcb, self.format_counter(counter_kind)); + if let Some(counter_kind) = + self.basic_coverage_blocks[to_bcb].edge_counter_from(from_bcb) + { + debug!( + " Edge {:?}->{:?} already has a counter: {}", + from_bcb, + to_bcb, + self.format_counter(counter_kind) + ); counter_kind.as_operand_id() } else { - let counter_kind = self.make_counter(|| Some(format!("{:?}->{:?}", from_bcb, to_bcb))); - debug!(" Edge {:?}->{:?} gets a new counter: {}", from_bcb, to_bcb, self.format_counter(&counter_kind)); + let counter_kind = + self.make_counter(|| Some(format!("{:?}->{:?}", from_bcb, to_bcb))); + debug!( + " Edge {:?}->{:?} gets a new counter: {}", + from_bcb, + to_bcb, + self.format_counter(&counter_kind) + ); self.basic_coverage_blocks[to_bcb].set_edge_counter_from(from_bcb, counter_kind) } } else { @@ -1890,67 +2024,9 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } } - // loop through backedges - - // select inner loops before their outer loops, so the first matched loop for a given branch BCB - // is it's inner-most loop - - // CASE #1: - // if a branch_bcb is_dominated_by a loop bcb (target of backedge), and if any other branch_bcb is NOT dominated by the loop bcb, - // add expression to the first branch_bcb that dominated by the loop bcb, and counters to all others. Compute expressions from - // counter pairs as needed, to provide a single sum that can be subtracted from the branching block's counter. - - // CASE #2: - // if all branch_bcb are dominated_by the loop bcb, no branch ends the loop (directly), so pick any branch to have the expression, - - // CASE #3: - // if NONE of the branch_bcb are dominated_by the loop bcb, check if there's an outer loop (from stack of active loops?) - // and re-do this check again to see if one of them jumps out of the outer loop while other(s) don't, and assign the expression - // to one of the branch_bcb that is dominated_by that outer loop. (Continue this if none are dominated by the outer loop either.) - - // TODO(richkadel): In the last case above, also see the next TODO below. If all branches exit the loop then can we pass that info - // to the predecessor (if only one??) so if the predecessor is a branch of another branching BCB, we know that the predecessor exits - // the loop, and should have the counter, if the predecessor is in CASE #2 (none of the other branches of the predecessor's - // branching BCB exited the loop?) - - // TODO(richkadel): What about a branch BCB that is another branching BCB, where both branches exit the loop? - // Can I detect that somehow? - - // TODO(richkadel): For any node, N, and one of its successors, H (so N -> H), if (_also_) - // N is_dominated_by H, then N -> H is a backedge. That is, we've identified that N -> H is - // at least _one_ of possibly multiple arcs that loop back to the start of the loop with - // "header" H, and this also means we've identified a loop, that has "header" H. - // - // H dominates everything inside the loop. - // - // TODO(richkadel): This doesn't make as much sense after renaming some terms: - // So a branching BCB's branch in a BasicBlock that is_dominated_by H and has a branch to a - // BasicBlock that is: - // * not H, and ... (what if the branching BCB successor _is_ H? `continue`? is this a - // candidate for a middle or optional priority for getting a Counter?) - // * not is_dominated_by H - // is a branch that jumps outside the loop, and should get an actual Counter, most likely - // - // Or perhaps conversely, a branching BCB is dominated by H with a branch that has a target that - // ALSO is dominated by H should get a Expression. - // - // - // So I need to identify all of the "H"'s, by identifying all of the backedges. - // - // If I have multiple H's (multiple loops), how do I decide which loop to compare a branch - // BCB (by dominator) to? - // - // Can I assume the traversal order is helpful here? I.e., the "last" encountered loop - // header is the (only?) one to compare to? (Probably not only... I don't see how that would - // work for nested loops.) - // - // What about multiple loops in sequence? - // - // - // What about nexted loops and jumping out of one or more of them at a time? - fn make_counter(&mut self, block_label_fn: F) -> CoverageKind - where F: Fn() -> Option + where + F: Fn() -> Option, { let counter = CoverageKind::Counter { function_source_hash: self.function_source_hash(), @@ -1969,10 +2045,13 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { mut rhs: ExpressionOperandId, block_label_fn: F, ) -> CoverageKind - where F: Fn() -> Option + where + F: Fn() -> Option, { if let Some(expressions_cache) = self.debug_expressions_cache.as_ref() { - if let Some(CoverageKind::Expression { lhs: lhs_lhs, op, rhs: lhs_rhs, .. } ) = expressions_cache.get(&lhs) { + if let Some(CoverageKind::Expression { lhs: lhs_lhs, op, rhs: lhs_rhs, .. }) = + expressions_cache.get(&lhs) + { if *lhs_rhs == ExpressionOperandId::ZERO { lhs = *lhs_lhs; } else if *op == Op::Subtract && *lhs_rhs == rhs { @@ -1986,7 +2065,9 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } } - if let Some(CoverageKind::Expression { lhs: rhs_lhs, op, rhs: rhs_rhs, .. } ) = expressions_cache.get(&rhs) { + if let Some(CoverageKind::Expression { lhs: rhs_lhs, op, rhs: rhs_rhs, .. }) = + expressions_cache.get(&rhs) + { if *rhs_rhs == ExpressionOperandId::ZERO { rhs = *rhs_rhs; } else if *op == Op::Subtract && *rhs_rhs == lhs { @@ -2018,19 +2099,28 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } else { None }; - let expressions_cache = self.debug_expressions_cache.as_mut().expect("`as_duplicate_expression()` requires the debug_expressions_cache"); + let expressions_cache = self + .debug_expressions_cache + .as_mut() + .expect("`as_duplicate_expression()` requires the debug_expressions_cache"); match expression { CoverageKind::Expression { ref mut id, .. } => { - *id = next_expression_id.expect("next_expression_id should be Some if there is a debug_expressions_cache"); + *id = next_expression_id.expect( + "next_expression_id should be Some if there is a debug_expressions_cache", + ); expressions_cache.insert(id.into(), expression.clone()); } - _ => bug!("make_duplicate_expression called with non-expression type: {:?}", expression), + _ => { + bug!("make_duplicate_expression called with non-expression type: {:?}", expression) + } } expression } fn make_identity_counter(&mut self, counter_operand: ExpressionOperandId) -> CoverageKind { - if let Some(expression) = self.debug_expressions_cache.as_ref().map_or(None, |c| c.get(&counter_operand)) { + if let Some(expression) = + self.debug_expressions_cache.as_ref().map_or(None, |c| c.get(&counter_operand)) + { let new_expression = expression.clone(); self.as_duplicate_expression(new_expression) } else { @@ -2039,7 +2129,9 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } else { None }; - self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO, || some_block_label.clone()) + self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO, || { + some_block_label.clone() + }) } } @@ -2049,14 +2141,15 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { bb: BasicBlock, code_region: CodeRegion, ) { - // TODO(richkadel): uncomment debug! - // debug!(" injecting statement {:?} covering {:?}", counter_kind, code_region); - + debug!(" injecting statement {:?} covering {:?}", counter_kind, code_region); let data = &mut self.mir_body[bb]; let source_info = data.terminator().source_info; let statement = Statement { source_info, - kind: StatementKind::Coverage(box Coverage { kind: counter_kind, code_region: Some(code_region) }), + kind: StatementKind::Coverage(box Coverage { + kind: counter_kind, + code_region: Some(code_region), + }), }; data.statements.push(statement); } @@ -2064,9 +2157,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // Non-code expressions are injected into the coverage map, without generating executable code. fn inject_intermediate_expression(&mut self, expression: CoverageKind) { debug_assert!(if let CoverageKind::Expression { .. } = expression { true } else { false }); - // TODO(richkadel): uncomment debug! - // debug!(" injecting non-code expression {:?}", expression); - + debug!(" injecting non-code expression {:?}", expression); let inject_in_bb = mir::START_BLOCK; let data = &mut self.mir_body[inject_in_bb]; let source_info = data.terminator().source_info; @@ -2191,7 +2282,12 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { .unwrap() }); - let refinery = CoverageSpanRefinery::from_sorted_spans(initial_spans, &self.mir_body, &self.basic_coverage_blocks, &self.bcb_dominators); + let refinery = CoverageSpanRefinery::from_sorted_spans( + initial_spans, + &self.mir_body, + &self.basic_coverage_blocks, + &self.bcb_dominators, + ); refinery.to_refined_spans() } } @@ -2204,7 +2300,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { /// execution /// * Carve out (leave uncovered) any span that will be counted by another MIR (notably, closures) struct CoverageSpanRefinery<'a, 'tcx> { - /// The initial set of `CoverageSpan`s, sorted by `Span` (`lo` and `hi`) and by relative /// dominance between the `BasicCoverageBlock`s of equal `Span`s. sorted_spans_iter: std::vec::IntoIter, @@ -2313,7 +2408,13 @@ impl<'a, 'tcx> CoverageSpanRefinery<'a, 'tcx> { debug!(" AT END, adding last prev={:?}", self.prev()); let prev = self.take_prev(); - let CoverageSpanRefinery { mir_body, basic_coverage_blocks, pending_dups, mut refined_spans, .. } = self; + let CoverageSpanRefinery { + mir_body, + basic_coverage_blocks, + pending_dups, + mut refined_spans, + .. + } = self; for dup in pending_dups { debug!(" ...adding at least one pending dup={:?}", dup); refined_spans.push(dup); @@ -2324,16 +2425,14 @@ impl<'a, 'tcx> CoverageSpanRefinery<'a, 'tcx> { // least one other non-empty `CoverageSpan`. let mut has_coverage = BitSet::new_empty(basic_coverage_blocks.num_nodes()); for covspan in &refined_spans { - if ! covspan.span.is_empty() { + if !covspan.span.is_empty() { has_coverage.insert(covspan.bcb); } } refined_spans.retain(|covspan| { - !( - covspan.span.is_empty() + !(covspan.span.is_empty() && is_goto(&basic_coverage_blocks[covspan.bcb].terminator(mir_body).kind) - && has_coverage.contains(covspan.bcb) - ) + && has_coverage.contains(covspan.bcb)) }); // Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage @@ -2484,16 +2583,9 @@ impl<'a, 'tcx> CoverageSpanRefinery<'a, 'tcx> { } } - - - // prev and any/all pending_dups are equal in non-dominance over the other. - // So when comparing to prev, need to also compare to all pending dups. - - - - -// TODO(richkadel): UPDATE COMMENT IF WE ARE CHANGING THIS LOGIC! - /// Called if `curr.span` = `prev.span` (and all `pending_dups` spans, if any). + /// Called if `curr.span` equals `prev_original_span` (and potentially equal to all + /// `pending_dups` spans, if any); but keep in mind, `prev.span` may start at a `Span.lo()` that + /// is less than (further left of) `prev_original_span.lo()`. /// /// When two `CoverageSpan`s have the same `Span`, dominated spans can be discarded; but if /// neither `CoverageSpan` dominates the other, both (or possibly more than two) are held, @@ -2519,52 +2611,28 @@ impl<'a, 'tcx> CoverageSpanRefinery<'a, 'tcx> { } if self.curr().is_dominated_by(self.prev(), self.bcb_dominators) { - // Keep in mind, if discarding `prev`, that this method is called if - // `curr.span` == `prev_original_span`, which may not be the full `prev.span` (if merged - // during the previous iteration). debug!( " different bcbs but SAME spans, and prev dominates curr. Discard prev={:?}", self.prev() ); - // TODO(richkadel): remove? - // let discarded_prev = self.take_prev(); self.cutoff_prev_at_overlapping_curr(); -// TODO(richkadel): remove? -// if self.curr().is_dominated_by(self.prev(), self.bcb_dominators) { -// IF WE KEEP IT, WE REALLY SHOULD CHECK ALL: -// if self.pending_dups.iter().chain(self.some_prev.iter()).any(|dup| -// self.curr().is_dominated_by(dup, self.bcb_dominators)) { - -// TODO(richkadel): UPDATE COMMENT IF WE ARE CHANGING THIS LOGIC! - // If one span dominates the other, assocate the span with the dominator only. - // - // For example: - // match somenum { - // x if x < 1 => { ... } - // }... - // The span for the first `x` is referenced by both the pattern block (every - // time it is evaluated) and the arm code (only when matched). The counter - // will be applied only to the dominator block. - // - // The dominator's (`prev`) execution count may be higher than the dominated - // block's execution count, so drop `curr`. - - // **** MAYBE UNCOMMENT, OR REMOVE IF NEW debug! above *** TODO(richkadel): uncomment debug! - // debug!( - // " different bcbs but SAME spans, and prev dominates curr. Drop curr and \ - // keep prev for next iter. prev={:?}", - // self.prev() - // ); - -// TODO(richkadel): Update logic and remove the following line if it works because this may have -// worked for the example above, but it seems to be consuming the `?` (try operator) as part of -// the call span that dominates it. - -// self.discard_curr(); - -// TODO(richkadel): If I don't change it, should I have checked not only prev dominates curr but -// (if not) also check any of the pending dups dominates curr? - + // If one span dominates the other, assocate the span with the code from the dominated + // block only (`curr`), and discard the overlapping portion of the `prev` span. (Note + // that if `prev.span` is wider than `prev_original_span`, a `CoverageSpan` will still + // be created for `prev`s block, for the non-overlapping portion, left of `curr.span`.) + // + // For example: + // match somenum { + // x if x < 1 => { ... } + // }... + // + // The span for the first `x` is referenced by both the pattern block (every time it is + // evaluated) and the arm code (only when matched). The counter will be applied only to + // the dominated block. This allows coverage to track and highlight things like the + // assignment of `x` above, if the branch is matched, making `x` available to the arm + // code; and to track and highlight the question mark `?` "try" operator at the end of + // a function call returning a `Result`, so the `?` is covered when the function returns + // an `Err`, and not counted as covered if the function always returns `Ok`. } else { // Save `prev` in `pending_dups`. (`curr` will become `prev` in the next iteration.) // If the `curr` CoverageSpan is later discarded, `pending_dups` can be discarded as @@ -2668,40 +2736,34 @@ fn filtered_terminator_span(terminator: &'a Terminator<'tcx>, body_span: Span) - | TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } | TerminatorKind::SwitchInt { .. } -// TODO(richkadel): Should Goto still be here? Delete if not -// | TerminatorKind::Goto { .. } // For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`. + // FIXME(richkadel): Note that `Goto` was moved to it's own match arm, for the reasons + // described below. Add tests to confirm whether or not similar cases also apply to + // `FalseEdge`. | TerminatorKind::FalseEdge { .. } => None, -// TODO(richkadel): Add a comment if this works. Any `Goto` still in the BCB CFG is probably -// required, as an intermediate target for some conditional branches (typically, `SwitchInt` -// targets). Some of these `Goto` branch blocks (BCB and BB) have multiple incoming edges (that is, -// not just a single incoming edge from a SwitchInt), so they are often required to accurately -// represent the control flow. -// -// Since these retained `Goto` terminators generally impact control flow, they often generate their -// own `BasicCoverageBlock`, but their `source_info.span` typically covers the source code for an -// entire branch (that might or might not be executed, depending on the condition that may (or may -// not) lead to the `Goto` branch. This source span is far too broad. For `Goto` terminators that -// are part of a BCB with other statements/terminators with usable `Span`s, ignoring the `Goto` -// `Span` is OK, because the other statements/terminators in the BCB generate `CoverageSpans` -// that represent the code executed leading up to (and including) that `Goto`. -// -// But for `Goto` terminators without other statements/terminators, there needs to be some visible -// region to count, that doesn't include code that was not executed. So in this case, we set the -// `Span` to the 0-length span from and to `source_info.span.hi()`. -// -// At least one character will be covered--the character immediately following the `Span` position. -// (`make_code_region()` ensures all zero-length spans are extended to at least one character.) -// -// Note that, if we did not select some non-zero length `Span` at this location, but the `Goto`s -// BCB still needed to be counted (other `CoverageSpan`-dependent `Expression`s may depend on the -// execution count for this `Goto`), we have at least two other less-appealing choices: -// 1. Insert a truly zero-length span on some line. In this case, there may be no visible -// coverage region, but its line is still counted, which is more confusing. -// 2. Insert a span for a line beyond the end of the file. This works well for `llvm-cov show` -// reports, but `llvm-cov export` reports still count the lines and regions outside the -// file's line range. + // FIXME(richkadel): Note that `Goto` was initially filtered out (by returning `None`, as + // with the `TerminatorKind`s above) because its `Span` was way to broad to be beneficial, + // and, at the time, `Goto` didn't seem to provide any additional contributions to the + // coverage analysis. Upon further review, `Goto` terminated blocks do appear to benefit + // the coverage analysis, and the BCB CFG. To overcome the issues with the `Spans`, the + // coverage algorithms--and the final coverage map generation--include some exceptional + // behaviors. + // + // `Goto`s are often the targets of `SwitchInt` branches, and certain important + // optimizations to replace some `Counter`s with `Expression`s require a separate + // `BasicCoverageBlock` for each branch, to support the `Counter`, when needed. + // + // Also, some test cases showed that `Goto` terminators, and to some degree their `Span`s, + // provided useful context for coverage, such as to count and show when `if` blocks + // _without_ `else` blocks execute the `false` case (counting when the body of the `if` + // was _not_ taken). In these cases, the `Goto` span is ultimately given a `CoverageSpan` + // of 1 character, at the end of it's original `Span`. + // + // However, in other cases, a visible `CoverageSpan` is not wanted, but the `Goto` + // block must still be counted (for example, to contribute its count to an `Expression` + // that reports the execution count for some other block). The code region for the `Goto` + // counters, in these cases, is created using `make_non_reportable_code_region()`. TerminatorKind::Goto { .. } => { Some(function_source_span(terminator.source_info.span.shrink_to_hi(), body_span)) } @@ -2726,39 +2788,24 @@ fn function_source_span(span: Span, body_span: Span) -> Span { if body_span.contains(span) { span } else { body_span } } -// TODO(richkadel): Update this function comment. -// It only works to use an empty span on an actual source line if the line already has -// a coverage code region. It works because -// compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs -// knows to look for empty spans, and generate a `GapRegion` instead of a `CodeRegion`. -// -/// Make a non-reportable code region -/// Count empty spans, but don't make them reportable as coverage. Set the source -/// position out of range. (Note that `llvm-cov` fails to report coverage if any -/// coverage region starts line `0`, so we are using a line number just beyond -/// the last line of the file.) +/// Make a non-reportable code region. (Used coverage-generated "edge counters", and for some +/// `Goto`-terminated blocks.) +/// +/// Only the `Span`s `hi()` position is used, and an empty `Span` at that location is generated, +/// for coverage counting. +/// +/// The coverage map generation phase (part of codegen) knows to report any `Counter` or +/// `Expression` with an empty span as what LLVM's Coverage Mapping Specification calls a +/// `GapRegion`, rather than a `CodeRegion` (used in all other cases). Counters associated with a +/// `GapRegion` still contribute their counter values to other expressions, but if the `GapRegion`s +/// line has any other `CodeRegion` covering the same line, the `GapRegion` will not be counted +/// toward the line execution count. This prevents double-counting line execution, which would +/// otherwise occur. fn make_non_reportable_code_region( file_name: Symbol, -// last_line: usize, -// last_col: CharPos, source_file: &Lrc, span: Span, ) -> CodeRegion { - // let line_past_end_of_file = (source_file.lines.len() + 1) as u32; - // CodeRegion { - // file_name, - // start_line: line_past_end_of_file, - // start_col: 1, - // end_line: line_past_end_of_file, - // end_col: 1, - // } - // CodeRegion { - // file_name, - // start_line: last_line as u32, - // start_col: last_col.to_u32() + 1, - // end_line: last_line as u32, - // end_col: last_col.to_u32() + 1, - // } let (start_line, start_col) = source_file.lookup_file_pos(span.hi()); let (end_line, end_col) = (start_line, start_col); CodeRegion { @@ -2828,7 +2875,10 @@ fn hash( stable_hasher.finish() } -fn bcb_filtered_successors<'a, 'tcx>(body: &'tcx &'a mir::Body<'tcx>, term_kind: &'tcx TerminatorKind<'tcx>) -> Box + 'a> { +fn bcb_filtered_successors<'a, 'tcx>( + body: &'tcx &'a mir::Body<'tcx>, + term_kind: &'tcx TerminatorKind<'tcx>, +) -> Box + 'a> { let mut successors = term_kind.successors(); box match &term_kind { // SwitchInt successors are never unwind, and all of them should be traversed. @@ -2837,13 +2887,17 @@ fn bcb_filtered_successors<'a, 'tcx>(body: &'tcx &'a mir::Body<'tcx>, term_kind: // NOTE: `chain(&[])` is required to coerce the `option::iter` (from // `next().into_iter()`) into the `mir::Successors` aliased type. _ => successors.next().into_iter().chain(&[]), - }.filter(move |&&successor| body[successor].terminator().kind != TerminatorKind::Unreachable) + } + .filter(move |&&successor| body[successor].terminator().kind != TerminatorKind::Unreachable) } pub struct ShortCircuitPreorder< 'a, 'tcx, - F: Fn(&'tcx &'a mir::Body<'tcx>, &'tcx TerminatorKind<'tcx>) -> Box + 'a>, + F: Fn( + &'tcx &'a mir::Body<'tcx>, + &'tcx TerminatorKind<'tcx>, + ) -> Box + 'a>, > { body: &'tcx &'a mir::Body<'tcx>, visited: BitSet, @@ -2851,8 +2905,14 @@ pub struct ShortCircuitPreorder< filtered_successors: F, } -impl<'a, 'tcx, F: Fn(&'tcx &'a mir::Body<'tcx>, &'tcx TerminatorKind<'tcx>) -> Box + 'a>> - ShortCircuitPreorder<'a, 'tcx, F> +impl< + 'a, + 'tcx, + F: Fn( + &'tcx &'a mir::Body<'tcx>, + &'tcx TerminatorKind<'tcx>, + ) -> Box + 'a>, +> ShortCircuitPreorder<'a, 'tcx, F> { pub fn new( body: &'tcx &'a mir::Body<'tcx>, @@ -2869,8 +2929,14 @@ impl<'a, 'tcx, F: Fn(&'tcx &'a mir::Body<'tcx>, &'tcx TerminatorKind<'tcx>) -> B } } -impl<'a: 'tcx, 'tcx, F: Fn(&'tcx &'a mir::Body<'tcx>, &'tcx TerminatorKind<'tcx>) -> Box + 'a>> Iterator - for ShortCircuitPreorder<'a, 'tcx, F> +impl< + 'a: 'tcx, + 'tcx, + F: Fn( + &'tcx &'a mir::Body<'tcx>, + &'tcx TerminatorKind<'tcx>, + ) -> Box + 'a>, +> Iterator for ShortCircuitPreorder<'a, 'tcx, F> { type Item = (BasicBlock, &'a BasicBlockData<'tcx>); @@ -2897,11 +2963,3 @@ impl<'a: 'tcx, 'tcx, F: Fn(&'tcx &'a mir::Body<'tcx>, &'tcx TerminatorKind<'tcx> (size, Some(size)) } } - -// TODO(richkadel): try_error_result.rs -// When executing the Result as Try>::from_error() returns to one or more -// Goto that then targets Return. -// Both the Goto (after error) and the Return have coverage at the last -// bytepos, 0-length Span. -// -// How should I eliminate one, and which one, to avoid counting both. diff --git a/compiler/rustc_mir/src/util/generic_graphviz.rs b/compiler/rustc_mir/src/util/generic_graphviz.rs index 7576c32af1962..91499bb61c287 100644 --- a/compiler/rustc_mir/src/util/generic_graphviz.rs +++ b/compiler/rustc_mir/src/util/generic_graphviz.rs @@ -3,9 +3,11 @@ use rustc_graphviz as dot; use rustc_middle::ty::TyCtxt; use std::io::{self, Write}; -pub struct GraphvizWriter<'a, G: graph::DirectedGraph + graph::WithSuccessors + graph::WithStartNode + graph::WithNumNodes, -NodeContentFn: Fn(::Node) -> Vec, -EdgeLabelsFn: Fn(::Node) -> Vec +pub struct GraphvizWriter< + 'a, + G: graph::DirectedGraph + graph::WithSuccessors + graph::WithStartNode + graph::WithNumNodes, + NodeContentFn: Fn(::Node) -> Vec, + EdgeLabelsFn: Fn(::Node) -> Vec, > { graph: &'a G, is_subgraph: bool, @@ -15,11 +17,19 @@ EdgeLabelsFn: Fn(::Node) -> Ve edge_labels_fn: EdgeLabelsFn, } -impl<'a, G: graph::DirectedGraph + graph::WithSuccessors + graph::WithStartNode + graph::WithNumNodes, -NodeContentFn: Fn(::Node) -> Vec, -EdgeLabelsFn: Fn(::Node) -> Vec -> GraphvizWriter<'a, G, NodeContentFn, EdgeLabelsFn> { - pub fn new(graph: &'a G, graphviz_name: &str, node_content_fn: NodeContentFn, edge_labels_fn: EdgeLabelsFn) -> Self { +impl< + 'a, + G: graph::DirectedGraph + graph::WithSuccessors + graph::WithStartNode + graph::WithNumNodes, + NodeContentFn: Fn(::Node) -> Vec, + EdgeLabelsFn: Fn(::Node) -> Vec, +> GraphvizWriter<'a, G, NodeContentFn, EdgeLabelsFn> +{ + pub fn new( + graph: &'a G, + graphviz_name: &str, + node_content_fn: NodeContentFn, + edge_labels_fn: EdgeLabelsFn, + ) -> Self { Self { graph, is_subgraph: false, @@ -30,7 +40,12 @@ EdgeLabelsFn: Fn(::Node) -> Ve } } - pub fn new_subgraph(graph: &'a G, graphviz_name: &str, node_content_fn: NodeContentFn, edge_labels_fn: EdgeLabelsFn) -> Self { + pub fn new_subgraph( + graph: &'a G, + graphviz_name: &str, + node_content_fn: NodeContentFn, + edge_labels_fn: EdgeLabelsFn, + ) -> Self { Self { graph, is_subgraph: true, @@ -46,16 +61,12 @@ EdgeLabelsFn: Fn(::Node) -> Ve } /// Write a graphviz DOT of the graph - pub fn write_graphviz<'tcx, W>( - &self, - tcx: TyCtxt<'tcx>, - w: &mut W, - ) -> io::Result<()> + pub fn write_graphviz<'tcx, W>(&self, tcx: TyCtxt<'tcx>, w: &mut W) -> io::Result<()> where - W: Write + W: Write, { let kind = if self.is_subgraph { "subgraph" } else { "digraph" }; - let cluster = if self.is_subgraph { "cluster_" } else { "" }; // Prints a border around graph + let cluster = if self.is_subgraph { "cluster_" } else { "" }; // Print border around graph // FIXME(richkadel): If/when migrating the MIR graphviz to this generic implementation, // prepend "Mir_" to the graphviz_safe_def_name(def_id) writeln!(w, "{} {}{} {{", kind, cluster, self.graphviz_name)?; @@ -96,14 +107,9 @@ EdgeLabelsFn: Fn(::Node) -> Ve } /// Write a graphviz DOT node for the given node. - pub fn write_node( - &self, - node: G::Node, - dark_mode: bool, - w: &mut W, - ) -> io::Result<()> + pub fn write_node(&self, node: G::Node, dark_mode: bool, w: &mut W) -> io::Result<()> where - W: Write + W: Write, { // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables. write!(w, r#" {} [shape="none", label=<"#, self.node(node))?; @@ -129,7 +135,11 @@ EdgeLabelsFn: Fn(::Node) -> Ve )?; for section in (self.node_content_fn)(node) { - write!(w, r#"{}"#, dot::escape_html(§ion).replace("\n", "
"))?; + write!( + w, + r#"{}"#, + dot::escape_html(§ion).replace("\n", "
") + )?; } // Close the table @@ -142,7 +152,7 @@ EdgeLabelsFn: Fn(::Node) -> Ve /// Write graphviz DOT edges with labels between the given node and all of its successors. fn write_edges(&self, source: G::Node, w: &mut W) -> io::Result<()> where - W: Write + W: Write, { let edge_labels = (self.edge_labels_fn)(source); for (index, target) in self.graph.successors(source).enumerate() { @@ -162,7 +172,7 @@ EdgeLabelsFn: Fn(::Node) -> Ve /// will appear below the graph. fn write_graph_label(&self, label: &str, w: &mut W) -> io::Result<()> where - W: Write + W: Write, { let lines = label.split("\n").map(|s| dot::escape_html(s)).collect::>(); let escaped_label = lines.join(r#"
"#); @@ -172,4 +182,4 @@ EdgeLabelsFn: Fn(::Node) -> Ve fn node(&self, node: G::Node) -> String { format!("{:?}__{}", node, self.graphviz_name) } -} \ No newline at end of file +} diff --git a/src/test/mir-opt/coverage_graphviz.rs b/src/test/mir-opt/coverage_graphviz.rs index 7bb6d33bee900..b3c90c528377d 100644 --- a/src/test/mir-opt/coverage_graphviz.rs +++ b/src/test/mir-opt/coverage_graphviz.rs @@ -17,4 +17,4 @@ fn main() { #[inline(never)] fn bar() -> bool { true -} \ No newline at end of file +} From 643689212bfd17ead40b68c0a81bc1491d634853 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Sat, 17 Oct 2020 21:49:42 -0700 Subject: [PATCH 11/18] Make CodeRegions optional for Counters too I relized the "hack" with GapRegions was completely unnecessary. It is possible to inject counters (`llvm.instrprof.increment` intrinsic calls without corresponding code regions in the coverage map. An expression can still uses these counter values, solving the problem I thought I needed GapRegions for. --- .../src/coverageinfo/mapgen.rs | 36 ++---- .../rustc_codegen_ssa/src/coverageinfo/map.rs | 8 +- .../rustc_codegen_ssa/src/mir/coverageinfo.rs | 19 ++-- .../src/transform/instrument_coverage.rs | 106 ++++++------------ ...expected_export_coverage.lazy_boolean.json | 8 +- ...ed_export_coverage.various_conditions.json | 8 +- .../expected_show_coverage.lazy_boolean.txt | 3 +- ...ected_show_coverage.various_conditions.txt | 3 +- ...ed_show_coverage_counters.lazy_boolean.txt | 20 +--- ...how_coverage_counters.try_error_result.txt | 2 - ...w_coverage_counters.various_conditions.txt | 69 +++--------- ...w_coverage_counters.while_early_return.txt | 2 - ...expected_export_coverage.lazy_boolean.json | 8 +- ...ed_export_coverage.various_conditions.json | 8 +- .../expected_show_coverage.lazy_boolean.txt | 3 +- ...ected_show_coverage.various_conditions.txt | 3 +- ...ed_show_coverage_counters.lazy_boolean.txt | 20 +--- ...how_coverage_counters.try_error_result.txt | 2 - ...w_coverage_counters.various_conditions.txt | 69 +++--------- ...w_coverage_counters.while_early_return.txt | 2 - 20 files changed, 123 insertions(+), 276 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 8bc6e8bf87dcd..9cb6f1c469af8 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -126,33 +126,15 @@ impl CoverageMapGenerator { let (filenames_index, _) = self.filenames.insert_full(c_filename); virtual_file_mapping.push(filenames_index as u32); } - debug!("Adding counter {:?} to map for {:?}", counter, region,); - if start_line == end_line && start_col == end_col { - // The MIR `InstrumentCoverage` pass generates empty spans _ONLY_ for `BasicBlocks` - // that contribute to the counts of `CoverageKind::Expression`s, but don't represent - // specific source code to count. - // - // Make empty spans `GapRegion`s so their region executions counts are still - // available to any expressions that might reference them, but they don't affect - // the line execution count (as long as the line has at least one other counter). - mapping_regions.push(CounterMappingRegion::gap_region( - counter, - current_file_id, - start_line, - start_col, - end_line, - end_col, - )); - } else { - mapping_regions.push(CounterMappingRegion::code_region( - counter, - current_file_id, - start_line, - start_col, - end_line, - end_col, - )); - } + debug!("Adding counter {:?} to map for {:?}", counter, region); + mapping_regions.push(CounterMappingRegion::code_region( + counter, + current_file_id, + start_line, + start_col, + end_line, + end_col, + )); } // Encode and append the current function's coverage mapping data diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs index 61d12dc036871..b3f5b7c6a0aa2 100644 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs @@ -149,12 +149,10 @@ impl FunctionCoverage { if id == ExpressionOperandId::ZERO { Some(Counter::zero()) } else if id.index() < self.counters.len() { + // Note: Some codegen-injected Counters may be only referenced by `Expression`s, + // and may not have their own `CodeRegion`s, let index = CounterValueReference::from(id.index()); - self.counters - .get(index) - .expect("counter id is out of range") - .as_ref() - .map(|_| Counter::counter_value_reference(index)) + Some(Counter::counter_value_reference(index)) } else { let index = self.expression_index(u32::from(id)); self.expressions diff --git a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs index 17fa4c8f3824b..9daf097eaf2b4 100644 --- a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs @@ -10,24 +10,23 @@ 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 } => { - bx.add_coverage_counter( - self.instance, - function_source_hash, - id, - code_region.expect("counters always have code regions"), - ); - let coverageinfo = bx.tcx().coverageinfo(self.instance.def_id()); let fn_name = bx.create_pgo_func_name_var(self.instance); let hash = bx.const_u64(function_source_hash); let num_counters = bx.const_u32(coverageinfo.num_counters); - let id = bx.const_u32(u32::from(id)); + let index = bx.const_u32(u32::from(id)); debug!( "codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})", - fn_name, hash, num_counters, id, + fn_name, hash, num_counters, index, ); - bx.instrprof_increment(fn_name, hash, num_counters, id); + bx.instrprof_increment(fn_name, hash, num_counters, index); + + if let Some(code_region) = code_region { + bx.add_coverage_counter(self.instance, function_source_hash, id, code_region); + } + // Note: Some counters do not have code regions, but may still be referenced from + // expressions. } CoverageKind::Expression { id, lhs, op, rhs } => { bx.add_coverage_counter_expression(self.instance, id, lhs, op, rhs, code_region); diff --git a/compiler/rustc_mir/src/transform/instrument_coverage.rs b/compiler/rustc_mir/src/transform/instrument_coverage.rs index 9cb9bb9701d18..11f20f143d0fa 100644 --- a/compiler/rustc_mir/src/transform/instrument_coverage.rs +++ b/compiler/rustc_mir/src/transform/instrument_coverage.rs @@ -1244,12 +1244,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } else { bug!("Every BasicCoverageBlock should have a Counter or Expression"); }; - debug!( - "Injecting {} at: {:?}:\n{}\n==========", - self.format_counter(&counter_kind), - span, - source_map.span_to_snippet(span).expect("Error getting source for span"), - ); if let Some(bcb_to_coverage_spans_with_counters) = debug_bcb_to_coverage_spans_with_counters.as_mut() { @@ -1258,25 +1252,12 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { .or_insert_with(|| Vec::new()) .push((covspan.clone(), counter_kind.clone())); } - let mut code_region = None; - if span.hi() == body_span.hi() { - // All functions execute a `Return`-terminated `BasicBlock`, regardless of how the - // function returns; but only some functions also _can_ return after a `Goto` block - // that ends on the closing brace of the function (with the `Return`). When this - // happens, the last character is counted 2 (or possibly more) times, when we know - // the function returned only once (of course). By giving all `Goto` terminators at - // the end of a function a `non-reportable` code region, they are still counted - // if appropriate, but they don't increment the line counter, as long as their is - // also a `Return` on that last line. - if let TerminatorKind::Goto { .. } = self.bcb_terminator(bcb).kind { - code_region = - Some(make_non_reportable_code_region(file_name, &source_file, span)); - } - } - if code_region.is_none() { - code_region = Some(make_code_region(file_name, &source_file, span, body_span)); + let some_code_region = if self.is_code_region_redundant(bcb, span, body_span) { + None + } else { + Some(make_code_region(file_name, &source_file, span, body_span)) }; - self.inject_statement(counter_kind, self.bcb_last_bb(bcb), code_region.unwrap()); + self.inject_statement(counter_kind, self.bcb_last_bb(bcb), some_code_region); } // The previous step looped through the `CoverageSpan`s and injected the counter from the @@ -1423,18 +1404,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { target_bb }; - let span = self.mir_body[inject_to_bb].terminator().source_info.span; - debug!( - "make_non_reportable_code_region for {:?} at span={:?}, counter={}", - inject_to_bb, - span, - self.format_counter(&counter_kind) - ); - self.inject_statement( - counter_kind, - inject_to_bb, - make_non_reportable_code_region(file_name, &source_file, span), - ); + self.inject_statement(counter_kind, inject_to_bb, None); } CoverageKind::Expression { .. } => { self.inject_intermediate_expression(counter_kind) @@ -1583,6 +1553,28 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } } + fn is_code_region_redundant( + &self, + bcb: BasicCoverageBlock, + span: Span, + body_span: Span, + ) -> bool { + if span.hi() == body_span.hi() { + // All functions execute a `Return`-terminated `BasicBlock`, regardless of how the + // function returns; but only some functions also _can_ return after a `Goto` block + // that ends on the closing brace of the function (with the `Return`). When this + // happens, the last character is counted 2 (or possibly more) times, when we know + // the function returned only once (of course). By giving all `Goto` terminators at + // the end of a function a `non-reportable` code region, they are still counted + // if appropriate, but they don't increment the line counter, as long as their is + // also a `Return` on that last line. + if let TerminatorKind::Goto { .. } = self.bcb_terminator(bcb).kind { + return true; + } + } + false + } + /// Traverse the BCB CFG and add either a `Counter` or `Expression` to ever BCB, to be /// injected with `CoverageSpan`s. `Expressions` have no runtime overhead, so if a viable /// expression (adding or subtracting two other counters or expressions) can compute the same @@ -2139,16 +2131,19 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { &mut self, counter_kind: CoverageKind, bb: BasicBlock, - code_region: CodeRegion, + some_code_region: Option, ) { - debug!(" injecting statement {:?} covering {:?}", counter_kind, code_region); + debug!( + " injecting statement {:?} for {:?} at code region: {:?}", + counter_kind, bb, some_code_region + ); let data = &mut self.mir_body[bb]; let source_info = data.terminator().source_info; let statement = Statement { source_info, kind: StatementKind::Coverage(box Coverage { kind: counter_kind, - code_region: Some(code_region), + code_region: some_code_region, }), }; data.statements.push(statement); @@ -2762,8 +2757,8 @@ fn filtered_terminator_span(terminator: &'a Terminator<'tcx>, body_span: Span) - // // However, in other cases, a visible `CoverageSpan` is not wanted, but the `Goto` // block must still be counted (for example, to contribute its count to an `Expression` - // that reports the execution count for some other block). The code region for the `Goto` - // counters, in these cases, is created using `make_non_reportable_code_region()`. + // that reports the execution count for some other block). In these cases, the code region + // is set to `None`. TerminatorKind::Goto { .. } => { Some(function_source_span(terminator.source_info.span.shrink_to_hi(), body_span)) } @@ -2788,35 +2783,6 @@ fn function_source_span(span: Span, body_span: Span) -> Span { if body_span.contains(span) { span } else { body_span } } -/// Make a non-reportable code region. (Used coverage-generated "edge counters", and for some -/// `Goto`-terminated blocks.) -/// -/// Only the `Span`s `hi()` position is used, and an empty `Span` at that location is generated, -/// for coverage counting. -/// -/// The coverage map generation phase (part of codegen) knows to report any `Counter` or -/// `Expression` with an empty span as what LLVM's Coverage Mapping Specification calls a -/// `GapRegion`, rather than a `CodeRegion` (used in all other cases). Counters associated with a -/// `GapRegion` still contribute their counter values to other expressions, but if the `GapRegion`s -/// line has any other `CodeRegion` covering the same line, the `GapRegion` will not be counted -/// toward the line execution count. This prevents double-counting line execution, which would -/// otherwise occur. -fn make_non_reportable_code_region( - file_name: Symbol, - source_file: &Lrc, - span: Span, -) -> CodeRegion { - let (start_line, start_col) = source_file.lookup_file_pos(span.hi()); - let (end_line, end_col) = (start_line, start_col); - CodeRegion { - file_name, - start_line: start_line as u32, - start_col: start_col.to_u32() + 1, - end_line: end_line as u32, - end_col: end_col.to_u32() + 1, - } -} - /// Convert the Span into its file name, start line and column, and end line and column fn make_code_region( file_name: Symbol, diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.lazy_boolean.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.lazy_boolean.json index 0d81693814606..5a953b90b423f 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.lazy_boolean.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.lazy_boolean.json @@ -17,8 +17,8 @@ }, "lines": { "count": 40, - "covered": 29, - "percent": 72.5 + "covered": 30, + "percent": 75 }, "regions": { "count": 37, @@ -42,8 +42,8 @@ }, "lines": { "count": 40, - "covered": 29, - "percent": 72.5 + "covered": 30, + "percent": 75 }, "regions": { "count": 37, diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.various_conditions.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.various_conditions.json index 1d239867b801a..d93458b614372 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.various_conditions.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.various_conditions.json @@ -17,8 +17,8 @@ }, "lines": { "count": 49, - "covered": 24, - "percent": 48.97959183673469 + "covered": 23, + "percent": 46.93877551020408 }, "regions": { "count": 69, @@ -42,8 +42,8 @@ }, "lines": { "count": 49, - "covered": 24, - "percent": 48.97959183673469 + "covered": 23, + "percent": 46.93877551020408 }, "regions": { "count": 69, diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.lazy_boolean.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.lazy_boolean.txt index eff74aa3b82f6..1b503033911c5 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.lazy_boolean.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.lazy_boolean.txt @@ -18,7 +18,8 @@ 17| | = 18| 1| a < b 19| | || - 20| 0| b < c + 20| 1| b < c + ^0 21| | ; 22| | let 23| 1| somebool diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.various_conditions.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.various_conditions.txt index bd52c52a66840..173ff4aa4c481 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.various_conditions.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.various_conditions.txt @@ -27,8 +27,7 @@ 27| | 28| 1| if countdown > 7 { 29| 1| countdown -= 4; - 30| 1| } else if countdown > 2 { - ^0 + 30| 0| } else if countdown > 2 { 31| 0| if countdown < 1 || countdown > 5 || countdown != 9 { 32| 0| countdown = 0; 33| 0| } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.lazy_boolean.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.lazy_boolean.txt index 57b8b9b3c49f6..e955fcd8ba05e 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.lazy_boolean.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.lazy_boolean.txt @@ -6,29 +6,23 @@ Counter in file 0 14:6 -> 14:7, (#1 - #2) Counter in file 0 16:9 -> 16:17, ((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) Counter in file 0 18:13 -> 18:18, (#2 + (#1 - #2)) Counter in file 0 20:13 -> 20:18, ((#2 + (#1 - #2)) - #3) -Counter in file 0 20:18 -> 20:19, (((#2 + (#1 - #2)) - #3) - #4) Counter in file 0 20:18 -> 20:19, (#3 + #4) -Counter in file 0 20:18 -> 20:18, #3 -Counter in file 0 20:18 -> 20:18, #4 +Counter in file 0 20:18 -> 20:19, (((#2 + (#1 - #2)) - #3) - #4) Counter in file 0 23:9 -> 23:17, ((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) Counter in file 0 25:13 -> 25:18, (((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) + 0) Counter in file 0 27:13 -> 27:18, (((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) Counter in file 0 27:18 -> 27:19, (#5 + #6) Counter in file 0 27:18 -> 27:19, ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6) -Counter in file 0 27:18 -> 27:18, #5 -Counter in file 0 27:18 -> 27:18, #6 Counter in file 0 29:9 -> 29:17, ((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) Counter in file 0 29:20 -> 29:25, (((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) + 0) Counter in file 0 29:29 -> 29:34, #7 Counter in file 0 29:34 -> 29:35, ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8) Counter in file 0 29:34 -> 29:35, (#7 - #8) -Counter in file 0 29:34 -> 29:34, #8 Counter in file 0 30:9 -> 30:17, ((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) Counter in file 0 30:20 -> 30:25, (((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) + 0) Counter in file 0 30:29 -> 30:34, #9 Counter in file 0 30:34 -> 30:35, ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10) Counter in file 0 30:34 -> 30:35, (#9 - #10) -Counter in file 0 30:34 -> 30:34, #10 Counter in file 0 33:9 -> 34:16, (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) + 0) Counter in file 0 35:5 -> 38:6, #11 Counter in file 0 38:6 -> 38:7, (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11) @@ -52,22 +46,18 @@ Combined regions: 18:13 -> 18:18 (count=1) 20:13 -> 20:18 (count=0) 20:18 -> 20:19 (count=1) - 20:18 -> 20:18 (count=1) 23:9 -> 23:17 (count=1) 25:13 -> 25:18 (count=1) 27:13 -> 27:18 (count=1) 27:18 -> 27:19 (count=1) - 27:18 -> 27:18 (count=1) 29:9 -> 29:17 (count=1) 29:20 -> 29:25 (count=1) 29:29 -> 29:34 (count=1) 29:34 -> 29:35 (count=1) - 29:34 -> 29:34 (count=0) 30:9 -> 30:17 (count=1) 30:20 -> 30:25 (count=1) 30:29 -> 30:34 (count=0) 30:34 -> 30:35 (count=1) - 30:34 -> 30:34 (count=0) 33:9 -> 34:16 (count=1) 35:5 -> 38:6 (count=0) 38:6 -> 38:7 (count=1) @@ -93,28 +83,28 @@ Segment at 16:17 (count = 0), Skipped Segment at 18:13 (count = 1), RegionEntry Segment at 18:18 (count = 0), Skipped Segment at 20:13 (count = 0), RegionEntry -Segment at 20:18 (count = 1) +Segment at 20:18 (count = 1), RegionEntry Segment at 20:19 (count = 0), Skipped Segment at 23:9 (count = 1), RegionEntry Segment at 23:17 (count = 0), Skipped Segment at 25:13 (count = 1), RegionEntry Segment at 25:18 (count = 0), Skipped Segment at 27:13 (count = 1), RegionEntry -Segment at 27:18 (count = 1) +Segment at 27:18 (count = 1), RegionEntry Segment at 27:19 (count = 0), Skipped Segment at 29:9 (count = 1), RegionEntry Segment at 29:17 (count = 0), Skipped Segment at 29:20 (count = 1), RegionEntry Segment at 29:25 (count = 0), Skipped Segment at 29:29 (count = 1), RegionEntry -Segment at 29:34 (count = 1) +Segment at 29:34 (count = 1), RegionEntry Segment at 29:35 (count = 0), Skipped Segment at 30:9 (count = 1), RegionEntry Segment at 30:17 (count = 0), Skipped Segment at 30:20 (count = 1), RegionEntry Segment at 30:25 (count = 0), Skipped Segment at 30:29 (count = 0), RegionEntry -Segment at 30:34 (count = 1) +Segment at 30:34 (count = 1), RegionEntry Segment at 30:35 (count = 0), Skipped Segment at 33:9 (count = 1), RegionEntry Segment at 34:16 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.try_error_result.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.try_error_result.txt index ede836997ea17..30109a1e20020 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.try_error_result.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.try_error_result.txt @@ -12,7 +12,6 @@ Counter in file 0 32:10 -> 32:11, #3 Counter in file 0 33:6 -> 33:7, (#2 + #3) Counter in file 0 34:5 -> 34:11, #4 Counter in file 0 35:1 -> 35:2, ((#5 + #6) + #4) -Counter in file 0 35:2 -> 35:2, (#5 + #6) Counter in file 0 5:8 -> 5:20, #1 Counter in file 0 6:9 -> 6:16, #2 Counter in file 0 8:9 -> 8:15, (#1 - #2) @@ -35,7 +34,6 @@ Combined regions: 33:6 -> 33:7 (count=5) 34:5 -> 34:11 (count=0) 35:1 -> 35:2 (count=1) - 35:2 -> 35:2 (count=1) Segment at 5:8 (count = 6), RegionEntry Segment at 5:20 (count = 0), Skipped Segment at 6:9 (count = 1), RegionEntry diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.various_conditions.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.various_conditions.txt index fbc29873312fb..b229410a495a3 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.various_conditions.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.various_conditions.txt @@ -5,19 +5,14 @@ Counter in file 0 5:13 -> 7:6, #2 Counter in file 0 10:9 -> 10:10, (#4 + #11) Counter in file 0 10:16 -> 10:29, (#2 + 0) Counter in file 0 11:9 -> 12:10, #4 -Counter in file 0 11:23 -> 11:23, #3 Counter in file 0 13:15 -> 13:28, ((#2 + 0) - #3) Counter in file 0 14:12 -> 14:25, #5 Counter in file 0 14:29 -> 14:42, (#5 - #13) -Counter in file 0 14:42 -> 14:43, ((#5 - #13) - #14) Counter in file 0 14:42 -> 14:43, (#13 + #14) -Counter in file 0 14:42 -> 14:42, #13 -Counter in file 0 14:42 -> 14:42, #14 +Counter in file 0 14:42 -> 14:43, ((#5 - #13) - #14) Counter in file 0 14:46 -> 14:60, #21 -Counter in file 0 14:60 -> 14:61, (#21 - #18) Counter in file 0 14:60 -> 14:61, (#17 + #18) -Counter in file 0 14:60 -> 14:60, #17 -Counter in file 0 14:60 -> 14:60, #18 +Counter in file 0 14:60 -> 14:61, (#21 - #18) Counter in file 0 14:61 -> 16:10, #22 Counter in file 0 16:10 -> 16:11, #23 Counter in file 0 17:9 -> 18:18, #11 @@ -27,19 +22,14 @@ Counter in file 0 24:8 -> 24:12, ((#4 + #11) + 0) Counter in file 0 24:13 -> 26:6, #12 Counter in file 0 28:8 -> 28:21, (#12 + 0) Counter in file 0 29:9 -> 29:23, #16 -Counter in file 0 29:23 -> 29:23, #15 Counter in file 0 30:15 -> 30:28, ((#12 + 0) - #15) Counter in file 0 31:12 -> 31:25, (((#12 + 0) - #15) - #8) Counter in file 0 31:29 -> 31:42, ((((#12 + 0) - #15) - #8) - #24) -Counter in file 0 31:42 -> 31:43, (#24 + #25) Counter in file 0 31:42 -> 31:43, (((((#12 + 0) - #15) - #8) - #24) - #25) -Counter in file 0 31:42 -> 31:42, #25 -Counter in file 0 31:42 -> 31:42, #24 +Counter in file 0 31:42 -> 31:43, (#24 + #25) Counter in file 0 31:46 -> 31:60, #32 Counter in file 0 31:60 -> 31:61, (#28 + #29) Counter in file 0 31:60 -> 31:61, (#32 - #29) -Counter in file 0 31:60 -> 31:60, #28 -Counter in file 0 31:60 -> 31:60, #29 Counter in file 0 31:61 -> 33:10, #33 Counter in file 0 33:10 -> 33:11, #34 Counter in file 0 34:9 -> 34:23, #19 @@ -50,19 +40,14 @@ Counter in file 0 40:13 -> 42:6, #20 Counter in file 0 44:9 -> 44:10, (#27 + #30) Counter in file 0 44:16 -> 44:29, (#20 + 0) Counter in file 0 45:9 -> 45:23, #27 -Counter in file 0 45:23 -> 45:23, #26 Counter in file 0 46:15 -> 46:28, ((#20 + 0) - #26) Counter in file 0 47:12 -> 47:25, (((#20 + 0) - #26) - #7) Counter in file 0 47:29 -> 47:42, ((((#20 + 0) - #26) - #7) - #35) -Counter in file 0 47:42 -> 47:43, (((((#20 + 0) - #26) - #7) - #35) - #36) Counter in file 0 47:42 -> 47:43, (#35 + #36) -Counter in file 0 47:42 -> 47:42, #35 -Counter in file 0 47:42 -> 47:42, #36 +Counter in file 0 47:42 -> 47:43, (((((#20 + 0) - #26) - #7) - #35) - #36) Counter in file 0 47:46 -> 47:60, #41 -Counter in file 0 47:60 -> 47:61, (#41 - #38) Counter in file 0 47:60 -> 47:61, (#37 + #38) -Counter in file 0 47:60 -> 47:60, #38 -Counter in file 0 47:60 -> 47:60, #37 +Counter in file 0 47:60 -> 47:61, (#41 - #38) Counter in file 0 47:61 -> 49:10, #42 Counter in file 0 49:10 -> 49:11, #43 Counter in file 0 50:9 -> 50:23, #30 @@ -70,25 +55,19 @@ Counter in file 0 52:13 -> 54:15, #7 Counter in file 0 57:9 -> 57:10, (#9 + #10) Counter in file 0 57:16 -> 57:29, ((#27 + #30) + 0) Counter in file 0 58:9 -> 58:23, #9 -Counter in file 0 58:23 -> 58:23, #31 Counter in file 0 59:15 -> 59:28, ((#27 + #30) - #31) Counter in file 0 60:12 -> 60:25, (((#27 + #30) - #31) - #6) Counter in file 0 60:29 -> 60:42, ((((#27 + #30) - #31) - #6) - #39) Counter in file 0 60:42 -> 60:43, (#39 + #40) Counter in file 0 60:42 -> 60:43, (((((#27 + #30) - #31) - #6) - #39) - #40) -Counter in file 0 60:42 -> 60:42, #40 -Counter in file 0 60:42 -> 60:42, #39 Counter in file 0 60:46 -> 60:60, #46 -Counter in file 0 60:60 -> 60:61, (#44 + #45) Counter in file 0 60:60 -> 60:61, (#46 - #45) -Counter in file 0 60:60 -> 60:60, #44 -Counter in file 0 60:60 -> 60:60, #45 +Counter in file 0 60:60 -> 60:61, (#44 + #45) Counter in file 0 60:61 -> 62:10, #47 Counter in file 0 62:10 -> 62:11, #48 Counter in file 0 63:9 -> 63:23, #10 Counter in file 0 65:9 -> 65:15, #6 Counter in file 0 67:1 -> 67:2, ((#9 + #10) + (((#6 + #7) + #8) + (((#2 + 0) - #3) - #5))) -Counter in file 0 67:2 -> 67:2, (((#6 + #7) + #8) + (((#2 + 0) - #3) - #5)) Emitting segments for file: ../coverage/various_conditions.rs Combined regions: 4:9 -> 4:26 (count=1) @@ -97,15 +76,12 @@ Combined regions: 10:9 -> 10:10 (count=1) 10:16 -> 10:29 (count=1) 11:9 -> 12:10 (count=1) - 11:23 -> 11:23 (count=1) 13:15 -> 13:28 (count=0) 14:12 -> 14:25 (count=0) 14:29 -> 14:42 (count=0) 14:42 -> 14:43 (count=0) - 14:42 -> 14:42 (count=0) 14:46 -> 14:60 (count=0) 14:60 -> 14:61 (count=0) - 14:60 -> 14:60 (count=0) 14:61 -> 16:10 (count=0) 16:10 -> 16:11 (count=0) 17:9 -> 18:18 (count=0) @@ -115,15 +91,12 @@ Combined regions: 24:13 -> 26:6 (count=1) 28:8 -> 28:21 (count=1) 29:9 -> 29:23 (count=1) - 29:23 -> 29:23 (count=1) 30:15 -> 30:28 (count=0) 31:12 -> 31:25 (count=0) 31:29 -> 31:42 (count=0) 31:42 -> 31:43 (count=0) - 31:42 -> 31:42 (count=0) 31:46 -> 31:60 (count=0) 31:60 -> 31:61 (count=0) - 31:60 -> 31:60 (count=0) 31:61 -> 33:10 (count=0) 33:10 -> 33:11 (count=0) 34:9 -> 34:23 (count=0) @@ -134,15 +107,12 @@ Combined regions: 44:9 -> 44:10 (count=0) 44:16 -> 44:29 (count=1) 45:9 -> 45:23 (count=0) - 45:23 -> 45:23 (count=0) 46:15 -> 46:28 (count=1) 47:12 -> 47:25 (count=0) 47:29 -> 47:42 (count=0) 47:42 -> 47:43 (count=0) - 47:42 -> 47:42 (count=0) 47:46 -> 47:60 (count=0) 47:60 -> 47:61 (count=0) - 47:60 -> 47:60 (count=0) 47:61 -> 49:10 (count=0) 49:10 -> 49:11 (count=0) 50:9 -> 50:23 (count=0) @@ -150,21 +120,17 @@ Combined regions: 57:9 -> 57:10 (count=0) 57:16 -> 57:29 (count=0) 58:9 -> 58:23 (count=0) - 58:23 -> 58:23 (count=0) 59:15 -> 59:28 (count=0) 60:12 -> 60:25 (count=0) 60:29 -> 60:42 (count=0) 60:42 -> 60:43 (count=0) - 60:42 -> 60:42 (count=0) 60:46 -> 60:60 (count=0) 60:60 -> 60:61 (count=0) - 60:60 -> 60:60 (count=0) 60:61 -> 62:10 (count=0) 62:10 -> 62:11 (count=0) 63:9 -> 63:23 (count=0) 65:9 -> 65:15 (count=0) 67:1 -> 67:2 (count=1) - 67:2 -> 67:2 (count=1) Segment at 4:9 (count = 1), RegionEntry Segment at 4:26 (count = 0), Skipped Segment at 5:8 (count = 1), RegionEntry @@ -176,17 +142,16 @@ Segment at 10:10 (count = 0), Skipped Segment at 10:16 (count = 1), RegionEntry Segment at 10:29 (count = 0), Skipped Segment at 11:9 (count = 1), RegionEntry -Segment at 11:23 (count = 1) Segment at 12:10 (count = 0), Skipped Segment at 13:15 (count = 0), RegionEntry Segment at 13:28 (count = 0), Skipped Segment at 14:12 (count = 0), RegionEntry Segment at 14:25 (count = 0), Skipped Segment at 14:29 (count = 0), RegionEntry -Segment at 14:42 (count = 0) +Segment at 14:42 (count = 0), RegionEntry Segment at 14:43 (count = 0), Skipped Segment at 14:46 (count = 0), RegionEntry -Segment at 14:60 (count = 0) +Segment at 14:60 (count = 0), RegionEntry Segment at 14:61 (count = 0), RegionEntry Segment at 16:10 (count = 0), RegionEntry Segment at 16:11 (count = 0), Skipped @@ -203,16 +168,16 @@ Segment at 26:6 (count = 0), Skipped Segment at 28:8 (count = 1), RegionEntry Segment at 28:21 (count = 0), Skipped Segment at 29:9 (count = 1), RegionEntry -Segment at 29:23 (count = 1), Gap +Segment at 29:23 (count = 0), Skipped Segment at 30:15 (count = 0), RegionEntry Segment at 30:28 (count = 0), Skipped Segment at 31:12 (count = 0), RegionEntry Segment at 31:25 (count = 0), Skipped Segment at 31:29 (count = 0), RegionEntry -Segment at 31:42 (count = 0) +Segment at 31:42 (count = 0), RegionEntry Segment at 31:43 (count = 0), Skipped Segment at 31:46 (count = 0), RegionEntry -Segment at 31:60 (count = 0) +Segment at 31:60 (count = 0), RegionEntry Segment at 31:61 (count = 0), RegionEntry Segment at 33:10 (count = 0), RegionEntry Segment at 33:11 (count = 0), Skipped @@ -231,16 +196,16 @@ Segment at 44:10 (count = 0), Skipped Segment at 44:16 (count = 1), RegionEntry Segment at 44:29 (count = 0), Skipped Segment at 45:9 (count = 0), RegionEntry -Segment at 45:23 (count = 0), Gap +Segment at 45:23 (count = 0), Skipped Segment at 46:15 (count = 1), RegionEntry Segment at 46:28 (count = 0), Skipped Segment at 47:12 (count = 0), RegionEntry Segment at 47:25 (count = 0), Skipped Segment at 47:29 (count = 0), RegionEntry -Segment at 47:42 (count = 0) +Segment at 47:42 (count = 0), RegionEntry Segment at 47:43 (count = 0), Skipped Segment at 47:46 (count = 0), RegionEntry -Segment at 47:60 (count = 0) +Segment at 47:60 (count = 0), RegionEntry Segment at 47:61 (count = 0), RegionEntry Segment at 49:10 (count = 0), RegionEntry Segment at 49:11 (count = 0), Skipped @@ -253,16 +218,16 @@ Segment at 57:10 (count = 0), Skipped Segment at 57:16 (count = 0), RegionEntry Segment at 57:29 (count = 0), Skipped Segment at 58:9 (count = 0), RegionEntry -Segment at 58:23 (count = 0), Gap +Segment at 58:23 (count = 0), Skipped Segment at 59:15 (count = 0), RegionEntry Segment at 59:28 (count = 0), Skipped Segment at 60:12 (count = 0), RegionEntry Segment at 60:25 (count = 0), Skipped Segment at 60:29 (count = 0), RegionEntry -Segment at 60:42 (count = 0) +Segment at 60:42 (count = 0), RegionEntry Segment at 60:43 (count = 0), Skipped Segment at 60:46 (count = 0), RegionEntry -Segment at 60:60 (count = 0) +Segment at 60:60 (count = 0), RegionEntry Segment at 60:61 (count = 0), RegionEntry Segment at 62:10 (count = 0), RegionEntry Segment at 62:11 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while_early_return.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while_early_return.txt index 0225b167746cc..a343f5056ac29 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while_early_return.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while_early_return.txt @@ -8,7 +8,6 @@ Counter in file 0 26:21 -> 26:27, #5 Counter in file 0 30:9 -> 32:10, #2 Counter in file 0 35:5 -> 35:11, #3 Counter in file 0 36:1 -> 36:2, ((#4 + #5) + #3) -Counter in file 0 36:2 -> 36:2, (#4 + #5) Emitting segments for file: ../coverage/while_early_return.rs Combined regions: 5:9 -> 5:27 (count=1) @@ -20,7 +19,6 @@ Combined regions: 30:9 -> 32:10 (count=6) 35:5 -> 35:11 (count=0) 36:1 -> 36:2 (count=1) - 36:2 -> 36:2 (count=1) Segment at 5:9 (count = 1), RegionEntry Segment at 5:27 (count = 0), Skipped Segment at 7:9 (count = 7), RegionEntry diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.lazy_boolean.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.lazy_boolean.json index 0d81693814606..5a953b90b423f 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.lazy_boolean.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.lazy_boolean.json @@ -17,8 +17,8 @@ }, "lines": { "count": 40, - "covered": 29, - "percent": 72.5 + "covered": 30, + "percent": 75 }, "regions": { "count": 37, @@ -42,8 +42,8 @@ }, "lines": { "count": 40, - "covered": 29, - "percent": 72.5 + "covered": 30, + "percent": 75 }, "regions": { "count": 37, diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.various_conditions.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.various_conditions.json index 1d239867b801a..d93458b614372 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.various_conditions.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.various_conditions.json @@ -17,8 +17,8 @@ }, "lines": { "count": 49, - "covered": 24, - "percent": 48.97959183673469 + "covered": 23, + "percent": 46.93877551020408 }, "regions": { "count": 69, @@ -42,8 +42,8 @@ }, "lines": { "count": 49, - "covered": 24, - "percent": 48.97959183673469 + "covered": 23, + "percent": 46.93877551020408 }, "regions": { "count": 69, diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.lazy_boolean.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.lazy_boolean.txt index eff74aa3b82f6..1b503033911c5 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.lazy_boolean.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.lazy_boolean.txt @@ -18,7 +18,8 @@ 17| | = 18| 1| a < b 19| | || - 20| 0| b < c + 20| 1| b < c + ^0 21| | ; 22| | let 23| 1| somebool diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.various_conditions.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.various_conditions.txt index bd52c52a66840..173ff4aa4c481 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.various_conditions.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.various_conditions.txt @@ -27,8 +27,7 @@ 27| | 28| 1| if countdown > 7 { 29| 1| countdown -= 4; - 30| 1| } else if countdown > 2 { - ^0 + 30| 0| } else if countdown > 2 { 31| 0| if countdown < 1 || countdown > 5 || countdown != 9 { 32| 0| countdown = 0; 33| 0| } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.lazy_boolean.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.lazy_boolean.txt index 39753bab2857a..191a3a58355d2 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.lazy_boolean.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.lazy_boolean.txt @@ -6,29 +6,23 @@ Counter in file 0 14:6 -> 14:7, (#1 - #2) Counter in file 0 16:9 -> 16:17, ((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) Counter in file 0 18:13 -> 18:18, (#2 + (#1 - #2)) Counter in file 0 20:13 -> 20:18, ((#2 + (#1 - #2)) - #3) -Counter in file 0 20:18 -> 20:19, (((#2 + (#1 - #2)) - #3) - #4) Counter in file 0 20:18 -> 20:19, (#3 + #4) -Counter in file 0 20:18 -> 20:18, #3 -Counter in file 0 20:18 -> 20:18, #4 +Counter in file 0 20:18 -> 20:19, (((#2 + (#1 - #2)) - #3) - #4) Counter in file 0 23:9 -> 23:17, ((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) Counter in file 0 25:13 -> 25:18, (((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) + 0) Counter in file 0 27:13 -> 27:18, (((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) Counter in file 0 27:18 -> 27:19, (#5 + #6) Counter in file 0 27:18 -> 27:19, ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6) -Counter in file 0 27:18 -> 27:18, #5 -Counter in file 0 27:18 -> 27:18, #6 Counter in file 0 29:9 -> 29:17, ((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) Counter in file 0 29:20 -> 29:25, (((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) + 0) Counter in file 0 29:29 -> 29:34, #7 Counter in file 0 29:34 -> 29:35, ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8) Counter in file 0 29:34 -> 29:35, (#7 - #8) -Counter in file 0 29:34 -> 29:34, #8 Counter in file 0 30:9 -> 30:17, ((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) Counter in file 0 30:20 -> 30:25, (((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) + 0) Counter in file 0 30:29 -> 30:34, #9 Counter in file 0 30:34 -> 30:35, ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10) Counter in file 0 30:34 -> 30:35, (#9 - #10) -Counter in file 0 30:34 -> 30:34, #10 Counter in file 0 33:9 -> 34:16, (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) + 0) Counter in file 0 35:5 -> 38:6, #11 Counter in file 0 38:6 -> 38:7, (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11) @@ -52,22 +46,18 @@ Combined regions: 18:13 -> 18:18 (count=1) 20:13 -> 20:18 (count=0) 20:18 -> 20:19 (count=1) - 20:18 -> 20:18 (count=1) 23:9 -> 23:17 (count=1) 25:13 -> 25:18 (count=1) 27:13 -> 27:18 (count=1) 27:18 -> 27:19 (count=1) - 27:18 -> 27:18 (count=1) 29:9 -> 29:17 (count=1) 29:20 -> 29:25 (count=1) 29:29 -> 29:34 (count=1) 29:34 -> 29:35 (count=1) - 29:34 -> 29:34 (count=0) 30:9 -> 30:17 (count=1) 30:20 -> 30:25 (count=1) 30:29 -> 30:34 (count=0) 30:34 -> 30:35 (count=1) - 30:34 -> 30:34 (count=0) 33:9 -> 34:16 (count=1) 35:5 -> 38:6 (count=0) 38:6 -> 38:7 (count=1) @@ -93,28 +83,28 @@ Segment at 16:17 (count = 0), Skipped Segment at 18:13 (count = 1), RegionEntry Segment at 18:18 (count = 0), Skipped Segment at 20:13 (count = 0), RegionEntry -Segment at 20:18 (count = 1) +Segment at 20:18 (count = 1), RegionEntry Segment at 20:19 (count = 0), Skipped Segment at 23:9 (count = 1), RegionEntry Segment at 23:17 (count = 0), Skipped Segment at 25:13 (count = 1), RegionEntry Segment at 25:18 (count = 0), Skipped Segment at 27:13 (count = 1), RegionEntry -Segment at 27:18 (count = 1) +Segment at 27:18 (count = 1), RegionEntry Segment at 27:19 (count = 0), Skipped Segment at 29:9 (count = 1), RegionEntry Segment at 29:17 (count = 0), Skipped Segment at 29:20 (count = 1), RegionEntry Segment at 29:25 (count = 0), Skipped Segment at 29:29 (count = 1), RegionEntry -Segment at 29:34 (count = 1) +Segment at 29:34 (count = 1), RegionEntry Segment at 29:35 (count = 0), Skipped Segment at 30:9 (count = 1), RegionEntry Segment at 30:17 (count = 0), Skipped Segment at 30:20 (count = 1), RegionEntry Segment at 30:25 (count = 0), Skipped Segment at 30:29 (count = 0), RegionEntry -Segment at 30:34 (count = 1) +Segment at 30:34 (count = 1), RegionEntry Segment at 30:35 (count = 0), Skipped Segment at 33:9 (count = 1), RegionEntry Segment at 34:16 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.try_error_result.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.try_error_result.txt index 12b880386b7c7..9cfc05e874a51 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.try_error_result.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.try_error_result.txt @@ -12,7 +12,6 @@ Counter in file 0 32:10 -> 32:11, #3 Counter in file 0 33:6 -> 33:7, (#2 + #3) Counter in file 0 34:5 -> 34:11, #4 Counter in file 0 35:1 -> 35:2, ((#5 + #6) + #4) -Counter in file 0 35:2 -> 35:2, (#5 + #6) Counter in file 0 5:8 -> 5:20, #1 Counter in file 0 6:9 -> 6:16, #2 Counter in file 0 8:9 -> 8:15, (#1 - #2) @@ -35,7 +34,6 @@ Combined regions: 33:6 -> 33:7 (count=5) 34:5 -> 34:11 (count=0) 35:1 -> 35:2 (count=1) - 35:2 -> 35:2 (count=1) Segment at 5:8 (count = 6), RegionEntry Segment at 5:20 (count = 0), Skipped Segment at 6:9 (count = 1), RegionEntry diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.various_conditions.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.various_conditions.txt index 5b94dd3a819f3..ba80cadbd3c8a 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.various_conditions.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.various_conditions.txt @@ -5,19 +5,14 @@ Counter in file 0 5:13 -> 7:6, #2 Counter in file 0 10:9 -> 10:10, (#4 + #11) Counter in file 0 10:16 -> 10:29, (#2 + 0) Counter in file 0 11:9 -> 12:10, #4 -Counter in file 0 11:23 -> 11:23, #3 Counter in file 0 13:15 -> 13:28, ((#2 + 0) - #3) Counter in file 0 14:12 -> 14:25, #5 Counter in file 0 14:29 -> 14:42, (#5 - #13) -Counter in file 0 14:42 -> 14:43, ((#5 - #13) - #14) Counter in file 0 14:42 -> 14:43, (#13 + #14) -Counter in file 0 14:42 -> 14:42, #13 -Counter in file 0 14:42 -> 14:42, #14 +Counter in file 0 14:42 -> 14:43, ((#5 - #13) - #14) Counter in file 0 14:46 -> 14:60, #21 -Counter in file 0 14:60 -> 14:61, (#21 - #18) Counter in file 0 14:60 -> 14:61, (#17 + #18) -Counter in file 0 14:60 -> 14:60, #17 -Counter in file 0 14:60 -> 14:60, #18 +Counter in file 0 14:60 -> 14:61, (#21 - #18) Counter in file 0 14:61 -> 16:10, #22 Counter in file 0 16:10 -> 16:11, #23 Counter in file 0 17:9 -> 18:18, #11 @@ -27,19 +22,14 @@ Counter in file 0 24:8 -> 24:12, ((#4 + #11) + 0) Counter in file 0 24:13 -> 26:6, #12 Counter in file 0 28:8 -> 28:21, (#12 + 0) Counter in file 0 29:9 -> 29:23, #16 -Counter in file 0 29:23 -> 29:23, #15 Counter in file 0 30:15 -> 30:28, ((#12 + 0) - #15) Counter in file 0 31:12 -> 31:25, (((#12 + 0) - #15) - #8) Counter in file 0 31:29 -> 31:42, ((((#12 + 0) - #15) - #8) - #24) -Counter in file 0 31:42 -> 31:43, (#24 + #25) Counter in file 0 31:42 -> 31:43, (((((#12 + 0) - #15) - #8) - #24) - #25) -Counter in file 0 31:42 -> 31:42, #25 -Counter in file 0 31:42 -> 31:42, #24 +Counter in file 0 31:42 -> 31:43, (#24 + #25) Counter in file 0 31:46 -> 31:60, #32 Counter in file 0 31:60 -> 31:61, (#28 + #29) Counter in file 0 31:60 -> 31:61, (#32 - #29) -Counter in file 0 31:60 -> 31:60, #28 -Counter in file 0 31:60 -> 31:60, #29 Counter in file 0 31:61 -> 33:10, #33 Counter in file 0 33:10 -> 33:11, #34 Counter in file 0 34:9 -> 34:23, #19 @@ -50,19 +40,14 @@ Counter in file 0 40:13 -> 42:6, #20 Counter in file 0 44:9 -> 44:10, (#27 + #30) Counter in file 0 44:16 -> 44:29, (#20 + 0) Counter in file 0 45:9 -> 45:23, #27 -Counter in file 0 45:23 -> 45:23, #26 Counter in file 0 46:15 -> 46:28, ((#20 + 0) - #26) Counter in file 0 47:12 -> 47:25, (((#20 + 0) - #26) - #7) Counter in file 0 47:29 -> 47:42, ((((#20 + 0) - #26) - #7) - #35) -Counter in file 0 47:42 -> 47:43, (((((#20 + 0) - #26) - #7) - #35) - #36) Counter in file 0 47:42 -> 47:43, (#35 + #36) -Counter in file 0 47:42 -> 47:42, #35 -Counter in file 0 47:42 -> 47:42, #36 +Counter in file 0 47:42 -> 47:43, (((((#20 + 0) - #26) - #7) - #35) - #36) Counter in file 0 47:46 -> 47:60, #41 -Counter in file 0 47:60 -> 47:61, (#41 - #38) Counter in file 0 47:60 -> 47:61, (#37 + #38) -Counter in file 0 47:60 -> 47:60, #38 -Counter in file 0 47:60 -> 47:60, #37 +Counter in file 0 47:60 -> 47:61, (#41 - #38) Counter in file 0 47:61 -> 49:10, #42 Counter in file 0 49:10 -> 49:11, #43 Counter in file 0 50:9 -> 50:23, #30 @@ -70,25 +55,19 @@ Counter in file 0 52:13 -> 54:15, #7 Counter in file 0 57:9 -> 57:10, (#9 + #10) Counter in file 0 57:16 -> 57:29, ((#27 + #30) + 0) Counter in file 0 58:9 -> 58:23, #9 -Counter in file 0 58:23 -> 58:23, #31 Counter in file 0 59:15 -> 59:28, ((#27 + #30) - #31) Counter in file 0 60:12 -> 60:25, (((#27 + #30) - #31) - #6) Counter in file 0 60:29 -> 60:42, ((((#27 + #30) - #31) - #6) - #39) Counter in file 0 60:42 -> 60:43, (#39 + #40) Counter in file 0 60:42 -> 60:43, (((((#27 + #30) - #31) - #6) - #39) - #40) -Counter in file 0 60:42 -> 60:42, #40 -Counter in file 0 60:42 -> 60:42, #39 Counter in file 0 60:46 -> 60:60, #46 -Counter in file 0 60:60 -> 60:61, (#44 + #45) Counter in file 0 60:60 -> 60:61, (#46 - #45) -Counter in file 0 60:60 -> 60:60, #44 -Counter in file 0 60:60 -> 60:60, #45 +Counter in file 0 60:60 -> 60:61, (#44 + #45) Counter in file 0 60:61 -> 62:10, #47 Counter in file 0 62:10 -> 62:11, #48 Counter in file 0 63:9 -> 63:23, #10 Counter in file 0 65:9 -> 65:15, #6 Counter in file 0 67:1 -> 67:2, ((#9 + #10) + (((#6 + #7) + #8) + (((#2 + 0) - #3) - #5))) -Counter in file 0 67:2 -> 67:2, (((#6 + #7) + #8) + (((#2 + 0) - #3) - #5)) Emitting segments for file: ../coverage/various_conditions.rs Combined regions: 4:9 -> 4:26 (count=1) @@ -97,15 +76,12 @@ Combined regions: 10:9 -> 10:10 (count=1) 10:16 -> 10:29 (count=1) 11:9 -> 12:10 (count=1) - 11:23 -> 11:23 (count=1) 13:15 -> 13:28 (count=0) 14:12 -> 14:25 (count=0) 14:29 -> 14:42 (count=0) 14:42 -> 14:43 (count=0) - 14:42 -> 14:42 (count=0) 14:46 -> 14:60 (count=0) 14:60 -> 14:61 (count=0) - 14:60 -> 14:60 (count=0) 14:61 -> 16:10 (count=0) 16:10 -> 16:11 (count=0) 17:9 -> 18:18 (count=0) @@ -115,15 +91,12 @@ Combined regions: 24:13 -> 26:6 (count=1) 28:8 -> 28:21 (count=1) 29:9 -> 29:23 (count=1) - 29:23 -> 29:23 (count=1) 30:15 -> 30:28 (count=0) 31:12 -> 31:25 (count=0) 31:29 -> 31:42 (count=0) 31:42 -> 31:43 (count=0) - 31:42 -> 31:42 (count=0) 31:46 -> 31:60 (count=0) 31:60 -> 31:61 (count=0) - 31:60 -> 31:60 (count=0) 31:61 -> 33:10 (count=0) 33:10 -> 33:11 (count=0) 34:9 -> 34:23 (count=0) @@ -134,15 +107,12 @@ Combined regions: 44:9 -> 44:10 (count=0) 44:16 -> 44:29 (count=1) 45:9 -> 45:23 (count=0) - 45:23 -> 45:23 (count=0) 46:15 -> 46:28 (count=1) 47:12 -> 47:25 (count=0) 47:29 -> 47:42 (count=0) 47:42 -> 47:43 (count=0) - 47:42 -> 47:42 (count=0) 47:46 -> 47:60 (count=0) 47:60 -> 47:61 (count=0) - 47:60 -> 47:60 (count=0) 47:61 -> 49:10 (count=0) 49:10 -> 49:11 (count=0) 50:9 -> 50:23 (count=0) @@ -150,21 +120,17 @@ Combined regions: 57:9 -> 57:10 (count=0) 57:16 -> 57:29 (count=0) 58:9 -> 58:23 (count=0) - 58:23 -> 58:23 (count=0) 59:15 -> 59:28 (count=0) 60:12 -> 60:25 (count=0) 60:29 -> 60:42 (count=0) 60:42 -> 60:43 (count=0) - 60:42 -> 60:42 (count=0) 60:46 -> 60:60 (count=0) 60:60 -> 60:61 (count=0) - 60:60 -> 60:60 (count=0) 60:61 -> 62:10 (count=0) 62:10 -> 62:11 (count=0) 63:9 -> 63:23 (count=0) 65:9 -> 65:15 (count=0) 67:1 -> 67:2 (count=1) - 67:2 -> 67:2 (count=1) Segment at 4:9 (count = 1), RegionEntry Segment at 4:26 (count = 0), Skipped Segment at 5:8 (count = 1), RegionEntry @@ -176,17 +142,16 @@ Segment at 10:10 (count = 0), Skipped Segment at 10:16 (count = 1), RegionEntry Segment at 10:29 (count = 0), Skipped Segment at 11:9 (count = 1), RegionEntry -Segment at 11:23 (count = 1) Segment at 12:10 (count = 0), Skipped Segment at 13:15 (count = 0), RegionEntry Segment at 13:28 (count = 0), Skipped Segment at 14:12 (count = 0), RegionEntry Segment at 14:25 (count = 0), Skipped Segment at 14:29 (count = 0), RegionEntry -Segment at 14:42 (count = 0) +Segment at 14:42 (count = 0), RegionEntry Segment at 14:43 (count = 0), Skipped Segment at 14:46 (count = 0), RegionEntry -Segment at 14:60 (count = 0) +Segment at 14:60 (count = 0), RegionEntry Segment at 14:61 (count = 0), RegionEntry Segment at 16:10 (count = 0), RegionEntry Segment at 16:11 (count = 0), Skipped @@ -203,16 +168,16 @@ Segment at 26:6 (count = 0), Skipped Segment at 28:8 (count = 1), RegionEntry Segment at 28:21 (count = 0), Skipped Segment at 29:9 (count = 1), RegionEntry -Segment at 29:23 (count = 1), Gap +Segment at 29:23 (count = 0), Skipped Segment at 30:15 (count = 0), RegionEntry Segment at 30:28 (count = 0), Skipped Segment at 31:12 (count = 0), RegionEntry Segment at 31:25 (count = 0), Skipped Segment at 31:29 (count = 0), RegionEntry -Segment at 31:42 (count = 0) +Segment at 31:42 (count = 0), RegionEntry Segment at 31:43 (count = 0), Skipped Segment at 31:46 (count = 0), RegionEntry -Segment at 31:60 (count = 0) +Segment at 31:60 (count = 0), RegionEntry Segment at 31:61 (count = 0), RegionEntry Segment at 33:10 (count = 0), RegionEntry Segment at 33:11 (count = 0), Skipped @@ -231,16 +196,16 @@ Segment at 44:10 (count = 0), Skipped Segment at 44:16 (count = 1), RegionEntry Segment at 44:29 (count = 0), Skipped Segment at 45:9 (count = 0), RegionEntry -Segment at 45:23 (count = 0), Gap +Segment at 45:23 (count = 0), Skipped Segment at 46:15 (count = 1), RegionEntry Segment at 46:28 (count = 0), Skipped Segment at 47:12 (count = 0), RegionEntry Segment at 47:25 (count = 0), Skipped Segment at 47:29 (count = 0), RegionEntry -Segment at 47:42 (count = 0) +Segment at 47:42 (count = 0), RegionEntry Segment at 47:43 (count = 0), Skipped Segment at 47:46 (count = 0), RegionEntry -Segment at 47:60 (count = 0) +Segment at 47:60 (count = 0), RegionEntry Segment at 47:61 (count = 0), RegionEntry Segment at 49:10 (count = 0), RegionEntry Segment at 49:11 (count = 0), Skipped @@ -253,16 +218,16 @@ Segment at 57:10 (count = 0), Skipped Segment at 57:16 (count = 0), RegionEntry Segment at 57:29 (count = 0), Skipped Segment at 58:9 (count = 0), RegionEntry -Segment at 58:23 (count = 0), Gap +Segment at 58:23 (count = 0), Skipped Segment at 59:15 (count = 0), RegionEntry Segment at 59:28 (count = 0), Skipped Segment at 60:12 (count = 0), RegionEntry Segment at 60:25 (count = 0), Skipped Segment at 60:29 (count = 0), RegionEntry -Segment at 60:42 (count = 0) +Segment at 60:42 (count = 0), RegionEntry Segment at 60:43 (count = 0), Skipped Segment at 60:46 (count = 0), RegionEntry -Segment at 60:60 (count = 0) +Segment at 60:60 (count = 0), RegionEntry Segment at 60:61 (count = 0), RegionEntry Segment at 62:10 (count = 0), RegionEntry Segment at 62:11 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while_early_return.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while_early_return.txt index 54938f8dc8284..60f389fc9d467 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while_early_return.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while_early_return.txt @@ -8,7 +8,6 @@ Counter in file 0 26:21 -> 26:27, #5 Counter in file 0 30:9 -> 32:10, #2 Counter in file 0 35:5 -> 35:11, #3 Counter in file 0 36:1 -> 36:2, ((#4 + #5) + #3) -Counter in file 0 36:2 -> 36:2, (#4 + #5) Emitting segments for file: ../coverage/while_early_return.rs Combined regions: 5:9 -> 5:27 (count=1) @@ -20,7 +19,6 @@ Combined regions: 30:9 -> 32:10 (count=6) 35:5 -> 35:11 (count=0) 36:1 -> 36:2 (count=1) - 36:2 -> 36:2 (count=1) Segment at 5:9 (count = 1), RegionEntry Segment at 5:27 (count = 0), Skipped Segment at 7:9 (count = 7), RegionEntry From b05239103812ae69ad7dd45352f850657020836f Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Sun, 18 Oct 2020 10:26:32 -0700 Subject: [PATCH 12/18] instrument_coverage.rs -> instrument_coverage/mod.rs Just moved the file, before refactoring into multiple files in the new compiler/rustc_mir/src/transform/instrument_coverage/ directory. --- .../{instrument_coverage.rs => instrument_coverage/mod.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename compiler/rustc_mir/src/transform/{instrument_coverage.rs => instrument_coverage/mod.rs} (100%) diff --git a/compiler/rustc_mir/src/transform/instrument_coverage.rs b/compiler/rustc_mir/src/transform/instrument_coverage/mod.rs similarity index 100% rename from compiler/rustc_mir/src/transform/instrument_coverage.rs rename to compiler/rustc_mir/src/transform/instrument_coverage/mod.rs From c564a3f1638cc7657403813aa20ea3986317124a Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Mon, 19 Oct 2020 02:24:54 -0700 Subject: [PATCH 13/18] Completed most of the refactoring, except make_bcb_counters() Still working on make_bcb_counters() refacoring to split it into separate functions. --- .../rustc_mir/src/transform/coverage/debug.rs | 491 +++ .../rustc_mir/src/transform/coverage/graph.rs | 562 ++++ .../rustc_mir/src/transform/coverage/mod.rs | 1209 +++++++ .../rustc_mir/src/transform/coverage/query.rs | 113 + .../rustc_mir/src/transform/coverage/spans.rs | 773 +++++ .../src/transform/instrument_coverage/mod.rs | 2931 ----------------- compiler/rustc_mir/src/transform/mod.rs | 6 +- 7 files changed, 3151 insertions(+), 2934 deletions(-) create mode 100644 compiler/rustc_mir/src/transform/coverage/debug.rs create mode 100644 compiler/rustc_mir/src/transform/coverage/graph.rs create mode 100644 compiler/rustc_mir/src/transform/coverage/mod.rs create mode 100644 compiler/rustc_mir/src/transform/coverage/query.rs create mode 100644 compiler/rustc_mir/src/transform/coverage/spans.rs delete mode 100644 compiler/rustc_mir/src/transform/instrument_coverage/mod.rs diff --git a/compiler/rustc_mir/src/transform/coverage/debug.rs b/compiler/rustc_mir/src/transform/coverage/debug.rs new file mode 100644 index 0000000000000..5083ca8cedac2 --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/debug.rs @@ -0,0 +1,491 @@ +use super::graph::BasicCoverageBlock; +use super::spans::CoverageSpan; + +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::mir::coverage::*; +use rustc_middle::mir::{BasicBlock, TerminatorKind}; + +use std::lazy::SyncOnceCell; + +const RUSTC_COVERAGE_DEBUG_OPTIONS: &str = "RUSTC_COVERAGE_DEBUG_OPTIONS"; + +pub(crate) fn debug_options<'a>() -> &'a DebugOptions { + static DEBUG_OPTIONS: SyncOnceCell = SyncOnceCell::new(); + + &DEBUG_OPTIONS.get_or_init(|| DebugOptions::new()) +} + +#[derive(Debug, Clone)] +pub(crate) struct DebugOptions { + pub allow_unused_expressions: bool, + pub simplify_expressions: bool, + counter_format: ExpressionFormat, +} + +impl DebugOptions { + fn new() -> Self { + let mut allow_unused_expressions = true; + let mut simplify_expressions = false; + let mut counter_format = ExpressionFormat::default(); + + if let Ok(env_debug_options) = std::env::var(RUSTC_COVERAGE_DEBUG_OPTIONS) { + for setting_str in env_debug_options.replace(" ", "").replace("-", "_").split(",") { + let mut setting = setting_str.splitn(2, "="); + match setting.next() { + Some(option) if option == "allow_unused_expressions" => { + allow_unused_expressions = bool_option_val(option, setting.next()); + debug!( + "{} env option `allow_unused_expressions` is set to {}", + RUSTC_COVERAGE_DEBUG_OPTIONS, allow_unused_expressions + ); + } + Some(option) if option == "simplify_expressions" => { + simplify_expressions = bool_option_val(option, setting.next()); + debug!( + "{} env option `simplify_expressions` is set to {}", + RUSTC_COVERAGE_DEBUG_OPTIONS, simplify_expressions + ); + } + Some(option) if option == "counter_format" => { + if let Some(strval) = setting.next() { + counter_format = counter_format_option_val(strval); + debug!( + "{} env option `counter_format` is set to {:?}", + RUSTC_COVERAGE_DEBUG_OPTIONS, counter_format + ); + } else { + bug!( + "`{}` option in environment variable {} requires one or more \ + plus-separated choices (a non-empty subset of \ + `id+block+operation`)", + option, + RUSTC_COVERAGE_DEBUG_OPTIONS + ); + } + } + Some("") => {} + Some(invalid) => bug!( + "Unsupported setting `{}` in environment variable {}", + invalid, + RUSTC_COVERAGE_DEBUG_OPTIONS + ), + None => {} + } + } + } + + Self { allow_unused_expressions, simplify_expressions, counter_format } + } +} + +fn bool_option_val(option: &str, some_strval: Option<&str>) -> bool { + if let Some(val) = some_strval { + if vec!["yes", "y", "on", "true"].contains(&val) { + true + } else if vec!["no", "n", "off", "false"].contains(&val) { + false + } else { + bug!( + "Unsupported value `{}` for option `{}` in environment variable {}", + option, + val, + RUSTC_COVERAGE_DEBUG_OPTIONS + ) + } + } else { + true + } +} + +fn counter_format_option_val(strval: &str) -> ExpressionFormat { + let mut counter_format = ExpressionFormat { id: false, block: false, operation: false }; + let components = strval.splitn(3, "+"); + for component in components { + match component { + "id" => counter_format.id = true, + "block" => counter_format.block = true, + "operation" => counter_format.operation = true, + _ => bug!( + "Unsupported counter_format choice `{}` in environment variable {}", + component, + RUSTC_COVERAGE_DEBUG_OPTIONS + ), + } + } + counter_format +} + +#[derive(Debug, Clone)] +struct ExpressionFormat { + id: bool, + block: bool, + operation: bool, +} + +impl Default for ExpressionFormat { + fn default() -> Self { + Self { id: false, block: true, operation: true } + } +} + +#[derive(Debug)] +struct DebugCounter { + counter_kind: CoverageKind, + some_block_label: Option, +} + +impl DebugCounter { + fn new(counter_kind: CoverageKind, some_block_label: Option) -> Self { + Self { counter_kind, some_block_label } + } +} + +pub(crate) struct DebugCounters { + some_counters: Option>, +} + +impl DebugCounters { + pub fn new() -> Self { + Self { some_counters: None } + } + + pub fn enable(&mut self) { + self.some_counters.replace(FxHashMap::default()); + } + + pub fn is_enabled(&mut self) -> bool { + self.some_counters.is_some() + } + + pub fn add_counter(&mut self, counter_kind: &CoverageKind, some_block_label: Option) { + if let Some(counters) = &mut self.some_counters { + let id: ExpressionOperandId = match *counter_kind { + CoverageKind::Counter { id, .. } => id.into(), + CoverageKind::Expression { id, .. } => id.into(), + _ => bug!( + "the given `CoverageKind` is not an counter or expression: {:?}", + counter_kind + ), + }; + counters + .insert(id.into(), DebugCounter::new(counter_kind.clone(), some_block_label)) + .expect_none( + "attempt to add the same counter_kind to DebugCounters more than once", + ); + } + } + + pub fn some_block_label(&self, operand: ExpressionOperandId) -> Option<&String> { + self.some_counters.as_ref().map_or(None, |counters| { + counters + .get(&operand) + .map_or(None, |debug_counter| debug_counter.some_block_label.as_ref()) + }) + } + + pub fn format_counter(&self, counter_kind: &CoverageKind) -> String { + match *counter_kind { + CoverageKind::Counter { .. } => { + format!("Counter({})", self.format_counter_kind(counter_kind)) + } + CoverageKind::Expression { .. } => { + format!("Expression({})", self.format_counter_kind(counter_kind)) + } + CoverageKind::Unreachable { .. } => "Unreachable".to_owned(), + } + } + + fn format_counter_kind(&self, counter_kind: &CoverageKind) -> String { + let counter_format = &debug_options().counter_format; + if let CoverageKind::Expression { id, lhs, op, rhs } = *counter_kind { + if counter_format.operation { + return format!( + "{}{} {} {}", + if counter_format.id || self.some_counters.is_none() { + format!("#{} = ", id.index()) + } else { + String::new() + }, + self.format_operand(lhs), + if op == Op::Add { "+" } else { "-" }, + self.format_operand(rhs), + ); + } + } + + let id: ExpressionOperandId = match *counter_kind { + CoverageKind::Counter { id, .. } => id.into(), + CoverageKind::Expression { id, .. } => id.into(), + _ => { + bug!("the given `CoverageKind` is not an counter or expression: {:?}", counter_kind) + } + }; + if self.some_counters.is_some() && (counter_format.block || !counter_format.id) { + let counters = self.some_counters.as_ref().unwrap(); + if let Some(DebugCounter { some_block_label: Some(block_label), .. }) = + counters.get(&id.into()) + { + return if counter_format.id { + format!("{}#{}", block_label, id.index()) + } else { + format!("{}", block_label) + }; + } + } + format!("#{}", id.index()) + } + + fn format_operand(&self, operand: ExpressionOperandId) -> String { + if operand.index() == 0 { + return String::from("0"); + } + if let Some(counters) = &self.some_counters { + if let Some(DebugCounter { counter_kind, some_block_label }) = counters.get(&operand) { + if let CoverageKind::Expression { .. } = counter_kind { + if let Some(block_label) = some_block_label { + if debug_options().counter_format.block { + return format!( + "{}:({})", + block_label, + self.format_counter_kind(counter_kind) + ); + } + } + return format!("({})", self.format_counter_kind(counter_kind)); + } + return format!("{}", self.format_counter_kind(counter_kind)); + } + } + format!("#{}", operand.index().to_string()) + } +} + +pub(crate) struct GraphvizData { + some_bcb_to_coverage_spans_with_counters: + Option>>, + some_bcb_to_dependency_counters: Option>>, + some_edge_to_counter: Option>, +} + +impl GraphvizData { + pub fn new() -> Self { + Self { + some_bcb_to_coverage_spans_with_counters: None, + some_bcb_to_dependency_counters: None, + some_edge_to_counter: None, + } + } + + pub fn enable(&mut self) { + self.some_bcb_to_coverage_spans_with_counters = Some(FxHashMap::default()); + self.some_bcb_to_dependency_counters = Some(FxHashMap::default()); + self.some_edge_to_counter = Some(FxHashMap::default()); + } + + pub fn is_enabled(&mut self) -> bool { + self.some_bcb_to_coverage_spans_with_counters.is_some() + } + + pub fn add_bcb_coverage_span_with_counter( + &mut self, + bcb: BasicCoverageBlock, + coverage_span: &CoverageSpan, + counter_kind: &CoverageKind, + ) { + if let Some(bcb_to_coverage_spans_with_counters) = + self.some_bcb_to_coverage_spans_with_counters.as_mut() + { + bcb_to_coverage_spans_with_counters + .entry(bcb) + .or_insert_with(|| Vec::new()) + .push((coverage_span.clone(), counter_kind.clone())); + } + } + + pub fn get_bcb_coverage_spans_with_counters( + &self, + bcb: BasicCoverageBlock, + ) -> Option<&Vec<(CoverageSpan, CoverageKind)>> { + if let Some(bcb_to_coverage_spans_with_counters) = + self.some_bcb_to_coverage_spans_with_counters.as_ref() + { + bcb_to_coverage_spans_with_counters.get(&bcb) + } else { + None + } + } + + pub fn add_bcb_dependency_counter( + &mut self, + bcb: BasicCoverageBlock, + counter_kind: &CoverageKind, + ) { + if let Some(bcb_to_dependency_counters) = self.some_bcb_to_dependency_counters.as_mut() { + bcb_to_dependency_counters + .entry(bcb) + .or_insert_with(|| Vec::new()) + .push(counter_kind.clone()); + } + } + + pub fn get_bcb_dependency_counters( + &self, + bcb: BasicCoverageBlock, + ) -> Option<&Vec> { + if let Some(bcb_to_dependency_counters) = self.some_bcb_to_dependency_counters.as_ref() { + bcb_to_dependency_counters.get(&bcb) + } else { + None + } + } + + pub fn set_edge_counter( + &mut self, + from_bcb: BasicCoverageBlock, + to_bb: BasicBlock, + counter_kind: &CoverageKind, + ) { + if let Some(edge_to_counter) = self.some_edge_to_counter.as_mut() { + edge_to_counter.insert((from_bcb, to_bb), counter_kind.clone()).expect_none( + "invalid attempt to insert more than one edge counter for the same edge", + ); + } + } + + pub fn get_edge_counter( + &self, + from_bcb: BasicCoverageBlock, + to_bb: BasicBlock, + ) -> Option<&CoverageKind> { + if let Some(edge_to_counter) = self.some_edge_to_counter.as_ref() { + edge_to_counter.get(&(from_bcb, to_bb)) + } else { + None + } + } +} + +pub(crate) struct UsedExpressions { + some_used_expression_operands: + Option>>, + some_unused_expressions: + Option, BasicCoverageBlock)>>, +} + +impl UsedExpressions { + pub fn new() -> Self { + Self { some_used_expression_operands: None, some_unused_expressions: None } + } + + pub fn enable(&mut self) { + self.some_used_expression_operands = Some(FxHashMap::default()); + self.some_unused_expressions = Some(Vec::new()); + } + + pub fn is_enabled(&mut self) -> bool { + self.some_used_expression_operands.is_some() + } + + pub fn add_expression_operands(&mut self, expression: &CoverageKind) { + if let Some(used_expression_operands) = self.some_used_expression_operands.as_mut() { + if let CoverageKind::Expression { id, lhs, rhs, .. } = *expression { + used_expression_operands.entry(lhs).or_insert_with(|| Vec::new()).push(id); + used_expression_operands.entry(rhs).or_insert_with(|| Vec::new()).push(id); + } + } + } + + pub fn expression_is_used(&mut self, expression: &CoverageKind) -> bool { + if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() { + used_expression_operands.contains_key(&expression.as_operand_id()) + } else { + false + } + } + + pub fn validate_expression_is_used( + &mut self, + expression: &CoverageKind, + edge_from_bcb: Option, + target_bcb: BasicCoverageBlock, + ) { + if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() { + if !used_expression_operands.contains_key(&expression.as_operand_id()) { + self.some_unused_expressions.as_mut().unwrap().push(( + expression.clone(), + edge_from_bcb, + target_bcb, + )); + } + } + } + + /// Return the list of unused counters (if any) as a tuple with the counter (`CoverageKind`), + /// optional `from_bcb` (if it was an edge counter), and `target_bcb`. + pub fn get_unused_expressions( + &self, + ) -> Vec<(CoverageKind, Option, BasicCoverageBlock)> { + if let Some(unused_expressions) = self.some_unused_expressions.as_ref() { + unused_expressions.clone() + } else { + Vec::new() + } + } + + pub fn check_no_unused(&self, debug_counters: &DebugCounters) { + if let Some(unused_expressions) = self.some_unused_expressions.as_ref() { + for (counter_kind, edge_from_bcb, target_bcb) in unused_expressions { + let unused_counter_message = if let Some(from_bcb) = edge_from_bcb.as_ref() { + format!( + "non-coverage edge counter found without a dependent expression, in \ + {:?}->{:?}; counter={}", + from_bcb, + target_bcb, + debug_counters.format_counter(&counter_kind), + ) + } else { + format!( + "non-coverage counter found without a dependent expression, in {:?}; \ + counter={}", + target_bcb, + debug_counters.format_counter(&counter_kind), + ) + }; + + if debug_options().simplify_expressions || debug_options().allow_unused_expressions + { + // Note, the debugging option `simplify_expressions`, which initializes the + // `debug_expressions_cache` can cause some counters to become unused, and + // is not a bug. + // + // For example, converting `x + (y - x)` to just `y` removes a dependency + // on `y - x`. If that expression is not a dependency elsewhere, and if it is + // not associated with a `CoverageSpan`, it is now considered `unused`. + debug!("WARNING: {}", unused_counter_message); + } else { + bug!("{}", unused_counter_message); + } + } + } + } +} + +pub(crate) fn term_type(kind: &TerminatorKind<'tcx>) -> &'static str { + match kind { + TerminatorKind::Goto { .. } => "Goto", + TerminatorKind::SwitchInt { .. } => "SwitchInt", + TerminatorKind::Resume => "Resume", + TerminatorKind::Abort => "Abort", + TerminatorKind::Return => "Return", + TerminatorKind::Unreachable => "Unreachable", + TerminatorKind::Drop { .. } => "Drop", + TerminatorKind::DropAndReplace { .. } => "DropAndReplace", + TerminatorKind::Call { .. } => "Call", + TerminatorKind::Assert { .. } => "Assert", + TerminatorKind::Yield { .. } => "Yield", + TerminatorKind::GeneratorDrop => "GeneratorDrop", + TerminatorKind::FalseEdge { .. } => "FalseEdge", + TerminatorKind::FalseUnwind { .. } => "FalseUnwind", + TerminatorKind::InlineAsm { .. } => "InlineAsm", + } +} diff --git a/compiler/rustc_mir/src/transform/coverage/graph.rs b/compiler/rustc_mir/src/transform/coverage/graph.rs new file mode 100644 index 0000000000000..6885fc5c61cbe --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/graph.rs @@ -0,0 +1,562 @@ +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::graph::dominators::{self, Dominators}; +use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode}; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::IndexVec; +use rustc_middle::mir::coverage::*; +use rustc_middle::mir::{self, BasicBlock, BasicBlockData, Terminator, TerminatorKind}; + +use std::ops::{Index, IndexMut}; + +const ID_SEPARATOR: &str = ","; + +pub(crate) struct CoverageGraph { + bcbs: IndexVec, + bb_to_bcb: IndexVec>, + pub successors: IndexVec>, + pub predecessors: IndexVec>, +} + +impl CoverageGraph { + pub fn from_mir(mir_body: &mir::Body<'tcx>) -> Self { + let (bcbs, bb_to_bcb) = Self::compute_basic_coverage_blocks(mir_body); + + // Pre-transform MIR `BasicBlock` successors and predecessors into the BasicCoverageBlock + // equivalents. Note that since the BasicCoverageBlock graph has been fully simplified, the + // each predecessor of a BCB leader_bb should be in a unique BCB, and each successor of a + // BCB last_bb should bin in its own unique BCB. Therefore, collecting the BCBs using + // `bb_to_bcb` should work without requiring a deduplication step. + + let successors = IndexVec::from_fn_n( + |bcb| { + let bcb_data = &bcbs[bcb]; + let bcb_successors = + bcb_filtered_successors(&mir_body, &bcb_data.terminator(mir_body).kind) + .filter_map(|&successor_bb| bb_to_bcb[successor_bb]) + .collect::>(); + debug_assert!({ + let mut sorted = bcb_successors.clone(); + sorted.sort_unstable(); + let initial_len = sorted.len(); + sorted.dedup(); + sorted.len() == initial_len + }); + bcb_successors + }, + bcbs.len(), + ); + + let mut predecessors = IndexVec::from_elem_n(Vec::new(), bcbs.len()); + for (bcb, bcb_successors) in successors.iter_enumerated() { + for &successor in bcb_successors { + predecessors[successor].push(bcb); + } + } + + Self { bcbs, bb_to_bcb, successors, predecessors } + } + + fn compute_basic_coverage_blocks( + mir_body: &mir::Body<'tcx>, + ) -> ( + IndexVec, + IndexVec>, + ) { + let num_basic_blocks = mir_body.num_nodes(); + let mut bcbs = IndexVec::with_capacity(num_basic_blocks); + let mut bb_to_bcb = IndexVec::from_elem_n(None, num_basic_blocks); + + // Walk the MIR CFG using a Preorder traversal, which starts from `START_BLOCK` and follows + // each block terminator's `successors()`. Coverage spans must map to actual source code, + // so compiler generated blocks and paths can be ignored. To that end, the CFG traversal + // intentionally omits unwind paths. + let mir_cfg_without_unwind = ShortCircuitPreorder::new(&mir_body, bcb_filtered_successors); + + let mut basic_blocks = Vec::new(); + for (bb, data) in mir_cfg_without_unwind { + if let Some(last) = basic_blocks.last() { + let predecessors = &mir_body.predecessors()[bb]; + if predecessors.len() > 1 || !predecessors.contains(last) { + // The `bb` has more than one _incoming_ edge, and should start its own + // `BasicCoverageBlockData`. (Note, the `basic_blocks` vector does not yet + // include `bb`; it contains a sequence of one or more sequential basic_blocks + // with no intermediate branches in or out. Save these as a new + // `BasicCoverageBlockData` before starting the new one.) + Self::add_basic_coverage_block( + &mut bcbs, + &mut bb_to_bcb, + basic_blocks.split_off(0), + ); + debug!( + " because {}", + if predecessors.len() > 1 { + "predecessors.len() > 1".to_owned() + } else { + format!("bb {} is not in precessors: {:?}", bb.index(), predecessors) + } + ); + } + } + basic_blocks.push(bb); + + let term = data.terminator(); + + match term.kind { + TerminatorKind::Return { .. } + // FIXME(richkadel): Add test(s) for `Abort` coverage. + | TerminatorKind::Abort + // FIXME(richkadel): Add test(s) for `Assert` coverage. + // Should `Assert` be handled like `FalseUnwind` instead? Since we filter out unwind + // branches when creating the BCB CFG, aren't `Assert`s (without unwinds) just like + // `FalseUnwinds` (which are kind of like `Goto`s)? + | TerminatorKind::Assert { .. } + // FIXME(richkadel): Add test(s) for `Yield` coverage, and confirm coverage is + // sensible for code using the `yield` keyword. + | TerminatorKind::Yield { .. } + // FIXME(richkadel): Also add coverage tests using async/await, and threading. + + | TerminatorKind::SwitchInt { .. } => { + // The `bb` has more than one _outgoing_ edge, or exits the function. Save the + // current sequence of `basic_blocks` gathered to this point, as a new + // `BasicCoverageBlockData`. + Self::add_basic_coverage_block( + &mut bcbs, + &mut bb_to_bcb, + basic_blocks.split_off(0), + ); + debug!(" because term.kind = {:?}", term.kind); + // Note that this condition is based on `TerminatorKind`, even though it + // theoretically boils down to `successors().len() != 1`; that is, either zero + // (e.g., `Return`, `Abort`) or multiple successors (e.g., `SwitchInt`), but + // since the BCB CFG ignores things like unwind branches (which exist in the + // `Terminator`s `successors()` list) checking the number of successors won't + // work. + } + TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Unreachable + | TerminatorKind::Drop { .. } + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::Call { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::InlineAsm { .. } => {} + } + } + + if !basic_blocks.is_empty() { + // process any remaining basic_blocks into a final `BasicCoverageBlockData` + Self::add_basic_coverage_block(&mut bcbs, &mut bb_to_bcb, basic_blocks.split_off(0)); + debug!(" because the end of the MIR CFG was reached while traversing"); + } + + (bcbs, bb_to_bcb) + } + + fn add_basic_coverage_block( + bcbs: &mut IndexVec, + bb_to_bcb: &mut IndexVec>, + basic_blocks: Vec, + ) { + let bcb = BasicCoverageBlock::from_usize(bcbs.len()); + for &bb in basic_blocks.iter() { + bb_to_bcb[bb] = Some(bcb); + } + let bcb_data = BasicCoverageBlockData::from(basic_blocks); + debug!("adding bcb{}: {:?}", bcb.index(), bcb_data); + bcbs.push(bcb_data); + } + + #[inline(always)] + pub fn iter_enumerated( + &self, + ) -> impl Iterator { + self.bcbs.iter_enumerated() + } + + #[inline(always)] + pub fn iter_enumerated_mut( + &mut self, + ) -> impl Iterator { + self.bcbs.iter_enumerated_mut() + } + + #[inline(always)] + pub fn bcb_from_bb(&self, bb: BasicBlock) -> Option { + if bb.index() < self.bb_to_bcb.len() { self.bb_to_bcb[bb] } else { None } + } + + #[inline(always)] + pub fn compute_bcb_dominators(&self) -> Dominators { + dominators::dominators(self) + } +} + +impl Index for CoverageGraph { + type Output = BasicCoverageBlockData; + + #[inline] + fn index(&self, index: BasicCoverageBlock) -> &BasicCoverageBlockData { + &self.bcbs[index] + } +} + +impl IndexMut for CoverageGraph { + #[inline] + fn index_mut(&mut self, index: BasicCoverageBlock) -> &mut BasicCoverageBlockData { + &mut self.bcbs[index] + } +} + +impl graph::DirectedGraph for CoverageGraph { + type Node = BasicCoverageBlock; +} + +impl graph::WithNumNodes for CoverageGraph { + #[inline] + fn num_nodes(&self) -> usize { + self.bcbs.len() + } +} + +impl graph::WithStartNode for CoverageGraph { + #[inline] + fn start_node(&self) -> Self::Node { + self.bcb_from_bb(mir::START_BLOCK) + .expect("mir::START_BLOCK should be in a BasicCoverageBlock") + } +} + +type BcbSuccessors<'graph> = std::slice::Iter<'graph, BasicCoverageBlock>; + +impl<'graph> graph::GraphSuccessors<'graph> for CoverageGraph { + type Item = BasicCoverageBlock; + type Iter = std::iter::Cloned>; +} + +impl graph::WithSuccessors for CoverageGraph { + #[inline] + fn successors(&self, node: Self::Node) -> >::Iter { + self.successors[node].iter().cloned() + } +} + +impl graph::GraphPredecessors<'graph> for CoverageGraph { + type Item = BasicCoverageBlock; + type Iter = std::vec::IntoIter; +} + +impl graph::WithPredecessors for CoverageGraph { + #[inline] + fn predecessors(&self, node: Self::Node) -> >::Iter { + self.predecessors[node].clone().into_iter() + } +} + +rustc_index::newtype_index! { + /// A node in the [control-flow graph][CFG] of CoverageGraph. + pub(crate) struct BasicCoverageBlock { + DEBUG_FORMAT = "bcb{}", + } +} + +/// A BasicCoverageBlockData (BCB) represents the maximal-length sequence of MIR BasicBlocks without +/// conditional branches, and form a new, simplified, coverage-specific Control Flow Graph, without +/// altering the original MIR CFG. +/// +/// Note that running the MIR `SimplifyCfg` transform is not sufficient (and therefore not +/// necessary). The BCB-based CFG is a more aggressive simplification. For example: +/// +/// * The BCB CFG ignores (trims) branches not relevant to coverage, such as unwind-related code, +/// that is injected by the Rust compiler but has no physical source code to count. This also +/// means a BasicBlock with a `Call` terminator can be merged into its primary successor target +/// block, in the same BCB. +/// * Some BasicBlock terminators support Rust-specific concerns--like borrow-checking--that are +/// not relevant to coverage analysis. `FalseUnwind`, for example, can be treated the same as +/// a `Goto`, and merged with its successor into the same BCB. +/// +/// Each BCB with at least one computed `CoverageSpan` will have no more than one `Counter`. +/// In some cases, a BCB's execution count can be computed by `Expression`. Additional +/// disjoint `CoverageSpan`s in a BCB can also be counted by `Expression` (by adding `ZERO` +/// to the BCB's primary counter or expression). +/// +/// The BCB CFG is critical to simplifying the coverage analysis by ensuring graph path-based +/// queries (`is_dominated_by()`, `predecessors`, `successors`, etc.) have branch (control flow) +/// significance. +#[derive(Debug, Clone)] +pub(crate) struct BasicCoverageBlockData { + pub basic_blocks: Vec, + pub counter_kind: Option, + edge_from_bcbs: Option>, +} + +impl BasicCoverageBlockData { + pub fn from(basic_blocks: Vec) -> Self { + assert!(basic_blocks.len() > 0); + Self { basic_blocks, counter_kind: None, edge_from_bcbs: None } + } + + #[inline(always)] + pub fn leader_bb(&self) -> BasicBlock { + self.basic_blocks[0] + } + + #[inline(always)] + pub fn last_bb(&self) -> BasicBlock { + *self.basic_blocks.last().unwrap() + } + + #[inline(always)] + pub fn terminator<'a, 'tcx>(&self, mir_body: &'a mir::Body<'tcx>) -> &'a Terminator<'tcx> { + &mir_body[self.last_bb()].terminator() + } + + #[inline(always)] + pub fn set_counter(&mut self, counter_kind: CoverageKind) -> ExpressionOperandId { + debug_assert!( + self.edge_from_bcbs.is_none() || counter_kind.is_expression(), + "attempt to add a `Counter` to a BCB target with existing incoming edge counters" + ); + let operand = counter_kind.as_operand_id(); + self.counter_kind + .replace(counter_kind) + .expect_none("attempt to set a BasicCoverageBlock coverage counter more than once"); + operand + } + + #[inline(always)] + pub fn counter(&self) -> Option<&CoverageKind> { + self.counter_kind.as_ref() + } + + #[inline(always)] + pub fn take_counter(&mut self) -> Option { + self.counter_kind.take() + } + + #[inline(always)] + pub fn set_edge_counter_from( + &mut self, + from_bcb: BasicCoverageBlock, + counter_kind: CoverageKind, + ) -> ExpressionOperandId { + debug_assert!( + self.counter_kind.as_ref().map_or(true, |c| c.is_expression()), + "attempt to add an incoming edge counter from {:?} when the target BCB already has a \ + `Counter`", + from_bcb + ); + let operand = counter_kind.as_operand_id(); + self.edge_from_bcbs + .get_or_insert_with(|| FxHashMap::default()) + .insert(from_bcb, counter_kind) + .expect_none("attempt to set an edge counter more than once"); + operand + } + + #[inline(always)] + pub fn edge_counter_from(&self, from_bcb: BasicCoverageBlock) -> Option<&CoverageKind> { + if let Some(edge_from_bcbs) = &self.edge_from_bcbs { + edge_from_bcbs.get(&from_bcb) + } else { + None + } + } + + #[inline(always)] + pub fn take_edge_counters( + &mut self, + ) -> Option> { + self.edge_from_bcbs.take().map_or(None, |m| Some(m.into_iter())) + } + + pub fn id(&self) -> String { + format!( + "@{}", + self.basic_blocks + .iter() + .map(|bb| bb.index().to_string()) + .collect::>() + .join(ID_SEPARATOR) + ) + } +} + +fn bcb_filtered_successors<'a, 'tcx>( + body: &'tcx &'a mir::Body<'tcx>, + term_kind: &'tcx TerminatorKind<'tcx>, +) -> Box + 'a> { + let mut successors = term_kind.successors(); + box match &term_kind { + // SwitchInt successors are never unwind, and all of them should be traversed. + TerminatorKind::SwitchInt { .. } => successors, + // For all other kinds, return only the first successor, if any, and ignore unwinds. + // NOTE: `chain(&[])` is required to coerce the `option::iter` (from + // `next().into_iter()`) into the `mir::Successors` aliased type. + _ => successors.next().into_iter().chain(&[]), + } + .filter(move |&&successor| body[successor].terminator().kind != TerminatorKind::Unreachable) +} + +/// Maintains separate worklists for each loop in the BasicCoverageBlock CFG, plus one for the +/// CoverageGraph outside all loops. This supports traversing the BCB CFG in a way that +/// ensures a loop is completely traversed before processing Blocks after the end of the loop. +#[derive(Debug)] +pub(crate) struct TraversalContext { + /// From one or more backedges returning to a loop header. + pub loop_backedges: Option<(Vec, BasicCoverageBlock)>, + + /// worklist, to be traversed, of CoverageGraph in the loop with the given loop + /// backedges, such that the loop is the inner inner-most loop containing these + /// CoverageGraph + pub worklist: Vec, +} + +pub(crate) struct TraverseCoverageGraphWithLoops { + pub backedges: IndexVec>, + pub context_stack: Vec, + visited: BitSet, +} + +impl TraverseCoverageGraphWithLoops { + pub fn new( + basic_coverage_blocks: &CoverageGraph, + dominators: &Dominators, + ) -> Self { + let start_bcb = basic_coverage_blocks.start_node(); + let backedges = find_loop_backedges(basic_coverage_blocks, dominators); + let mut context_stack = Vec::new(); + context_stack.push(TraversalContext { loop_backedges: None, worklist: vec![start_bcb] }); + // `context_stack` starts with a `TraversalContext` for the main function context (beginning + // with the `start` BasicCoverageBlock of the function). New worklists are pushed to the top + // of the stack as loops are entered, and popped off of the stack when a loop's worklist is + // exhausted. + let visited = BitSet::new_empty(basic_coverage_blocks.num_nodes()); + Self { backedges, context_stack, visited } + } + + pub fn next(&mut self) -> Option { + // Strip contexts with empty worklists from the top of the stack + while self.context_stack.last().map_or(false, |context| context.worklist.is_empty()) { + self.context_stack.pop(); + } + // Pop the next bcb off of the current context_stack. If none, all BCBs were visited. + while let Some(next_bcb) = + self.context_stack.last_mut().map_or(None, |context| context.worklist.pop()) + { + if !self.visited.insert(next_bcb) { + debug!("Already visited: {:?}", next_bcb); + continue; + } + debug!("Visiting {:?}", next_bcb); + if self.backedges[next_bcb].len() > 0 { + debug!("{:?} is a loop header! Start a new TraversalContext...", next_bcb); + self.context_stack.push(TraversalContext { + loop_backedges: Some((self.backedges[next_bcb].clone(), next_bcb)), + worklist: Vec::new(), + }); + } + return Some(next_bcb); + } + debug_assert_eq!(self.visited.count(), self.visited.domain_size()); + None + } +} + +fn find_loop_backedges( + basic_coverage_blocks: &CoverageGraph, + dominators: &Dominators, +) -> IndexVec> { + let num_bcbs = basic_coverage_blocks.num_nodes(); + let mut backedges = IndexVec::from_elem_n(Vec::::new(), num_bcbs); + + // Identify loops by their backedges + for (bcb, _) in basic_coverage_blocks.iter_enumerated() { + for &successor in &basic_coverage_blocks.successors[bcb] { + if dominators.is_dominated_by(bcb, successor) { + let loop_header = successor; + let backedge_from_bcb = bcb; + debug!( + "Found BCB backedge: {:?} -> loop_header: {:?}", + backedge_from_bcb, loop_header + ); + backedges[loop_header].push(backedge_from_bcb); + } + } + } + backedges +} + +pub struct ShortCircuitPreorder< + 'a, + 'tcx, + F: Fn( + &'tcx &'a mir::Body<'tcx>, + &'tcx TerminatorKind<'tcx>, + ) -> Box + 'a>, +> { + body: &'tcx &'a mir::Body<'tcx>, + visited: BitSet, + worklist: Vec, + filtered_successors: F, +} + +impl< + 'a, + 'tcx, + F: Fn( + &'tcx &'a mir::Body<'tcx>, + &'tcx TerminatorKind<'tcx>, + ) -> Box + 'a>, +> ShortCircuitPreorder<'a, 'tcx, F> +{ + pub fn new( + body: &'tcx &'a mir::Body<'tcx>, + filtered_successors: F, + ) -> ShortCircuitPreorder<'a, 'tcx, F> { + let worklist = vec![mir::START_BLOCK]; + + ShortCircuitPreorder { + body, + visited: BitSet::new_empty(body.basic_blocks().len()), + worklist, + filtered_successors, + } + } +} + +impl< + 'a: 'tcx, + 'tcx, + F: Fn( + &'tcx &'a mir::Body<'tcx>, + &'tcx TerminatorKind<'tcx>, + ) -> Box + 'a>, +> Iterator for ShortCircuitPreorder<'a, 'tcx, F> +{ + type Item = (BasicBlock, &'a BasicBlockData<'tcx>); + + fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> { + while let Some(idx) = self.worklist.pop() { + if !self.visited.insert(idx) { + continue; + } + + let data = &self.body[idx]; + + if let Some(ref term) = data.terminator { + self.worklist.extend((self.filtered_successors)(&self.body, &term.kind)); + } + + return Some((idx, data)); + } + + None + } + + fn size_hint(&self) -> (usize, Option) { + let size = self.body.basic_blocks().len() - self.visited.count(); + (size, Some(size)) + } +} diff --git a/compiler/rustc_mir/src/transform/coverage/mod.rs b/compiler/rustc_mir/src/transform/coverage/mod.rs new file mode 100644 index 0000000000000..dd963744ec865 --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/mod.rs @@ -0,0 +1,1209 @@ +pub mod query; + +mod debug; +mod graph; +mod spans; + +use debug::{debug_options, term_type}; +use graph::{ + BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, TraverseCoverageGraphWithLoops, +}; +use spans::{CoverageSpan, CoverageSpans}; + +use crate::transform::MirPass; +use crate::util::generic_graphviz::GraphvizWriter; +use crate::util::pretty; +use crate::util::spanview::{self, SpanViewable}; + +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::graph::dominators::Dominators; +use rustc_data_structures::graph::WithNumNodes; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::Lrc; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::hir; +use rustc_middle::hir::map::blocks::FnLikeNode; +use rustc_middle::ich::StableHashingContext; +use rustc_middle::mir::coverage::*; +use rustc_middle::mir::{ + self, BasicBlock, BasicBlockData, Coverage, SourceInfo, Statement, StatementKind, Terminator, + TerminatorKind, +}; +use rustc_middle::ty::TyCtxt; +use rustc_span::def_id::DefId; +use rustc_span::{CharPos, Pos, SourceFile, Span, Symbol}; + +/// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected +/// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen +/// to construct the coverage map. +pub struct InstrumentCoverage; + +impl<'tcx> MirPass<'tcx> for InstrumentCoverage { + fn run_pass(&self, tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) { + let mir_source = mir_body.source; + + // If the InstrumentCoverage pass is called on promoted MIRs, skip them. + // See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601 + if mir_source.promoted.is_some() { + trace!( + "InstrumentCoverage skipped for {:?} (already promoted for Miri evaluation)", + mir_source.def_id() + ); + return; + } + + let hir_id = tcx.hir().local_def_id_to_hir_id(mir_source.def_id().expect_local()); + let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some(); + + // Only instrument functions, methods, and closures (not constants since they are evaluated + // at compile time by Miri). + // FIXME(#73156): Handle source code coverage in const eval, but note, if and when const + // expressions get coverage spans, we will probably have to "carve out" space for const + // expressions from coverage spans in enclosing MIR's, like we do for closures. (That might + // be tricky if const expressions have no corresponding statements in the enclosing MIR. + // Closures are carved out by their initial `Assign` statement.) + if !is_fn_like { + trace!("InstrumentCoverage skipped for {:?} (not an FnLikeNode)", mir_source.def_id()); + return; + } + // FIXME(richkadel): By comparison, the MIR pass `ConstProp` includes associated constants, + // with functions, methods, and closures. I assume Miri is used for associated constants as + // well. If not, we may need to include them here too. + + trace!("InstrumentCoverage starting for {:?}", mir_source.def_id()); + Instrumentor::new(&self.name(), tcx, mir_body).inject_counters(); + trace!("InstrumentCoverage starting for {:?}", mir_source.def_id()); + } +} + +struct Instrumentor<'a, 'tcx> { + pass_name: &'a str, + tcx: TyCtxt<'tcx>, + mir_body: &'a mut mir::Body<'tcx>, + hir_body: &'tcx rustc_hir::Body<'tcx>, + bcb_dominators: Dominators, + basic_coverage_blocks: CoverageGraph, + function_source_hash: Option, + next_counter_id: u32, + num_expressions: u32, + debug_expressions_cache: Option>, + debug_counters: debug::DebugCounters, +} + +impl<'a, 'tcx> Instrumentor<'a, 'tcx> { + fn new(pass_name: &'a str, tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self { + let hir_body = hir_body(tcx, mir_body.source.def_id()); + let basic_coverage_blocks = CoverageGraph::from_mir(mir_body); + let bcb_dominators = basic_coverage_blocks.compute_bcb_dominators(); + Self { + pass_name, + tcx, + mir_body, + hir_body, + basic_coverage_blocks, + bcb_dominators, + function_source_hash: None, + next_counter_id: CounterValueReference::START.as_u32(), + num_expressions: 0, + debug_expressions_cache: None, + debug_counters: debug::DebugCounters::new(), + } + } + + /// Counter IDs start from one and go up. + fn next_counter(&mut self) -> CounterValueReference { + assert!(self.next_counter_id < u32::MAX - self.num_expressions); + let next = self.next_counter_id; + self.next_counter_id += 1; + CounterValueReference::from(next) + } + + /// Expression IDs start from u32::MAX and go down because a Expression can reference + /// (add or subtract counts) of both Counter regions and Expression regions. The counter + /// expression operand IDs must be unique across both types. + fn next_expression(&mut self) -> InjectedExpressionId { + assert!(self.next_counter_id < u32::MAX - self.num_expressions); + let next = u32::MAX - self.num_expressions; + self.num_expressions += 1; + InjectedExpressionId::from(next) + } + + fn function_source_hash(&mut self) -> u64 { + match self.function_source_hash { + Some(hash) => hash, + None => { + let hash = hash_mir_source(self.tcx, self.hir_body); + self.function_source_hash.replace(hash); + hash + } + } + } + + fn inject_counters(&'a mut self) { + let tcx = self.tcx; + let source_map = tcx.sess.source_map(); + let mir_source = self.mir_body.source; + let def_id = mir_source.def_id(); + let body_span = self.body_span(); + + debug!("instrumenting {:?}, span: {}", def_id, source_map.span_to_string(body_span)); + + let dump_spanview = pretty::dump_enabled(tcx, self.pass_name, def_id); + let dump_graphviz = tcx.sess.opts.debugging_opts.dump_mir_graphviz; + + if dump_graphviz { + self.debug_counters.enable(); + } + + let coverage_spans = CoverageSpans::generate_coverage_spans( + &self.mir_body, + body_span, + &self.basic_coverage_blocks, + &self.bcb_dominators, + ); + + // When dumping coverage spanview files, create `SpanViewables` from the `coverage_spans`. + let mut debug_span_viewables = None; + if dump_spanview { + debug_span_viewables = Some(self.span_viewables(&coverage_spans)); + } + + // When debug logging, or generating the coverage graphviz output, initialize the following + // data structures: + let mut debug_used_expressions = debug::UsedExpressions::new(); + if level_enabled!(tracing::Level::DEBUG) || dump_graphviz { + debug_used_expressions.enable(); + + if debug_options().simplify_expressions { + self.debug_expressions_cache.replace(FxHashMap::default()); + } + // CAUTION! The `simplify_expressions` option is only helpful for some debugging + // situations and it can change the generated MIR `Coverage` statements (resulting in + // differences in behavior when enabled, under `DEBUG`, compared to normal operation and + // testing). + // + // For debugging purposes, it is sometimes helpful to simplify some expression + // equations: + // + // * `x + (y - x)` becomes just `y` + // * `x + (y + 0)` becomes just x + y. + // + // Expression dependencies can deeply nested expressions, which can look quite long in + // printed debug messages and in graphs produced by `-Zdump-graphviz`. In reality, each + // referenced/nested expression is only present because that value is necessary to + // compute a counter value for another part of the coverage report. Simplifying + // expressions Does not result in less `Coverage` statements, so there is very little, + // if any, benefit to binary size or runtime to simplifying expressions, and adds + // additional compile-time complexity. Only enable this temporarily, if helpful to parse + // the debug output. + } + + // When debugging with BCB graphviz output, initialize additional data structures. + let mut graphviz_data = debug::GraphvizData::new(); + if dump_graphviz { + graphviz_data.enable(); + } + + // Analyze the coverage graph (aka, BCB control flow graph), and inject expression-optimized + // counters. + let mut collect_intermediate_expressions = + Vec::with_capacity(self.basic_coverage_blocks.num_nodes()); + self.make_bcb_counters(&coverage_spans, &mut collect_intermediate_expressions); + + // If debugging, add any intermediate expressions (which are not associated with any BCB) to + // the `debug_used_expressions` map. + + if debug_used_expressions.is_enabled() { + for intermediate_expression in &collect_intermediate_expressions { + debug_used_expressions.add_expression_operands(intermediate_expression); + } + } + + // Inject a counter for each `CoverageSpan`. + self.inject_coverage_span_counters( + coverage_spans, + &mut graphviz_data, + &mut debug_used_expressions, + ); + + // The previous step looped through the `CoverageSpan`s and injected the counter from the + // `CoverageSpan`s `BasicCoverageBlock`, removing it from the BCB in the process (via + // `take_counter()`). + // + // Any other counter associated with a `BasicCoverageBlock`, or its incoming edge, but not + // associated with a `CoverageSpan`, should only exist if the counter is a + // `Expression` dependency (one of the expression operands). Collect them, and inject + // the additional counters into the MIR, without a reportable coverage span. + let mut bcb_counters_without_direct_coverage_spans = Vec::new(); + for (target_bcb, target_bcb_data) in self.basic_coverage_blocks.iter_enumerated_mut() { + if let Some(counter_kind) = target_bcb_data.take_counter() { + bcb_counters_without_direct_coverage_spans.push((None, target_bcb, counter_kind)); + } + if let Some(edge_counters) = target_bcb_data.take_edge_counters() { + for (from_bcb, counter_kind) in edge_counters { + bcb_counters_without_direct_coverage_spans.push(( + Some(from_bcb), + target_bcb, + counter_kind, + )); + } + } + } + + if debug_used_expressions.is_enabled() { + // Validate that every BCB or edge counter not directly associated with a coverage span + // is at least indirectly associated (it is a dependency of a BCB counter that _is_ + // associated with a coverage span). + let mut not_validated = bcb_counters_without_direct_coverage_spans + .iter() + .map(|(_, _, counter_kind)| counter_kind) + .collect::>(); + let mut validating_count = 0; + while not_validated.len() != validating_count { + let to_validate = not_validated.split_off(0); + validating_count = to_validate.len(); + for counter_kind in to_validate { + if debug_used_expressions.expression_is_used(counter_kind) { + debug_used_expressions.add_expression_operands(counter_kind); + } else { + not_validated.push(counter_kind); + } + } + } + } + + self.inject_indirect_counters( + bcb_counters_without_direct_coverage_spans, + &mut graphviz_data, + &mut debug_used_expressions, + ); + + if graphviz_data.is_enabled() { + let node_content = |bcb| { + self.bcb_to_string_sections( + self.bcb_data(bcb), + graphviz_data.get_bcb_coverage_spans_with_counters(bcb), + graphviz_data.get_bcb_dependency_counters(bcb), + // collect_intermediate_expressions are injected into the mir::START_BLOCK, so + // include them in the first BCB. + if bcb.index() == 0 { Some(&collect_intermediate_expressions) } else { None }, + ) + }; + let edge_labels = |from_bcb| { + let from_terminator = self.bcb_terminator(from_bcb); + let mut edge_labels = from_terminator.kind.fmt_successor_labels(); + edge_labels.retain(|label| label.to_string() != "unreachable"); + let edge_counters = from_terminator + .successors() + .map(|&successor_bb| graphviz_data.get_edge_counter(from_bcb, successor_bb)); + edge_labels + .iter() + .zip(edge_counters) + .map(|(label, some_counter)| { + if let Some(counter) = some_counter { + format!("{}\n{}", label, self.format_counter(counter)) + } else { + label.to_string() + } + }) + .collect::>() + }; + let graphviz_name = format!("Cov_{}_{}", def_id.krate.index(), def_id.index.index()); + let mut graphviz_writer = GraphvizWriter::new( + &self.basic_coverage_blocks, + &graphviz_name, + node_content, + edge_labels, + ); + let unused_expressions = debug_used_expressions.get_unused_expressions(); + if unused_expressions.len() > 0 { + graphviz_writer.set_graph_label(&format!( + "Unused expressions:\n {}", + unused_expressions + .as_slice() + .iter() + .map(|(counter_kind, edge_from_bcb, target_bcb)| { + if let Some(from_bcb) = edge_from_bcb.as_ref() { + format!( + "{:?}->{:?}: {}", + from_bcb, + target_bcb, + self.format_counter(&counter_kind), + ) + } else { + format!("{:?}: {}", target_bcb, self.format_counter(&counter_kind),) + } + }) + .collect::>() + .join("\n ") + )); + } + let mut file = + pretty::create_dump_file(tcx, "dot", None, self.pass_name, &0, mir_source) + .expect("Unexpected error creating BasicCoverageBlock graphviz DOT file"); + graphviz_writer + .write_graphviz(tcx, &mut file) + .expect("Unexpected error writing BasicCoverageBlock graphviz DOT file"); + } + + debug_used_expressions.check_no_unused(&self.debug_counters); + + for intermediate_expression in collect_intermediate_expressions { + self.inject_intermediate_expression(intermediate_expression); + } + + if let Some(span_viewables) = debug_span_viewables { + let mut file = + pretty::create_dump_file(tcx, "html", None, self.pass_name, &0, mir_source) + .expect("Unexpected error creating MIR spanview HTML file"); + let crate_name = tcx.crate_name(def_id.krate); + let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate(); + let title = format!("{}.{} - Coverage Spans", crate_name, item_name); + spanview::write_document(tcx, def_id, span_viewables, &title, &mut file) + .expect("Unexpected IO error dumping coverage spans as HTML"); + } + } + + /// Inject a counter for each `CoverageSpan`. There can be multiple `CoverageSpan`s for a given + /// BCB, but only one actual counter needs to be incremented per BCB. `bb_counters` maps each + /// `bcb` to its `Counter`, when injected. Subsequent `CoverageSpan`s for a BCB that already has + /// a `Counter` will inject an `Expression` instead, and compute its value by adding `ZERO` to + /// the BCB `Counter` value. + /// + /// If debugging, add every BCB `Expression` associated with a `CoverageSpan`s to the + /// `used_expression_operands` map. + fn inject_coverage_span_counters( + &mut self, + coverage_spans: Vec, + graphviz_data: &mut debug::GraphvizData, + debug_used_expressions: &mut debug::UsedExpressions, + ) { + let tcx = self.tcx; + let source_map = tcx.sess.source_map(); + let body_span = self.body_span(); + let source_file = source_map.lookup_source_file(body_span.lo()); + let file_name = Symbol::intern(&source_file.name.to_string()); + + let mut bcb_counters = IndexVec::from_elem_n(None, self.basic_coverage_blocks.num_nodes()); + for covspan in coverage_spans { + let bcb = covspan.bcb; + let span = covspan.span; + let counter_kind = if let Some(&counter_operand) = bcb_counters[bcb].as_ref() { + self.make_identity_counter(counter_operand) + } else if let Some(counter_kind) = self.bcb_data_mut(bcb).take_counter() { + bcb_counters[bcb] = Some(counter_kind.as_operand_id()); + debug_used_expressions.add_expression_operands(&counter_kind); + counter_kind + } else { + bug!("Every BasicCoverageBlock should have a Counter or Expression"); + }; + graphviz_data.add_bcb_coverage_span_with_counter(bcb, &covspan, &counter_kind); + let some_code_region = if self.is_code_region_redundant(bcb, span, body_span) { + None + } else { + Some(make_code_region(file_name, &source_file, span, body_span)) + }; + self.inject_statement(counter_kind, self.bcb_last_bb(bcb), some_code_region); + } + } + + fn is_code_region_redundant( + &self, + bcb: BasicCoverageBlock, + span: Span, + body_span: Span, + ) -> bool { + if span.hi() == body_span.hi() { + // All functions execute a `Return`-terminated `BasicBlock`, regardless of how the + // function returns; but only some functions also _can_ return after a `Goto` block + // that ends on the closing brace of the function (with the `Return`). When this + // happens, the last character is counted 2 (or possibly more) times, when we know + // the function returned only once (of course). By giving all `Goto` terminators at + // the end of a function a `non-reportable` code region, they are still counted + // if appropriate, but they don't increment the line counter, as long as their is + // also a `Return` on that last line. + if let TerminatorKind::Goto { .. } = self.bcb_terminator(bcb).kind { + return true; + } + } + false + } + + fn inject_indirect_counters( + &mut self, + bcb_counters_without_direct_coverage_spans: Vec<( + Option, + BasicCoverageBlock, + CoverageKind, + )>, + graphviz_data: &mut debug::GraphvizData, + debug_used_expressions: &mut debug::UsedExpressions, + ) { + for (edge_from_bcb, target_bcb, counter_kind) in bcb_counters_without_direct_coverage_spans + { + debug_used_expressions.validate_expression_is_used( + &counter_kind, + edge_from_bcb, + target_bcb, + ); + + match counter_kind { + CoverageKind::Counter { .. } => { + let inject_to_bb = if let Some(from_bcb) = edge_from_bcb { + // The MIR edge starts `from_bb` (the outgoing / last BasicBlock in + // `from_bcb`) and ends at `to_bb` (the incoming / first BasicBlock in the + // `target_bcb`; also called the `leader_bb`). + let from_bb = self.bcb_last_bb(from_bcb); + let to_bb = self.bcb_leader_bb(target_bcb); + + debug!( + "Edge {:?} (last {:?}) -> {:?} (leader {:?}) requires a new MIR \ + BasicBlock, for unclaimed edge counter {}", + edge_from_bcb, + from_bb, + target_bcb, + to_bb, + self.format_counter(&counter_kind), + ); + debug!( + " from_bb {:?} has successors: {:?}", + from_bb, + self.mir_body[from_bb].terminator().successors(), + ); + let span = + self.mir_body[from_bb].terminator().source_info.span.shrink_to_hi(); + let new_bb = self.mir_body.basic_blocks_mut().push(BasicBlockData { + statements: vec![], // counter will be injected here + terminator: Some(Terminator { + source_info: SourceInfo::outermost(span), + kind: TerminatorKind::Goto { target: to_bb }, + }), + is_cleanup: false, + }); + debug!( + "Edge from_bcb={:?} to to_bb={:?} has edge_counter={}", + from_bcb, + new_bb, + self.format_counter(&counter_kind), + ); + graphviz_data.set_edge_counter(from_bcb, new_bb, &counter_kind); + let edge_ref = self.mir_body[from_bb] + .terminator_mut() + .successors_mut() + .find(|successor| **successor == to_bb) + .expect("from_bb should have a successor for to_bb"); + *edge_ref = new_bb; + new_bb + } else { + graphviz_data.add_bcb_dependency_counter(target_bcb, &counter_kind); + let target_bb = self.bcb_last_bb(target_bcb); + debug!( + "{:?} ({:?}) gets a new Coverage statement for unclaimed counter {}", + target_bcb, + target_bb, + self.format_counter(&counter_kind), + ); + target_bb + }; + + self.inject_statement(counter_kind, inject_to_bb, None); + } + CoverageKind::Expression { .. } => { + self.inject_intermediate_expression(counter_kind) + } + _ => bug!("CoverageKind should be a counter"), + } + } + } + + /// Traverse the BCB CFG and add either a `Counter` or `Expression` to ever BCB, to be + /// injected with `CoverageSpan`s. `Expressions` have no runtime overhead, so if a viable + /// expression (adding or subtracting two other counters or expressions) can compute the same + /// result as an embedded counter, an `Expression` should be used. + /// + /// If two `CoverageGraph` branch from another `BasicCoverageBlock`, one of the branches + /// can be counted by `Expression` by subtracting the other branch from the branching + /// block. Otherwise, the `BasicCoverageBlock` executed the least should have the `Counter`. + /// One way to predict which branch executes the least is by considering loops. A loop is exited + /// at a branch, so the branch that jumps to a `BasicCoverageBlock` outside the loop is almost + /// always executed less than the branch that does not exit the loop. + /// + /// Returns non-code-span expressions created to represent intermediate values (if required), + /// such as to add two counters so the result can be subtracted from another counter. + fn make_bcb_counters( + &mut self, + coverage_spans: &Vec, + collect_intermediate_expressions: &mut Vec, + ) { + debug!("make_bcb_counters(): adding a counter or expression to each BasicCoverageBlock"); + let num_bcbs = self.basic_coverage_blocks.num_nodes(); + + let mut bcbs_with_coverage = BitSet::new_empty(num_bcbs); + for covspan in coverage_spans { + bcbs_with_coverage.insert(covspan.bcb); + } + + // FIXME(richkadel): Add more comments to explain the logic here and in the rest of this + // function, and refactor this function to break it up into smaller functions that are + // easier to understand. + + let mut traversal = + TraverseCoverageGraphWithLoops::new(&self.basic_coverage_blocks, &self.bcb_dominators); + while let Some(bcb) = traversal.next() { + debug!( + "{:?} has {} successors:", + bcb, + self.basic_coverage_blocks.successors[bcb].len() + ); + for &successor in &self.basic_coverage_blocks.successors[bcb] { + for context in traversal.context_stack.iter_mut().rev() { + // Add successors of the current BCB to the appropriate context. Successors that + // stay within a loop are added to the BCBs context worklist. + // + // Branching blocks (with more than one successor) must be processed before + // blocks with only one successor, to prevent unnecessarily complicating + // Expression`s by creating a Counter in a `BasicCoverageBlock` that the + // branching block would have given an `Expression` (or vice versa). + if let Some((_, loop_header)) = context.loop_backedges { + if self.bcb_is_dominated_by(successor, loop_header) { + if self.bcb_successors(successor).len() > 1 { + debug!( + "Adding branching successor {:?} to the beginning of the \ + worklist of loop headed by {:?}", + successor, loop_header + ); + context.worklist.insert(0, successor); + } else { + debug!( + "Adding non-branching successor {:?} to the end of the \ + worklist of loop headed by {:?}", + successor, loop_header + ); + context.worklist.push(successor); + } + break; + } + } else { + if self.bcb_successors(successor).len() > 1 { + debug!( + "Adding branching successor {:?} to the beginning of the non-loop \ + worklist", + successor + ); + context.worklist.insert(0, successor); + } else { + debug!( + "Adding non-branching successor {:?} to the end of the non-loop \ + worklist", + successor + ); + context.worklist.push(successor); + } + } + } + } + + if !bcbs_with_coverage.contains(bcb) { + continue; + } + + let bcb_counter_operand = + self.get_or_make_counter_operand(bcb, collect_intermediate_expressions); + + let needs_branch_counters = { + let successors = self.bcb_successors(bcb); + successors.len() > 1 + && successors + .iter() + .any(|&successor| self.bcb_data(successor).counter().is_none()) + }; + if needs_branch_counters { + let branching_bcb = bcb; + let branching_counter_operand = bcb_counter_operand; + let branches = self.bcb_successors(branching_bcb).clone(); + + debug!("{:?} is branching, with branches: {:?}", branching_bcb, branches); + + // At most one of the branches (or its edge, from the branching_bcb, + // if the branch has multiple incoming edges) can have a counter computed by + // expression. + // + // If at least one of the branches leads outside of a loop (`found_loop_exit` is + // true), and at least one other branch does not exit the loop (the first of which + // is captured in `some_reloop_branch`), it's likely any reloop branch will be + // executed far more often than loop exit branch, making the reloop branch a better + // candidate for an expression. + let mut some_reloop_branch = None; + for context in traversal.context_stack.iter().rev() { + if let Some((backedge_from_bcbs, _)) = &context.loop_backedges { + let mut found_loop_exit = false; + for &branch in branches.iter() { + if backedge_from_bcbs.iter().any(|&backedge_from_bcb| { + self.bcb_is_dominated_by(backedge_from_bcb, branch) + }) { + // The path from branch leads back to the top of the loop + some_reloop_branch = Some(branch); + } else { + // The path from branch leads outside this loop + found_loop_exit = true; + } + if some_reloop_branch.is_some() && found_loop_exit { + break; + } + } + debug!( + "found_loop_exit={}, some_reloop_branch={:?}", + found_loop_exit, some_reloop_branch + ); + if !found_loop_exit { + // No branches exit a loop, so there is no specific recommended branch + // for an `Expression`. + break; + } + if some_reloop_branch.is_some() { + // A recommended branch for an `Expression` was found. + break; + } + // else all branches exited this loop context, so run the same checks with + // the outer loop(s) + } + } + + // Select a branch for the expression, either the recommended `reloop_branch`, or + // if none was found, select any branch. + let expression_branch = if let Some(reloop_branch) = some_reloop_branch { + debug!("Adding expression to reloop_branch={:?}", reloop_branch); + reloop_branch + } else { + let &branch_without_counter = branches + .iter() + .find(|&&branch| self.bcb_data(branch).counter().is_none()) + .expect( + "needs_branch_counters was `true` so there should be at least one \ + branch", + ); + debug!( + "No preferred expression branch. Selected the first branch without a \ + counter. That branch={:?}", + branch_without_counter + ); + branch_without_counter + }; + + // Assign a Counter or Expression to each branch, plus additional + // `Expression`s, as needed, to sum up intermediate results. + let mut some_sumup_counter_operand = None; + for branch in branches { + if branch != expression_branch { + let branch_counter_operand = if self.bcb_has_multiple_incoming_edges(branch) + { + debug!( + "{:?} has multiple incoming edges, so adding an edge counter from \ + {:?}", + branch, branching_bcb + ); + self.get_or_make_edge_counter_operand( + branching_bcb, + branch, + collect_intermediate_expressions, + ) + } else { + debug!( + "{:?} has only one incoming edge (from {:?}), so adding a counter", + branch, branching_bcb + ); + self.get_or_make_counter_operand( + branch, + collect_intermediate_expressions, + ) + }; + if let Some(sumup_counter_operand) = + some_sumup_counter_operand.replace(branch_counter_operand) + { + let intermediate_expression = self.make_expression( + branch_counter_operand, + Op::Add, + sumup_counter_operand, + || None, + ); + debug!( + " new intermediate expression: {}", + self.format_counter(&intermediate_expression) + ); + let intermediate_expression_operand = + intermediate_expression.as_operand_id(); + collect_intermediate_expressions.push(intermediate_expression); + some_sumup_counter_operand.replace(intermediate_expression_operand); + } + } + } + let sumup_counter_operand = + some_sumup_counter_operand.expect("sumup_counter_operand should have a value"); + let multiple_incoming_edges = + self.bcb_has_multiple_incoming_edges(expression_branch); + debug!( + "expression_branch is {:?}, multiple_incoming_edges={}, expression_branch \ + predecessors: {:?}", + expression_branch, + multiple_incoming_edges, + self.bcb_predecessors(expression_branch) + ); + let expression = self.make_expression( + branching_counter_operand, + Op::Subtract, + sumup_counter_operand, + || { + Some(if multiple_incoming_edges { + format!("{:?}->{:?}", branching_bcb, expression_branch) + } else { + format!("{:?}", expression_branch) + }) + }, + ); + if multiple_incoming_edges { + debug!( + "Edge {:?}->{:?} gets an expression: {}", + branching_bcb, + expression_branch, + self.format_counter(&expression) + ); + self.bcb_data_mut(expression_branch) + .set_edge_counter_from(branching_bcb, expression); + } else { + debug!( + "{:?} gets an expression: {}", + expression_branch, + self.format_counter(&expression) + ); + self.bcb_data_mut(expression_branch).set_counter(expression); + } + } + } + } + + #[inline] + fn format_counter(&self, counter_kind: &CoverageKind) -> String { + self.debug_counters.format_counter(counter_kind) + } + + #[inline] + fn bcb_leader_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock { + self.bcb_data(bcb).leader_bb() + } + + #[inline] + fn bcb_last_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock { + self.bcb_data(bcb).last_bb() + } + + #[inline] + fn bcb_terminator(&self, bcb: BasicCoverageBlock) -> &Terminator<'tcx> { + self.bcb_data(bcb).terminator(self.mir_body) + } + + #[inline] + fn bcb_data(&self, bcb: BasicCoverageBlock) -> &BasicCoverageBlockData { + &self.basic_coverage_blocks[bcb] + } + + #[inline] + fn bcb_data_mut(&mut self, bcb: BasicCoverageBlock) -> &mut BasicCoverageBlockData { + &mut self.basic_coverage_blocks[bcb] + } + + #[inline] + fn bcb_predecessors(&self, bcb: BasicCoverageBlock) -> &Vec { + &self.basic_coverage_blocks.predecessors[bcb] + } + + #[inline] + fn bcb_successors(&self, bcb: BasicCoverageBlock) -> &Vec { + &self.basic_coverage_blocks.successors[bcb] + } + + #[inline] + fn bcb_has_multiple_incoming_edges(&self, bcb: BasicCoverageBlock) -> bool { + self.bcb_predecessors(bcb).len() > 1 + } + + #[inline] + fn bcb_is_dominated_by(&self, node: BasicCoverageBlock, dom: BasicCoverageBlock) -> bool { + self.bcb_dominators.is_dominated_by(node, dom) + } + + fn bcb_to_string_sections( + &self, + bcb_data: &BasicCoverageBlockData, + some_coverage_spans_with_counters: Option<&Vec<(CoverageSpan, CoverageKind)>>, + some_dependency_counters: Option<&Vec>, + some_intermediate_expressions: Option<&Vec>, + ) -> Vec { + let len = bcb_data.basic_blocks.len(); + let mut sections = Vec::new(); + if let Some(collect_intermediate_expressions) = some_intermediate_expressions { + sections.push( + collect_intermediate_expressions + .iter() + .map(|expression| format!("Intermediate {}", self.format_counter(expression))) + .collect::>() + .join("\n"), + ); + } + if let Some(coverage_spans_with_counters) = some_coverage_spans_with_counters { + sections.push( + coverage_spans_with_counters + .iter() + .map(|(covspan, counter)| { + format!( + "{} at {}", + self.format_counter(counter), + covspan.format(self.tcx, self.mir_body) + ) + }) + .collect::>() + .join("\n"), + ); + } + if let Some(dependency_counters) = some_dependency_counters { + sections.push(format!( + "Non-coverage counters:\n {}", + dependency_counters + .iter() + .map(|counter| self.format_counter(counter)) + .collect::>() + .join(" \n"), + )); + } + if let Some(counter_kind) = &bcb_data.counter_kind { + sections.push(format!("{:?}", counter_kind)); + } + let non_term_blocks = bcb_data.basic_blocks[0..len - 1] + .iter() + .map(|&bb| format!("{:?}: {}", bb, term_type(&self.mir_body[bb].terminator().kind))) + .collect::>(); + if non_term_blocks.len() > 0 { + sections.push(non_term_blocks.join("\n")); + } + sections.push(format!( + "{:?}: {}", + bcb_data.basic_blocks.last().unwrap(), + term_type(&bcb_data.terminator(self.mir_body).kind) + )); + sections + } + + fn get_or_make_counter_operand( + &mut self, + bcb: BasicCoverageBlock, + collect_intermediate_expressions: &mut Vec, + ) -> ExpressionOperandId { + if let Some(counter_kind) = self.basic_coverage_blocks[bcb].counter() { + debug!(" {:?} already has a counter: {}", bcb, self.format_counter(counter_kind)); + counter_kind.as_operand_id() + } else { + if self.bcb_has_multiple_incoming_edges(bcb) { + let mut predecessors = self.bcb_predecessors(bcb).clone().into_iter(); + let first_edge_counter_operand = self.get_or_make_edge_counter_operand( + predecessors.next().unwrap(), + bcb, + collect_intermediate_expressions, + ); + let mut some_sumup_edge_counter_operand = None; + for predecessor in predecessors { + let edge_counter_operand = self.get_or_make_edge_counter_operand( + predecessor, + bcb, + collect_intermediate_expressions, + ); + if let Some(sumup_edge_counter_operand) = + some_sumup_edge_counter_operand.replace(edge_counter_operand) + { + let intermediate_expression = self.make_expression( + sumup_edge_counter_operand, + Op::Add, + edge_counter_operand, + || None, + ); + debug!( + " new intermediate expression: {}", + self.format_counter(&intermediate_expression) + ); + let intermediate_expression_operand = + intermediate_expression.as_operand_id(); + collect_intermediate_expressions.push(intermediate_expression); + some_sumup_edge_counter_operand.replace(intermediate_expression_operand); + } + } + let counter_kind = self.make_expression( + first_edge_counter_operand, + Op::Add, + some_sumup_edge_counter_operand.unwrap(), + || Some(format!("{:?}", bcb)), + ); + debug!( + " {:?} gets a new counter (sum of predecessor counters): {}", + bcb, + self.format_counter(&counter_kind) + ); + self.basic_coverage_blocks[bcb].set_counter(counter_kind) + } else { + let counter_kind = self.make_counter(|| Some(format!("{:?}", bcb))); + debug!(" {:?} gets a new counter: {}", bcb, self.format_counter(&counter_kind)); + self.basic_coverage_blocks[bcb].set_counter(counter_kind) + } + } + } + + fn get_or_make_edge_counter_operand( + &mut self, + from_bcb: BasicCoverageBlock, + to_bcb: BasicCoverageBlock, + collect_intermediate_expressions: &mut Vec, + ) -> ExpressionOperandId { + let successors = self.bcb_successors(from_bcb).iter(); + if successors.len() > 1 { + if let Some(counter_kind) = + self.basic_coverage_blocks[to_bcb].edge_counter_from(from_bcb) + { + debug!( + " Edge {:?}->{:?} already has a counter: {}", + from_bcb, + to_bcb, + self.format_counter(counter_kind) + ); + counter_kind.as_operand_id() + } else { + let counter_kind = + self.make_counter(|| Some(format!("{:?}->{:?}", from_bcb, to_bcb))); + debug!( + " Edge {:?}->{:?} gets a new counter: {}", + from_bcb, + to_bcb, + self.format_counter(&counter_kind) + ); + self.basic_coverage_blocks[to_bcb].set_edge_counter_from(from_bcb, counter_kind) + } + } else { + self.get_or_make_counter_operand(from_bcb, collect_intermediate_expressions) + } + } + + fn make_counter(&mut self, block_label_fn: F) -> CoverageKind + where + F: Fn() -> Option, + { + let counter = CoverageKind::Counter { + function_source_hash: self.function_source_hash(), + id: self.next_counter(), + }; + if self.debug_counters.is_enabled() { + self.debug_counters.add_counter(&counter, (block_label_fn)()); + } + counter + } + + fn make_expression( + &mut self, + mut lhs: ExpressionOperandId, + op: Op, + mut rhs: ExpressionOperandId, + block_label_fn: F, + ) -> CoverageKind + where + F: Fn() -> Option, + { + if let Some(expressions_cache) = self.debug_expressions_cache.as_ref() { + if let Some(CoverageKind::Expression { lhs: lhs_lhs, op, rhs: lhs_rhs, .. }) = + expressions_cache.get(&lhs) + { + if *lhs_rhs == ExpressionOperandId::ZERO { + lhs = *lhs_lhs; + } else if *op == Op::Subtract && *lhs_rhs == rhs { + if let Some(lhs_expression) = expressions_cache.get(lhs_lhs) { + let expression = lhs_expression.clone(); + return self.duplicate_expression(expression); + } else { + let counter = *lhs_lhs; + return self.make_identity_counter(counter); + } + } + } + + if let Some(CoverageKind::Expression { lhs: rhs_lhs, op, rhs: rhs_rhs, .. }) = + expressions_cache.get(&rhs) + { + if *rhs_rhs == ExpressionOperandId::ZERO { + rhs = *rhs_rhs; + } else if *op == Op::Subtract && *rhs_rhs == lhs { + if let Some(rhs_expression) = expressions_cache.get(rhs_lhs) { + let expression = rhs_expression.clone(); + return self.duplicate_expression(expression); + } else { + let counter = *rhs_lhs; + return self.make_identity_counter(counter); + } + } + } + } + + let id = self.next_expression(); + let expression = CoverageKind::Expression { id, lhs, op, rhs }; + if let Some(expressions_cache) = self.debug_expressions_cache.as_mut() { + expressions_cache.insert(id.into(), expression.clone()); + } + if self.debug_counters.is_enabled() { + self.debug_counters.add_counter(&expression, (block_label_fn)()); + } + expression + } + + fn make_identity_counter(&mut self, counter_operand: ExpressionOperandId) -> CoverageKind { + if let Some(expression) = + self.debug_expressions_cache.as_ref().map_or(None, |c| c.get(&counter_operand)) + { + let new_expression = expression.clone(); + self.duplicate_expression(new_expression) + } else { + let some_block_label = if self.debug_counters.is_enabled() { + self.debug_counters.some_block_label(counter_operand).cloned() + } else { + None + }; + self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO, || { + some_block_label.clone() + }) + } + } + + fn duplicate_expression(&mut self, mut expression: CoverageKind) -> CoverageKind { + let next_expression_id = if self.debug_expressions_cache.is_some() { + Some(self.next_expression()) + } else { + None + }; + let expressions_cache = self + .debug_expressions_cache + .as_mut() + .expect("`duplicate_expression()` requires the debug_expressions_cache"); + match expression { + CoverageKind::Expression { ref mut id, .. } => { + *id = next_expression_id.expect( + "next_expression_id should be Some if there is a debug_expressions_cache", + ); + expressions_cache.insert(id.into(), expression.clone()); + } + _ => { + bug!("make_duplicate_expression called with non-expression type: {:?}", expression) + } + } + expression + } + + fn inject_statement( + &mut self, + counter_kind: CoverageKind, + bb: BasicBlock, + some_code_region: Option, + ) { + debug!( + " injecting statement {:?} for {:?} at code region: {:?}", + counter_kind, bb, some_code_region + ); + let data = &mut self.mir_body[bb]; + let source_info = data.terminator().source_info; + let statement = Statement { + source_info, + kind: StatementKind::Coverage(box Coverage { + kind: counter_kind, + code_region: some_code_region, + }), + }; + data.statements.push(statement); + } + + // Non-code expressions are injected into the coverage map, without generating executable code. + fn inject_intermediate_expression(&mut self, expression: CoverageKind) { + debug_assert!(if let CoverageKind::Expression { .. } = expression { true } else { false }); + debug!(" injecting non-code expression {:?}", expression); + let inject_in_bb = mir::START_BLOCK; + let data = &mut self.mir_body[inject_in_bb]; + let source_info = data.terminator().source_info; + let statement = Statement { + source_info, + kind: StatementKind::Coverage(box Coverage { kind: expression, code_region: None }), + }; + data.statements.push(statement); + } + + /// Converts the computed `BasicCoverageBlockData`s into `SpanViewable`s. + fn span_viewables(&self, coverage_spans: &Vec) -> Vec { + let tcx = self.tcx; + let mut span_viewables = Vec::new(); + for coverage_span in coverage_spans { + let tooltip = coverage_span.format_coverage_statements(tcx, self.mir_body); + let CoverageSpan { span, bcb, .. } = coverage_span; + let bcb_data = self.bcb_data(*bcb); + let id = bcb_data.id(); + let leader_bb = bcb_data.leader_bb(); + span_viewables.push(SpanViewable { bb: leader_bb, span: *span, id, tooltip }); + } + span_viewables + } + + #[inline(always)] + fn body_span(&self) -> Span { + self.hir_body.value.span + } +} + +/// Convert the Span into its file name, start line and column, and end line and column +fn make_code_region( + file_name: Symbol, + source_file: &Lrc, + span: Span, + body_span: Span, +) -> CodeRegion { + let (start_line, mut start_col) = source_file.lookup_file_pos(span.lo()); + let (end_line, end_col) = if span.hi() == span.lo() { + let (end_line, mut end_col) = (start_line, start_col); + // Extend an empty span by one character so the region will be counted. + let CharPos(char_pos) = start_col; + if span.hi() == body_span.hi() { + start_col = CharPos(char_pos - 1); + } else { + end_col = CharPos(char_pos + 1); + } + (end_line, end_col) + } else { + source_file.lookup_file_pos(span.hi()) + }; + CodeRegion { + file_name, + start_line: start_line as u32, + start_col: start_col.to_u32() + 1, + end_line: end_line as u32, + end_col: end_col.to_u32() + 1, + } +} + +fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx rustc_hir::Body<'tcx> { + let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local"); + let fn_body_id = hir::map::associated_body(hir_node).expect("HIR node is a function with body"); + tcx.hir().body(fn_body_id) +} + +fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx>) -> u64 { + let mut hcx = tcx.create_no_span_stable_hashing_context(); + hash(&mut hcx, &hir_body.value).to_smaller_hash() +} + +fn hash( + hcx: &mut StableHashingContext<'tcx>, + node: &impl HashStable>, +) -> Fingerprint { + let mut stable_hasher = StableHasher::new(); + node.hash_stable(hcx, &mut stable_hasher); + stable_hasher.finish() +} diff --git a/compiler/rustc_mir/src/transform/coverage/query.rs b/compiler/rustc_mir/src/transform/coverage/query.rs new file mode 100644 index 0000000000000..c17221b78ddd4 --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/query.rs @@ -0,0 +1,113 @@ +use rustc_middle::mir::coverage::*; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{Coverage, CoverageInfo, Location}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; +use rustc_span::def_id::DefId; + +/// The `query` provider for `CoverageInfo`, requested by `codegen_coverage()` (to inject each +/// counter) and `FunctionCoverage::new()` (to extract the coverage map metadata from the MIR). +pub(crate) fn provide(providers: &mut Providers) { + providers.coverageinfo = |tcx, def_id| coverageinfo_from_mir(tcx, def_id); +} + +/// The `num_counters` argument to `llvm.instrprof.increment` is the max counter_id + 1, or in +/// other words, the number of counter value references injected into the MIR (plus 1 for the +/// reserved `ZERO` counter, which uses counter ID `0` when included in an expression). Injected +/// counters have a counter ID from `1..num_counters-1`. +/// +/// `num_expressions` is the number of counter expressions added to the MIR body. +/// +/// Both `num_counters` and `num_expressions` are used to initialize new vectors, during backend +/// code generate, to lookup counters and expressions by simple u32 indexes. +/// +/// MIR optimization may split and duplicate some BasicBlock sequences, or optimize out some code +/// including injected counters. (It is OK if some counters are optimized out, but those counters +/// are still included in the total `num_counters` or `num_expressions`.) Simply counting the +/// calls may not work; but computing the number of counters or expressions by adding `1` to the +/// highest ID (for a given instrumented function) is valid. +/// +/// This visitor runs twice, first with `add_missing_operands` set to `false`, to find the maximum +/// counter ID and maximum expression ID based on their enum variant `id` fields; then, as a +/// safeguard, with `add_missing_operands` set to `true`, to find any other counter or expression +/// IDs referenced by expression operands, if not already seen. +/// +/// Ideally, every expression operand in the MIR will have a corresponding Counter or Expression, +/// but since current or future MIR optimizations can theoretically optimize out segments of a +/// MIR, it may not be possible to guarantee this, so the second pass ensures the `CoverageInfo` +/// counts include all referenced IDs. +struct CoverageVisitor { + info: CoverageInfo, + add_missing_operands: bool, +} + +impl CoverageVisitor { + // If an expression operand is encountered with an ID outside the range of known counters and + // expressions, the only way to determine if the ID is a counter ID or an expression ID is to + // assume a maximum possible counter ID value. + const MAX_COUNTER_GUARD: u32 = (u32::MAX / 2) + 1; + + #[inline(always)] + fn update_num_counters(&mut self, counter_id: u32) { + self.info.num_counters = std::cmp::max(self.info.num_counters, counter_id + 1); + } + + #[inline(always)] + fn update_num_expressions(&mut self, expression_id: u32) { + let expression_index = u32::MAX - expression_id; + self.info.num_expressions = std::cmp::max(self.info.num_expressions, expression_index + 1); + } + + fn update_from_expression_operand(&mut self, operand_id: u32) { + if operand_id >= self.info.num_counters { + let operand_as_expression_index = u32::MAX - operand_id; + if operand_as_expression_index >= self.info.num_expressions { + if operand_id <= Self::MAX_COUNTER_GUARD { + self.update_num_counters(operand_id) + } else { + self.update_num_expressions(operand_id) + } + } + } + } +} + +impl Visitor<'_> for CoverageVisitor { + fn visit_coverage(&mut self, coverage: &Coverage, _location: Location) { + if self.add_missing_operands { + match coverage.kind { + CoverageKind::Expression { lhs, rhs, .. } => { + self.update_from_expression_operand(u32::from(lhs)); + self.update_from_expression_operand(u32::from(rhs)); + } + _ => {} + } + } else { + match coverage.kind { + CoverageKind::Counter { id, .. } => { + self.update_num_counters(u32::from(id)); + } + CoverageKind::Expression { id, .. } => { + self.update_num_expressions(u32::from(id)); + } + _ => {} + } + } + } +} + +fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo { + let mir_body = tcx.optimized_mir(def_id); + + let mut coverage_visitor = CoverageVisitor { + info: CoverageInfo { num_counters: 0, num_expressions: 0 }, + add_missing_operands: false, + }; + + coverage_visitor.visit_body(mir_body); + + coverage_visitor.add_missing_operands = true; + coverage_visitor.visit_body(mir_body); + + coverage_visitor.info +} diff --git a/compiler/rustc_mir/src/transform/coverage/spans.rs b/compiler/rustc_mir/src/transform/coverage/spans.rs new file mode 100644 index 0000000000000..491ebedd51ea2 --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/spans.rs @@ -0,0 +1,773 @@ +use super::debug::term_type; +use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; + +use crate::util::spanview::source_range_no_file; + +use rustc_data_structures::graph::dominators::Dominators; +use rustc_data_structures::graph::WithNumNodes; +use rustc_index::bit_set::BitSet; +use rustc_middle::mir::{ + self, AggregateKind, BasicBlock, FakeReadCause, Rvalue, Statement, StatementKind, Terminator, + TerminatorKind, +}; +use rustc_middle::ty::TyCtxt; + +use rustc_span::source_map::original_sp; +use rustc_span::{BytePos, Span, SyntaxContext}; + +use std::cmp::Ordering; + +#[derive(Debug, Copy, Clone)] +pub(crate) enum CoverageStatement { + Statement(BasicBlock, Span, usize), + Terminator(BasicBlock, Span), +} + +impl CoverageStatement { + pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String { + match *self { + Self::Statement(bb, span, stmt_index) => { + let stmt = &mir_body[bb].statements[stmt_index]; + format!( + "{}: @{}[{}]: {:?}", + source_range_no_file(tcx, &span), + bb.index(), + stmt_index, + stmt + ) + } + Self::Terminator(bb, span) => { + let term = mir_body[bb].terminator(); + format!( + "{}: @{}.{}: {:?}", + source_range_no_file(tcx, &span), + bb.index(), + term_type(&term.kind), + term.kind + ) + } + } + } + + pub fn span(&self) -> &Span { + match self { + Self::Statement(_, span, _) | Self::Terminator(_, span) => span, + } + } +} + +/// A BCB is deconstructed into one or more `Span`s. Each `Span` maps to a `CoverageSpan` that +/// references the originating BCB and one or more MIR `Statement`s and/or `Terminator`s. +/// Initially, the `Span`s come from the `Statement`s and `Terminator`s, but subsequent +/// transforms can combine adjacent `Span`s and `CoverageSpan` from the same BCB, merging the +/// `CoverageStatement` vectors, and the `Span`s to cover the extent of the combined `Span`s. +/// +/// Note: A `CoverageStatement` merged into another CoverageSpan may come from a `BasicBlock` that +/// is not part of the `CoverageSpan` bcb if the statement was included because it's `Span` matches +/// or is subsumed by the `Span` associated with this `CoverageSpan`, and it's `BasicBlock` +/// `is_dominated_by()` the `BasicBlock`s in this `CoverageSpan`. +#[derive(Debug, Clone)] +pub(crate) struct CoverageSpan { + pub span: Span, + pub bcb: BasicCoverageBlock, + pub coverage_statements: Vec, + pub is_closure: bool, +} + +impl CoverageSpan { + pub fn for_statement( + statement: &Statement<'tcx>, + span: Span, + bcb: BasicCoverageBlock, + bb: BasicBlock, + stmt_index: usize, + ) -> Self { + let is_closure = match statement.kind { + StatementKind::Assign(box ( + _, + Rvalue::Aggregate(box AggregateKind::Closure(_, _), _), + )) => true, + _ => false, + }; + + Self { + span, + bcb, + coverage_statements: vec![CoverageStatement::Statement(bb, span, stmt_index)], + is_closure, + } + } + + pub fn for_terminator(span: Span, bcb: BasicCoverageBlock, bb: BasicBlock) -> Self { + Self { + span, + bcb, + coverage_statements: vec![CoverageStatement::Terminator(bb, span)], + is_closure: false, + } + } + + pub fn merge_from(&mut self, mut other: CoverageSpan) { + debug_assert!(self.is_mergeable(&other)); + self.span = self.span.to(other.span); + if other.is_closure { + self.is_closure = true; + } + self.coverage_statements.append(&mut other.coverage_statements); + } + + pub fn cutoff_statements_at(&mut self, cutoff_pos: BytePos) { + self.coverage_statements.retain(|covstmt| covstmt.span().hi() <= cutoff_pos); + if let Some(highest_covstmt) = + self.coverage_statements.iter().max_by_key(|covstmt| covstmt.span().hi()) + { + self.span = self.span.with_hi(highest_covstmt.span().hi()); + } + } + + #[inline] + pub fn is_mergeable(&self, other: &Self) -> bool { + self.is_in_same_bcb(other) && !(self.is_closure || other.is_closure) + } + + #[inline] + pub fn is_in_same_bcb(&self, other: &Self) -> bool { + self.bcb == other.bcb + } + + pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String { + format!( + "{}\n {}", + source_range_no_file(tcx, &self.span), + self.format_coverage_statements(tcx, mir_body).replace("\n", "\n "), + ) + } + + pub fn format_coverage_statements( + &self, + tcx: TyCtxt<'tcx>, + mir_body: &'a mir::Body<'tcx>, + ) -> String { + let mut sorted_coverage_statements = self.coverage_statements.clone(); + sorted_coverage_statements.sort_unstable_by_key(|covstmt| match *covstmt { + CoverageStatement::Statement(bb, _, index) => (bb, index), + CoverageStatement::Terminator(bb, _) => (bb, usize::MAX), + }); + sorted_coverage_statements + .iter() + .map(|covstmt| covstmt.format(tcx, mir_body)) + .collect::>() + .join("\n") + } +} + +/// Converts the initial set of `CoverageSpan`s (one per MIR `Statement` or `Terminator`) into a +/// minimal set of `CoverageSpan`s, using the BCB CFG to determine where it is safe and useful to: +/// +/// * Remove duplicate source code coverage regions +/// * Merge spans that represent continuous (both in source code and control flow), non-branching +/// execution +/// * Carve out (leave uncovered) any span that will be counted by another MIR (notably, closures) +pub struct CoverageSpans<'a, 'tcx> { + /// The MIR, used to look up `BasicBlockData`. + mir_body: &'a mir::Body<'tcx>, + + /// A `Span` covering the function body of the MIR (typically from left curly brace to right + /// curly brace). + body_span: Span, + + /// The BasicCoverageBlock Control Flow Graph (BCB CFG). + basic_coverage_blocks: &'a CoverageGraph, + + /// The BCB CFG's dominators tree, used to compute the dominance relationships, if any. + bcb_dominators: &'a Dominators, + + /// The initial set of `CoverageSpan`s, sorted by `Span` (`lo` and `hi`) and by relative + /// dominance between the `BasicCoverageBlock`s of equal `Span`s. + sorted_spans_iter: Option>, + + /// The current `CoverageSpan` to compare to its `prev`, to possibly merge, discard, force the + /// discard of the `prev` (and or `pending_dups`), or keep both (with `prev` moved to + /// `pending_dups`). If `curr` is not discarded or merged, it becomes `prev` for the next + /// iteration. + some_curr: Option, + + /// The original `span` for `curr`, in case the `curr` span is modified. + curr_original_span: Span, + + /// The CoverageSpan from a prior iteration; typically assigned from that iteration's `curr`. + /// If that `curr` was discarded, `prev` retains its value from the previous iteration. + some_prev: Option, + + /// Assigned from `curr_original_span` from the previous iteration. + prev_original_span: Span, + + /// One or more `CoverageSpan`s with the same `Span` but different `BasicCoverageBlock`s, and + /// no `BasicCoverageBlock` in this list dominates another `BasicCoverageBlock` in the list. + /// If a new `curr` span also fits this criteria (compared to an existing list of + /// `pending_dups`), that `curr` `CoverageSpan` moves to `prev` before possibly being added to + /// the `pending_dups` list, on the next iteration. As a result, if `prev` and `pending_dups` + /// have the same `Span`, the criteria for `pending_dups` holds for `prev` as well: a `prev` + /// with a matching `Span` does not dominate any `pending_dup` and no `pending_dup` dominates a + /// `prev` with a matching `Span`) + pending_dups: Vec, + + /// The final `CoverageSpan`s to add to the coverage map. A `Counter` or `Expression` + /// will also be injected into the MIR for each `CoverageSpan`. + refined_spans: Vec, +} + +impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { + pub(crate) fn generate_coverage_spans( + mir_body: &'a mir::Body<'tcx>, + body_span: Span, + basic_coverage_blocks: &'a CoverageGraph, + bcb_dominators: &'a Dominators, + ) -> Vec { + let mut coverage_spans = CoverageSpans { + mir_body, + body_span, + basic_coverage_blocks, + bcb_dominators, + sorted_spans_iter: None, + refined_spans: Vec::with_capacity(basic_coverage_blocks.num_nodes() * 2), + some_curr: None, + curr_original_span: Span::with_root_ctxt(BytePos(0), BytePos(0)), + some_prev: None, + prev_original_span: Span::with_root_ctxt(BytePos(0), BytePos(0)), + pending_dups: Vec::new(), + }; + + let sorted_spans = coverage_spans.mir_to_initial_sorted_coverage_spans(); + + coverage_spans.sorted_spans_iter = Some(sorted_spans.into_iter()); + coverage_spans.some_prev = coverage_spans.sorted_spans_iter.as_mut().unwrap().next(); + coverage_spans.prev_original_span = + coverage_spans.some_prev.as_ref().expect("at least one span").span; + + coverage_spans.to_refined_spans() + } + + /// Generate a minimal set of `CoverageSpan`s, each representing a contiguous code region to be + /// counted. + /// + /// The basic steps are: + /// + /// 1. Extract an initial set of spans from the `Statement`s and `Terminator`s of each + /// `BasicCoverageBlockData`. + /// 2. Sort the spans by span.lo() (starting position). Spans that start at the same position + /// are sorted with longer spans before shorter spans; and equal spans are sorted + /// (deterministically) based on "dominator" relationship (if any). + /// 3. Traverse the spans in sorted order to identify spans that can be dropped (for instance, + /// if another span or spans are already counting the same code region), or should be merged + /// into a broader combined span (because it represents a contiguous, non-branching, and + /// uninterrupted region of source code). + /// + /// Closures are exposed in their enclosing functions as `Assign` `Rvalue`s, and since + /// closures have their own MIR, their `Span` in their enclosing function should be left + /// "uncovered". + /// + /// Note the resulting vector of `CoverageSpan`s does may not be fully sorted (and does not need + /// to be). + fn mir_to_initial_sorted_coverage_spans(&self) -> Vec { + let mut initial_spans = Vec::::with_capacity(self.mir_body.num_nodes() * 2); + for (bcb, bcb_data) in self.basic_coverage_blocks.iter_enumerated() { + for coverage_span in self.bcb_to_initial_coverage_spans(bcb, bcb_data) { + initial_spans.push(coverage_span); + } + } + + if initial_spans.is_empty() { + // This can happen if, for example, the function is unreachable (contains only a + // `BasicBlock`(s) with an `Unreachable` terminator). + return initial_spans; + } + + initial_spans.sort_unstable_by(|a, b| { + if a.span.lo() == b.span.lo() { + if a.span.hi() == b.span.hi() { + if a.is_in_same_bcb(b) { + Some(Ordering::Equal) + } else { + // Sort equal spans by dominator relationship, in reverse order (so + // dominators always come after the dominated equal spans). When later + // comparing two spans in order, the first will either dominate the second, + // or they will have no dominator relationship. + self.bcb_dominators.rank_partial_cmp(b.bcb, a.bcb) + } + } else { + // Sort hi() in reverse order so shorter spans are attempted after longer spans. + // This guarantees that, if a `prev` span overlaps, and is not equal to, a + // `curr` span, the prev span either extends further left of the curr span, or + // they start at the same position and the prev span extends further right of + // the end of the curr span. + b.span.hi().partial_cmp(&a.span.hi()) + } + } else { + a.span.lo().partial_cmp(&b.span.lo()) + } + .unwrap() + }); + + initial_spans + } + + /// Iterate through the sorted `CoverageSpan`s, and return the refined list of merged and + /// de-duplicated `CoverageSpan`s. + fn to_refined_spans(mut self) -> Vec { + while self.next_coverage_span() { + if self.curr().is_mergeable(self.prev()) { + debug!(" same bcb (and neither is a closure), merge with prev={:?}", self.prev()); + let prev = self.take_prev(); + self.curr_mut().merge_from(prev); + // Note that curr.span may now differ from curr_original_span + } else if self.prev_ends_before_curr() { + debug!( + " different bcbs and disjoint spans, so keep curr for next iter, and add \ + prev={:?}", + self.prev() + ); + let prev = self.take_prev(); + self.refined_spans.push(prev); + } else if self.prev().is_closure { + // drop any equal or overlapping span (`curr`) and keep `prev` to test again in the + // next iter + debug!( + " curr overlaps a closure (prev). Drop curr and keep prev for next iter. \ + prev={:?}", + self.prev() + ); + self.discard_curr(); + } else if self.curr().is_closure { + self.carve_out_span_for_closure(); + } else if self.prev_original_span == self.curr().span { + // Note that this compares the new span to `prev_original_span`, which may not + // be the full `prev.span` (if merged during the previous iteration). + self.hold_pending_dups_unless_dominated(); + } else { + self.cutoff_prev_at_overlapping_curr(); + } + } + + debug!(" AT END, adding last prev={:?}", self.prev()); + let prev = self.take_prev(); + let CoverageSpans { + mir_body, basic_coverage_blocks, pending_dups, mut refined_spans, .. + } = self; + for dup in pending_dups { + debug!(" ...adding at least one pending dup={:?}", dup); + refined_spans.push(dup); + } + refined_spans.push(prev); + + // Remove `CoverageSpan`s with empty spans ONLY if the empty `CoverageSpan`s BCB also has at + // least one other non-empty `CoverageSpan`. + let mut has_coverage = BitSet::new_empty(basic_coverage_blocks.num_nodes()); + for covspan in &refined_spans { + if !covspan.span.is_empty() { + has_coverage.insert(covspan.bcb); + } + } + refined_spans.retain(|covspan| { + !(covspan.span.is_empty() + && is_goto(&basic_coverage_blocks[covspan.bcb].terminator(mir_body).kind) + && has_coverage.contains(covspan.bcb)) + }); + + // Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage + // regions for the current function leave room for the closure's own coverage regions + // (injected separately, from the closure's own MIR). + refined_spans.retain(|covspan| !covspan.is_closure); + refined_spans + } + + // Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of + // the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One `CoverageSpan` is generated + // for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will + // merge some `CoverageSpan`s, at which point a `CoverageSpan` may represent multiple + // `Statement`s and/or `Terminator`s.) + fn bcb_to_initial_coverage_spans( + &self, + bcb: BasicCoverageBlock, + bcb_data: &'a BasicCoverageBlockData, + ) -> Vec { + bcb_data + .basic_blocks + .iter() + .map(|bbref| { + let bb = *bbref; + let data = &self.mir_body[bb]; + data.statements + .iter() + .enumerate() + .filter_map(move |(index, statement)| { + filtered_statement_span(statement, self.body_span).map(|span| { + CoverageSpan::for_statement(statement, span, bcb, bb, index) + }) + }) + .chain( + filtered_terminator_span(data.terminator(), self.body_span) + .map(|span| CoverageSpan::for_terminator(span, bcb, bb)), + ) + }) + .flatten() + .collect() + } + + fn curr(&self) -> &CoverageSpan { + self.some_curr + .as_ref() + .unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_curr")) + } + + fn curr_mut(&mut self) -> &mut CoverageSpan { + self.some_curr + .as_mut() + .unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_curr")) + } + + fn prev(&self) -> &CoverageSpan { + self.some_prev + .as_ref() + .unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_prev")) + } + + fn prev_mut(&mut self) -> &mut CoverageSpan { + self.some_prev + .as_mut() + .unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_prev")) + } + + fn take_prev(&mut self) -> CoverageSpan { + self.some_prev.take().unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_prev")) + } + + /// If there are `pending_dups` but `prev` is not a matching dup (`prev.span` doesn't match the + /// `pending_dups` spans), then one of the following two things happened during the previous + /// iteration: + /// * the previous `curr` span (which is now `prev`) was not a duplicate of the pending_dups + /// (in which case there should be at least two spans in `pending_dups`); or + /// * the `span` of `prev` was modified by `curr_mut().merge_from(prev)` (in which case + /// `pending_dups` could have as few as one span) + /// In either case, no more spans will match the span of `pending_dups`, so + /// add the `pending_dups` if they don't overlap `curr`, and clear the list. + fn check_pending_dups(&mut self) { + if let Some(dup) = self.pending_dups.last() { + if dup.span != self.prev().span { + debug!( + " SAME spans, but pending_dups are NOT THE SAME, so BCBs matched on \ + previous iteration, or prev started a new disjoint span" + ); + if dup.span.hi() <= self.curr().span.lo() { + let pending_dups = self.pending_dups.split_off(0); + for dup in pending_dups.into_iter() { + debug!(" ...adding at least one pending={:?}", dup); + self.refined_spans.push(dup); + } + } else { + self.pending_dups.clear(); + } + } + } + } + + /// Advance `prev` to `curr` (if any), and `curr` to the next `CoverageSpan` in sorted order. + fn next_coverage_span(&mut self) -> bool { + if let Some(curr) = self.some_curr.take() { + self.some_prev = Some(curr); + self.prev_original_span = self.curr_original_span; + } + while let Some(curr) = self.sorted_spans_iter.as_mut().unwrap().next() { + debug!("FOR curr={:?}", curr); + if self.prev_starts_after_next(&curr) { + debug!( + " prev.span starts after curr.span, so curr will be dropped (skipping past \ + closure?); prev={:?}", + self.prev() + ); + } else { + // Save a copy of the original span for `curr` in case the `CoverageSpan` is changed + // by `self.curr_mut().merge_from(prev)`. + self.curr_original_span = curr.span; + self.some_curr.replace(curr); + self.check_pending_dups(); + return true; + } + } + false + } + + /// If called, then the next call to `next_coverage_span()` will *not* update `prev` with the + /// `curr` coverage span. + fn discard_curr(&mut self) { + self.some_curr = None; + } + + /// Returns true if the curr span should be skipped because prev has already advanced beyond the + /// end of curr. This can only happen if a prior iteration updated `prev` to skip past a region + /// of code, such as skipping past a closure. + fn prev_starts_after_next(&self, next_curr: &CoverageSpan) -> bool { + self.prev().span.lo() > next_curr.span.lo() + } + + /// Returns true if the curr span starts past the end of the prev span, which means they don't + /// overlap, so we now know the prev can be added to the refined coverage spans. + fn prev_ends_before_curr(&self) -> bool { + self.prev().span.hi() <= self.curr().span.lo() + } + + /// If `prev`s span extends left of the closure (`curr`), carve out the closure's + /// span from `prev`'s span. (The closure's coverage counters will be injected when + /// processing the closure's own MIR.) Add the portion of the span to the left of the + /// closure; and if the span extends to the right of the closure, update `prev` to + /// that portion of the span. For any `pending_dups`, repeat the same process. + fn carve_out_span_for_closure(&mut self) { + let curr_span = self.curr().span; + let left_cutoff = curr_span.lo(); + let right_cutoff = curr_span.hi(); + let has_pre_closure_span = self.prev().span.lo() < right_cutoff; + let has_post_closure_span = self.prev().span.hi() > right_cutoff; + let mut pending_dups = self.pending_dups.split_off(0); + if has_pre_closure_span { + let mut pre_closure = self.prev().clone(); + pre_closure.span = pre_closure.span.with_hi(left_cutoff); + debug!(" prev overlaps a closure. Adding span for pre_closure={:?}", pre_closure); + if !pending_dups.is_empty() { + for mut dup in pending_dups.iter().cloned() { + dup.span = dup.span.with_hi(left_cutoff); + debug!(" ...and at least one pre_closure dup={:?}", dup); + self.refined_spans.push(dup); + } + } + self.refined_spans.push(pre_closure); + } + if has_post_closure_span { + // Update prev.span to start after the closure (and discard curr) + self.prev_mut().span = self.prev().span.with_lo(right_cutoff); + self.prev_original_span = self.prev().span; + for dup in pending_dups.iter_mut() { + dup.span = dup.span.with_lo(right_cutoff); + } + self.pending_dups.append(&mut pending_dups); + self.discard_curr(); // since self.prev() was already updated + } else { + pending_dups.clear(); + } + } + + /// Called if `curr.span` equals `prev_original_span` (and potentially equal to all + /// `pending_dups` spans, if any); but keep in mind, `prev.span` may start at a `Span.lo()` that + /// is less than (further left of) `prev_original_span.lo()`. + /// + /// When two `CoverageSpan`s have the same `Span`, dominated spans can be discarded; but if + /// neither `CoverageSpan` dominates the other, both (or possibly more than two) are held, + /// until their disposition is determined. In this latter case, the `prev` dup is moved into + /// `pending_dups` so the new `curr` dup can be moved to `prev` for the next iteration. + fn hold_pending_dups_unless_dominated(&mut self) { + // Equal coverage spans are ordered by dominators before dominated (if any), so it should be + // impossible for `curr` to dominate any previous `CoverageSpan`. + debug_assert!(!self.span_bcb_is_dominated_by(self.prev(), self.curr())); + + let initial_pending_count = self.pending_dups.len(); + if initial_pending_count > 0 { + let mut pending_dups = self.pending_dups.split_off(0); + pending_dups.retain(|dup| !self.span_bcb_is_dominated_by(self.curr(), dup)); + self.pending_dups.append(&mut pending_dups); + if self.pending_dups.len() < initial_pending_count { + debug!( + " discarded {} of {} pending_dups that dominated curr", + initial_pending_count - self.pending_dups.len(), + initial_pending_count + ); + } + } + + if self.span_bcb_is_dominated_by(self.curr(), self.prev()) { + debug!( + " different bcbs but SAME spans, and prev dominates curr. Discard prev={:?}", + self.prev() + ); + self.cutoff_prev_at_overlapping_curr(); + // If one span dominates the other, assocate the span with the code from the dominated + // block only (`curr`), and discard the overlapping portion of the `prev` span. (Note + // that if `prev.span` is wider than `prev_original_span`, a `CoverageSpan` will still + // be created for `prev`s block, for the non-overlapping portion, left of `curr.span`.) + // + // For example: + // match somenum { + // x if x < 1 => { ... } + // }... + // + // The span for the first `x` is referenced by both the pattern block (every time it is + // evaluated) and the arm code (only when matched). The counter will be applied only to + // the dominated block. This allows coverage to track and highlight things like the + // assignment of `x` above, if the branch is matched, making `x` available to the arm + // code; and to track and highlight the question mark `?` "try" operator at the end of + // a function call returning a `Result`, so the `?` is covered when the function returns + // an `Err`, and not counted as covered if the function always returns `Ok`. + } else { + // Save `prev` in `pending_dups`. (`curr` will become `prev` in the next iteration.) + // If the `curr` CoverageSpan is later discarded, `pending_dups` can be discarded as + // well; but if `curr` is added to refined_spans, the `pending_dups` will also be added. + debug!( + " different bcbs but SAME spans, and neither dominates, so keep curr for \ + next iter, and, pending upcoming spans (unless overlapping) add prev={:?}", + self.prev() + ); + let prev = self.take_prev(); + self.pending_dups.push(prev); + } + } + + /// `curr` overlaps `prev`. If `prev`s span extends left of `curr`s span, keep _only_ + /// statements that end before `curr.lo()` (if any), and add the portion of the + /// combined span for those statements. Any other statements have overlapping spans + /// that can be ignored because `curr` and/or other upcoming statements/spans inside + /// the overlap area will produce their own counters. This disambiguation process + /// avoids injecting multiple counters for overlapping spans, and the potential for + /// double-counting. + fn cutoff_prev_at_overlapping_curr(&mut self) { + debug!( + " different bcbs, overlapping spans, so ignore/drop pending and only add prev \ + if it has statements that end before curr; prev={:?}", + self.prev() + ); + if self.pending_dups.is_empty() { + let curr_span = self.curr().span; + self.prev_mut().cutoff_statements_at(curr_span.lo()); + if self.prev().coverage_statements.is_empty() { + debug!(" ... no non-overlapping statements to add"); + } else { + debug!(" ... adding modified prev={:?}", self.prev()); + let prev = self.take_prev(); + self.refined_spans.push(prev); + } + } else { + // with `pending_dups`, `prev` cannot have any statements that don't overlap + self.pending_dups.clear(); + } + } + + fn span_bcb_is_dominated_by(&self, covspan: &CoverageSpan, dom_covspan: &CoverageSpan) -> bool { + self.bcb_dominators.is_dominated_by(covspan.bcb, dom_covspan.bcb) + } +} + +fn filtered_statement_span(statement: &'a Statement<'tcx>, body_span: Span) -> Option { + match statement.kind { + // These statements have spans that are often outside the scope of the executed source code + // for their parent `BasicBlock`. + StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + // Coverage should not be encountered, but don't inject coverage coverage + | StatementKind::Coverage(_) + // Ignore `Nop`s + | StatementKind::Nop => None, + + // FIXME(richkadel): Look into a possible issue assigning the span to a + // FakeReadCause::ForGuardBinding, in this example: + // match somenum { + // x if x < 1 => { ... } + // }... + // The BasicBlock within the match arm code included one of these statements, but the span + // for it covered the `1` in this source. The actual statements have nothing to do with that + // source span: + // FakeRead(ForGuardBinding, _4); + // where `_4` is: + // _4 = &_1; (at the span for the first `x`) + // and `_1` is the `Place` for `somenum`. + // + // The arm code BasicBlock already has its own assignment for `x` itself, `_3 = 1`, and I've + // decided it's reasonable for that span (even though outside the arm code) to be part of + // the counted coverage of the arm code execution, but I can't justify including the literal + // `1` in the arm code. I'm pretty sure that, if the `FakeRead(ForGuardBinding)` has a + // purpose in codegen, it's probably in the right BasicBlock, but if so, the `Statement`s + // `source_info.span` can't be right. + // + // Consider correcting the span assignment, assuming there is a better solution, and see if + // the following pattern can be removed here: + StatementKind::FakeRead(cause, _) if cause == FakeReadCause::ForGuardBinding => None, + + // Retain spans from all other statements + StatementKind::FakeRead(_, _) // Not including `ForGuardBinding` + | StatementKind::Assign(_) + | StatementKind::SetDiscriminant { .. } + | StatementKind::LlvmInlineAsm(_) + | StatementKind::Retag(_, _) + | StatementKind::AscribeUserType(_, _) => { + Some(function_source_span(statement.source_info.span, body_span)) + } + } +} + +fn filtered_terminator_span(terminator: &'a Terminator<'tcx>, body_span: Span) -> Option { + match terminator.kind { + // These terminators have spans that don't positively contribute to computing a reasonable + // span of actually executed source code. (For example, SwitchInt terminators extracted from + // an `if condition { block }` has a span that includes the executed block, if true, + // but for coverage, the code region executed, up to *and* through the SwitchInt, + // actually stops before the if's block.) + TerminatorKind::Unreachable // Unreachable blocks are not connected to the MIR CFG + | TerminatorKind::Assert { .. } + | TerminatorKind::Drop { .. } + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::SwitchInt { .. } + // For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`. + // FIXME(richkadel): Note that `Goto` was moved to it's own match arm, for the reasons + // described below. Add tests to confirm whether or not similar cases also apply to + // `FalseEdge`. + | TerminatorKind::FalseEdge { .. } => None, + + // FIXME(richkadel): Note that `Goto` was initially filtered out (by returning `None`, as + // with the `TerminatorKind`s above) because its `Span` was way to broad to be beneficial, + // and, at the time, `Goto` didn't seem to provide any additional contributions to the + // coverage analysis. Upon further review, `Goto` terminated blocks do appear to benefit + // the coverage analysis, and the BCB CFG. To overcome the issues with the `Spans`, the + // coverage algorithms--and the final coverage map generation--include some exceptional + // behaviors. + // + // `Goto`s are often the targets of `SwitchInt` branches, and certain important + // optimizations to replace some `Counter`s with `Expression`s require a separate + // `BasicCoverageBlock` for each branch, to support the `Counter`, when needed. + // + // Also, some test cases showed that `Goto` terminators, and to some degree their `Span`s, + // provided useful context for coverage, such as to count and show when `if` blocks + // _without_ `else` blocks execute the `false` case (counting when the body of the `if` + // was _not_ taken). In these cases, the `Goto` span is ultimately given a `CoverageSpan` + // of 1 character, at the end of it's original `Span`. + // + // However, in other cases, a visible `CoverageSpan` is not wanted, but the `Goto` + // block must still be counted (for example, to contribute its count to an `Expression` + // that reports the execution count for some other block). In these cases, the code region + // is set to `None`. + TerminatorKind::Goto { .. } => { + Some(function_source_span(terminator.source_info.span.shrink_to_hi(), body_span)) + } + + // Retain spans from all other terminators + TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Call { .. } + | TerminatorKind::Yield { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::InlineAsm { .. } => { + Some(function_source_span(terminator.source_info.span, body_span)) + } + } +} + +#[inline(always)] +fn function_source_span(span: Span, body_span: Span) -> Span { + let span = original_sp(span, body_span).with_ctxt(SyntaxContext::root()); + if body_span.contains(span) { span } else { body_span } +} + +#[inline(always)] +fn is_goto(term_kind: &TerminatorKind<'tcx>) -> bool { + match term_kind { + TerminatorKind::Goto { .. } => true, + _ => false, + } +} diff --git a/compiler/rustc_mir/src/transform/instrument_coverage/mod.rs b/compiler/rustc_mir/src/transform/instrument_coverage/mod.rs deleted file mode 100644 index 11f20f143d0fa..0000000000000 --- a/compiler/rustc_mir/src/transform/instrument_coverage/mod.rs +++ /dev/null @@ -1,2931 +0,0 @@ -use crate::transform::MirPass; -use crate::util::generic_graphviz::GraphvizWriter; -use crate::util::pretty; -use crate::util::spanview::{self, SpanViewable}; - -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::graph::dominators::{self, Dominators}; -use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode}; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::Lrc; -use rustc_index::bit_set::BitSet; -use rustc_index::vec::{Idx, IndexVec}; -use rustc_middle::hir; -use rustc_middle::hir::map::blocks::FnLikeNode; -use rustc_middle::ich::StableHashingContext; -use rustc_middle::mir; -use rustc_middle::mir::coverage::*; -use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{ - AggregateKind, BasicBlock, BasicBlockData, Coverage, CoverageInfo, FakeReadCause, Location, - Rvalue, SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, -}; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::TyCtxt; -use rustc_span::def_id::DefId; -use rustc_span::source_map::original_sp; -use rustc_span::{BytePos, CharPos, Pos, SourceFile, Span, Symbol, SyntaxContext}; - -use std::cmp::Ordering; -use std::lazy::SyncOnceCell; -use std::ops::{Index, IndexMut}; - -const ID_SEPARATOR: &str = ","; - -const RUSTC_COVERAGE_DEBUG_OPTIONS: &str = "RUSTC_COVERAGE_DEBUG_OPTIONS"; - -/// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected -/// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen -/// to construct the coverage map. -pub struct InstrumentCoverage; - -#[derive(Debug, Clone)] -struct DebugOptions { - allow_unused_expressions: bool, - simplify_expressions: bool, - counter_format: ExpressionFormat, -} - -impl DebugOptions { - fn new() -> Self { - let mut allow_unused_expressions = true; - let mut simplify_expressions = false; - let mut counter_format = ExpressionFormat::default(); - - if let Ok(env_debug_options) = std::env::var(RUSTC_COVERAGE_DEBUG_OPTIONS) { - for setting_str in env_debug_options.replace(" ", "").replace("-", "_").split(",") { - let mut setting = setting_str.splitn(2, "="); - match setting.next() { - Some(option) if option == "allow_unused_expressions" => { - allow_unused_expressions = bool_option_val(option, setting.next()); - debug!( - "{} env option `allow_unused_expressions` is set to {}", - RUSTC_COVERAGE_DEBUG_OPTIONS, allow_unused_expressions - ); - } - Some(option) if option == "simplify_expressions" => { - simplify_expressions = bool_option_val(option, setting.next()); - debug!( - "{} env option `simplify_expressions` is set to {}", - RUSTC_COVERAGE_DEBUG_OPTIONS, simplify_expressions - ); - } - Some(option) if option == "counter_format" => { - if let Some(strval) = setting.next() { - counter_format = counter_format_option_val(strval); - debug!( - "{} env option `counter_format` is set to {:?}", - RUSTC_COVERAGE_DEBUG_OPTIONS, counter_format - ); - } else { - bug!( - "`{}` option in environment variable {} requires one or more \ - plus-separated choices (a non-empty subset of \ - `id+block+operation`)", - option, - RUSTC_COVERAGE_DEBUG_OPTIONS - ); - } - } - Some("") => {} - Some(invalid) => bug!( - "Unsupported setting `{}` in environment variable {}", - invalid, - RUSTC_COVERAGE_DEBUG_OPTIONS - ), - None => {} - } - } - } - - Self { allow_unused_expressions, simplify_expressions, counter_format } - } -} - -fn debug_options<'a>() -> &'a DebugOptions { - static DEBUG_OPTIONS: SyncOnceCell = SyncOnceCell::new(); - - &DEBUG_OPTIONS.get_or_init(|| DebugOptions::new()) -} - -fn bool_option_val(option: &str, some_strval: Option<&str>) -> bool { - if let Some(val) = some_strval { - if vec!["yes", "y", "on", "true"].contains(&val) { - true - } else if vec!["no", "n", "off", "false"].contains(&val) { - false - } else { - bug!( - "Unsupported value `{}` for option `{}` in environment variable {}", - option, - val, - RUSTC_COVERAGE_DEBUG_OPTIONS - ) - } - } else { - true - } -} - -fn counter_format_option_val(strval: &str) -> ExpressionFormat { - let mut counter_format = ExpressionFormat { id: false, block: false, operation: false }; - let components = strval.splitn(3, "+"); - for component in components { - match component { - "id" => counter_format.id = true, - "block" => counter_format.block = true, - "operation" => counter_format.operation = true, - _ => bug!( - "Unsupported counter_format choice `{}` in environment variable {}", - component, - RUSTC_COVERAGE_DEBUG_OPTIONS - ), - } - } - counter_format -} - -#[derive(Debug, Clone)] -struct ExpressionFormat { - id: bool, - block: bool, - operation: bool, -} - -impl Default for ExpressionFormat { - fn default() -> Self { - Self { id: false, block: true, operation: true } - } -} - -#[derive(Debug)] -struct DebugCounter { - counter_kind: CoverageKind, - some_block_label: Option, -} - -impl DebugCounter { - fn new(counter_kind: CoverageKind, some_block_label: Option) -> Self { - Self { counter_kind, some_block_label } - } -} - -struct DebugCounters { - some_counters: Option>, -} - -impl DebugCounters { - pub fn new() -> Self { - Self { some_counters: None } - } - - pub fn enable(&mut self) { - self.some_counters.replace(FxHashMap::default()); - } - - pub fn is_enabled(&mut self) -> bool { - self.some_counters.is_some() - } - - pub fn add_counter(&mut self, counter_kind: &CoverageKind, some_block_label: Option) { - if let Some(counters) = &mut self.some_counters { - let id: ExpressionOperandId = match *counter_kind { - CoverageKind::Counter { id, .. } => id.into(), - CoverageKind::Expression { id, .. } => id.into(), - _ => bug!( - "the given `CoverageKind` is not an counter or expression: {:?}", - counter_kind - ), - }; - counters - .insert(id.into(), DebugCounter::new(counter_kind.clone(), some_block_label)) - .expect_none( - "attempt to add the same counter_kind to DebugCounters more than once", - ); - } - } - - pub fn some_block_label(&self, operand: ExpressionOperandId) -> Option<&String> { - self.some_counters.as_ref().map_or(None, |counters| { - counters - .get(&operand) - .map_or(None, |debug_counter| debug_counter.some_block_label.as_ref()) - }) - } - - pub fn format_counter(&self, counter_kind: &CoverageKind) -> String { - match *counter_kind { - CoverageKind::Counter { .. } => { - format!("Counter({})", self.format_counter_kind(counter_kind)) - } - CoverageKind::Expression { .. } => { - format!("Expression({})", self.format_counter_kind(counter_kind)) - } - CoverageKind::Unreachable { .. } => "Unreachable".to_owned(), - } - } - - fn format_counter_kind(&self, counter_kind: &CoverageKind) -> String { - let counter_format = &debug_options().counter_format; - if let CoverageKind::Expression { id, lhs, op, rhs } = *counter_kind { - if counter_format.operation { - return format!( - "{}{} {} {}", - if counter_format.id || self.some_counters.is_none() { - format!("#{} = ", id.index()) - } else { - String::new() - }, - self.format_operand(lhs), - if op == Op::Add { "+" } else { "-" }, - self.format_operand(rhs), - ); - } - } - - let id: ExpressionOperandId = match *counter_kind { - CoverageKind::Counter { id, .. } => id.into(), - CoverageKind::Expression { id, .. } => id.into(), - _ => { - bug!("the given `CoverageKind` is not an counter or expression: {:?}", counter_kind) - } - }; - if self.some_counters.is_some() && (counter_format.block || !counter_format.id) { - let counters = self.some_counters.as_ref().unwrap(); - if let Some(DebugCounter { some_block_label: Some(block_label), .. }) = - counters.get(&id.into()) - { - return if counter_format.id { - format!("{}#{}", block_label, id.index()) - } else { - format!("{}", block_label) - }; - } - } - format!("#{}", id.index()) - } - - fn format_operand(&self, operand: ExpressionOperandId) -> String { - if operand.index() == 0 { - return String::from("0"); - } - if let Some(counters) = &self.some_counters { - if let Some(DebugCounter { counter_kind, some_block_label }) = counters.get(&operand) { - if let CoverageKind::Expression { .. } = counter_kind { - if let Some(block_label) = some_block_label { - if debug_options().counter_format.block { - return format!( - "{}:({})", - block_label, - self.format_counter_kind(counter_kind) - ); - } - } - return format!("({})", self.format_counter_kind(counter_kind)); - } - return format!("{}", self.format_counter_kind(counter_kind)); - } - } - format!("#{}", operand.index().to_string()) - } -} - -/// The `query` provider for `CoverageInfo`, requested by `codegen_coverage()` (to inject each -/// counter) and `FunctionCoverage::new()` (to extract the coverage map metadata from the MIR). -pub(crate) fn provide(providers: &mut Providers) { - providers.coverageinfo = |tcx, def_id| coverageinfo_from_mir(tcx, def_id); -} - -/// The `num_counters` argument to `llvm.instrprof.increment` is the max counter_id + 1, or in -/// other words, the number of counter value references injected into the MIR (plus 1 for the -/// reserved `ZERO` counter, which uses counter ID `0` when included in an expression). Injected -/// counters have a counter ID from `1..num_counters-1`. -/// -/// `num_expressions` is the number of counter expressions added to the MIR body. -/// -/// Both `num_counters` and `num_expressions` are used to initialize new vectors, during backend -/// code generate, to lookup counters and expressions by simple u32 indexes. -/// -/// MIR optimization may split and duplicate some BasicBlock sequences, or optimize out some code -/// including injected counters. (It is OK if some counters are optimized out, but those counters -/// are still included in the total `num_counters` or `num_expressions`.) Simply counting the -/// calls may not work; but computing the number of counters or expressions by adding `1` to the -/// highest ID (for a given instrumented function) is valid. -/// -/// This visitor runs twice, first with `add_missing_operands` set to `false`, to find the maximum -/// counter ID and maximum expression ID based on their enum variant `id` fields; then, as a -/// safeguard, with `add_missing_operands` set to `true`, to find any other counter or expression -/// IDs referenced by expression operands, if not already seen. -/// -/// Ideally, every expression operand in the MIR will have a corresponding Counter or Expression, -/// but since current or future MIR optimizations can theoretically optimize out segments of a -/// MIR, it may not be possible to guarantee this, so the second pass ensures the `CoverageInfo` -/// counts include all referenced IDs. -struct CoverageVisitor { - info: CoverageInfo, - add_missing_operands: bool, -} - -impl CoverageVisitor { - // If an expression operand is encountered with an ID outside the range of known counters and - // expressions, the only way to determine if the ID is a counter ID or an expression ID is to - // assume a maximum possible counter ID value. - const MAX_COUNTER_GUARD: u32 = (u32::MAX / 2) + 1; - - #[inline(always)] - fn update_num_counters(&mut self, counter_id: u32) { - self.info.num_counters = std::cmp::max(self.info.num_counters, counter_id + 1); - } - - #[inline(always)] - fn update_num_expressions(&mut self, expression_id: u32) { - let expression_index = u32::MAX - expression_id; - self.info.num_expressions = std::cmp::max(self.info.num_expressions, expression_index + 1); - } - - fn update_from_expression_operand(&mut self, operand_id: u32) { - if operand_id >= self.info.num_counters { - let operand_as_expression_index = u32::MAX - operand_id; - if operand_as_expression_index >= self.info.num_expressions { - if operand_id <= Self::MAX_COUNTER_GUARD { - self.update_num_counters(operand_id) - } else { - self.update_num_expressions(operand_id) - } - } - } - } -} - -impl Visitor<'_> for CoverageVisitor { - fn visit_coverage(&mut self, coverage: &Coverage, _location: Location) { - if self.add_missing_operands { - match coverage.kind { - CoverageKind::Expression { lhs, rhs, .. } => { - self.update_from_expression_operand(u32::from(lhs)); - self.update_from_expression_operand(u32::from(rhs)); - } - _ => {} - } - } else { - match coverage.kind { - CoverageKind::Counter { id, .. } => { - self.update_num_counters(u32::from(id)); - } - CoverageKind::Expression { id, .. } => { - self.update_num_expressions(u32::from(id)); - } - _ => {} - } - } - } -} - -fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo { - let mir_body = tcx.optimized_mir(def_id); - - let mut coverage_visitor = CoverageVisitor { - info: CoverageInfo { num_counters: 0, num_expressions: 0 }, - add_missing_operands: false, - }; - - coverage_visitor.visit_body(mir_body); - - coverage_visitor.add_missing_operands = true; - coverage_visitor.visit_body(mir_body); - - coverage_visitor.info -} - -impl<'tcx> MirPass<'tcx> for InstrumentCoverage { - fn run_pass(&self, tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) { - let mir_source = mir_body.source; - - // If the InstrumentCoverage pass is called on promoted MIRs, skip them. - // See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601 - if mir_source.promoted.is_some() { - trace!( - "InstrumentCoverage skipped for {:?} (already promoted for Miri evaluation)", - mir_source.def_id() - ); - return; - } - - let hir_id = tcx.hir().local_def_id_to_hir_id(mir_source.def_id().expect_local()); - let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some(); - - // Only instrument functions, methods, and closures (not constants since they are evaluated - // at compile time by Miri). - // FIXME(#73156): Handle source code coverage in const eval, but note, if and when const - // expressions get coverage spans, we will probably have to "carve out" space for const - // expressions from coverage spans in enclosing MIR's, like we do for closures. (That might - // be tricky if const expressions have no corresponding statements in the enclosing MIR. - // Closures are carved out by their initial `Assign` statement.) - if !is_fn_like { - trace!("InstrumentCoverage skipped for {:?} (not an FnLikeNode)", mir_source.def_id()); - return; - } - // FIXME(richkadel): By comparison, the MIR pass `ConstProp` includes associated constants, - // with functions, methods, and closures. I assume Miri is used for associated constants as - // well. If not, we may need to include them here too. - - trace!("InstrumentCoverage starting for {:?}", mir_source.def_id()); - Instrumentor::new(&self.name(), tcx, mir_body).inject_counters(); - trace!("InstrumentCoverage starting for {:?}", mir_source.def_id()); - } -} - -/// A BasicCoverageBlockData (BCB) represents the maximal-length sequence of MIR BasicBlocks without -/// conditional branches, and form a new, simplified, coverage-specific Control Flow Graph, without -/// altering the original MIR CFG. -/// -/// Note that running the MIR `SimplifyCfg` transform is not sufficient (and therefore not -/// necessary). The BCB-based CFG is a more aggressive simplification. For example: -/// -/// * The BCB CFG ignores (trims) branches not relevant to coverage, such as unwind-related code, -/// that is injected by the Rust compiler but has no physical source code to count. This also -/// means a BasicBlock with a `Call` terminator can be merged into its primary successor target -/// block, in the same BCB. -/// * Some BasicBlock terminators support Rust-specific concerns--like borrow-checking--that are -/// not relevant to coverage analysis. `FalseUnwind`, for example, can be treated the same as -/// a `Goto`, and merged with its successor into the same BCB. -/// -/// Each BCB with at least one computed `CoverageSpan` will have no more than one `Counter`. -/// In some cases, a BCB's execution count can be computed by `Expression`. Additional -/// disjoint `CoverageSpan`s in a BCB can also be counted by `Expression` (by adding `ZERO` -/// to the BCB's primary counter or expression). -/// -/// The BCB CFG is critical to simplifying the coverage analysis by ensuring graph path-based -/// queries (`is_dominated_by()`, `predecessors`, `successors`, etc.) have branch (control flow) -/// significance. -#[derive(Debug, Clone)] -struct BasicCoverageBlockData { - basic_blocks: Vec, - counter_kind: Option, - edge_counter_from_bcbs: Option>, -} - -impl BasicCoverageBlockData { - pub fn from(basic_blocks: Vec) -> Self { - assert!(basic_blocks.len() > 0); - Self { basic_blocks, counter_kind: None, edge_counter_from_bcbs: None } - } - - #[inline(always)] - pub fn basic_blocks(&self) -> std::slice::Iter<'_, BasicBlock> { - self.basic_blocks.iter() - } - - #[inline(always)] - pub fn leader_bb(&self) -> BasicBlock { - self.basic_blocks[0] - } - - #[inline(always)] - pub fn last_bb(&self) -> BasicBlock { - *self.basic_blocks.last().unwrap() - } - - #[inline(always)] - pub fn terminator<'a, 'tcx>(&self, mir_body: &'a mir::Body<'tcx>) -> &'a Terminator<'tcx> { - &mir_body[self.last_bb()].terminator() - } - - #[inline(always)] - pub fn set_counter(&mut self, counter_kind: CoverageKind) -> ExpressionOperandId { - debug_assert!( - self.edge_counter_from_bcbs.is_none() || counter_kind.is_expression(), - "attempt to add a `Counter` to a BCB target with existing incoming edge counters" - ); - let operand = counter_kind.as_operand_id(); - self.counter_kind - .replace(counter_kind) - .expect_none("attempt to set a BasicCoverageBlock coverage counter more than once"); - operand - } - - #[inline(always)] - pub fn counter(&self) -> Option<&CoverageKind> { - self.counter_kind.as_ref() - } - - #[inline(always)] - pub fn take_counter(&mut self) -> Option { - self.counter_kind.take() - } - - #[inline(always)] - pub fn set_edge_counter_from( - &mut self, - from_bcb: BasicCoverageBlock, - counter_kind: CoverageKind, - ) -> ExpressionOperandId { - debug_assert!( - self.counter_kind.as_ref().map_or(true, |c| c.is_expression()), - "attempt to add an incoming edge counter from {:?} when the target BCB already has a \ - `Counter`", - from_bcb - ); - let operand = counter_kind.as_operand_id(); - self.edge_counter_from_bcbs - .get_or_insert_with(|| FxHashMap::default()) - .insert(from_bcb, counter_kind) - .expect_none("attempt to set an edge counter more than once"); - operand - } - - #[inline(always)] - pub fn edge_counter_from(&self, from_bcb: BasicCoverageBlock) -> Option<&CoverageKind> { - if let Some(edge_counter_from_bcbs) = &self.edge_counter_from_bcbs { - edge_counter_from_bcbs.get(&from_bcb) - } else { - None - } - } - - #[inline(always)] - pub fn take_edge_counters( - &mut self, - ) -> Option> { - self.edge_counter_from_bcbs.take().map_or(None, |m| Some(m.into_iter())) - } - - pub fn id(&self) -> String { - format!( - "@{}", - self.basic_blocks - .iter() - .map(|bb| bb.index().to_string()) - .collect::>() - .join(ID_SEPARATOR) - ) - } - - pub fn to_string_sections( - &self, - tcx: TyCtxt<'tcx>, - mir_body: &mir::Body<'tcx>, - debug_counters: &DebugCounters, - some_coverage_spans_with_counters: Option<&Vec<(CoverageSpan, CoverageKind)>>, - some_dependency_counters: Option<&Vec>, - some_intermediate_expressions: Option<&Vec>, - ) -> Vec { - let len = self.basic_blocks.len(); - let mut sections = Vec::new(); - if let Some(collect_intermediate_expressions) = some_intermediate_expressions { - sections.push( - collect_intermediate_expressions - .iter() - .map(|expression| { - format!("Intermediate {}", debug_counters.format_counter(expression)) - }) - .collect::>() - .join("\n"), - ); - } - if let Some(coverage_spans_with_counters) = some_coverage_spans_with_counters { - sections.push( - coverage_spans_with_counters - .iter() - .map(|(covspan, counter)| { - format!( - "{} at {}", - debug_counters.format_counter(counter), - covspan.format(tcx, mir_body) - ) - }) - .collect::>() - .join("\n"), - ); - } - if let Some(dependency_counters) = some_dependency_counters { - sections.push(format!( - "Non-coverage counters:\n {}", - dependency_counters - .iter() - .map(|counter| debug_counters.format_counter(counter)) - .collect::>() - .join(" \n"), - )); - } - if let Some(counter_kind) = &self.counter_kind { - sections.push(format!("{:?}", counter_kind)); - } - let non_term_blocks = self.basic_blocks[0..len - 1] - .iter() - .map(|&bb| format!("{:?}: {}", bb, term_type(&mir_body[bb].terminator().kind))) - .collect::>(); - if non_term_blocks.len() > 0 { - sections.push(non_term_blocks.join("\n")); - } - sections.push(format!( - "{:?}: {}", - self.basic_blocks.last().unwrap(), - term_type(&self.terminator(mir_body).kind) - )); - sections - } -} - -rustc_index::newtype_index! { - /// A node in the [control-flow graph][CFG] of BasicCoverageBlocks. - pub struct BasicCoverageBlock { - DEBUG_FORMAT = "bcb{}", - } -} - -struct BasicCoverageBlocks { - bcbs: IndexVec, - bb_to_bcb: IndexVec>, - successors: IndexVec>, - predecessors: IndexVec, -} - -impl BasicCoverageBlocks { - pub fn from_mir(mir_body: &mir::Body<'tcx>) -> Self { - let (bcbs, bb_to_bcb) = Self::compute_basic_coverage_blocks(mir_body); - - // Pre-transform MIR `BasicBlock` successors and predecessors into the BasicCoverageBlock - // equivalents. Note that since the BasicCoverageBlock graph has been fully simplified, the - // each predecessor of a BCB leader_bb should be in a unique BCB, and each successor of a - // BCB last_bb should bin in its own unique BCB. Therefore, collecting the BCBs using - // `bb_to_bcb` should work without requiring a deduplication step. - - let successors = IndexVec::from_fn_n( - |bcb| { - let bcb_data = &bcbs[bcb]; - let bcb_successors = - bcb_filtered_successors(&mir_body, &bcb_data.terminator(mir_body).kind) - .filter_map(|&successor_bb| bb_to_bcb[successor_bb]) - .collect::>(); - debug_assert!({ - let mut sorted = bcb_successors.clone(); - sorted.sort_unstable(); - let initial_len = sorted.len(); - sorted.dedup(); - sorted.len() == initial_len - }); - bcb_successors - }, - bcbs.len(), - ); - - let mut predecessors = IndexVec::from_elem_n(Vec::new(), bcbs.len()); - for (bcb, bcb_successors) in successors.iter_enumerated() { - for &successor in bcb_successors { - predecessors[successor].push(bcb); - } - } - - Self { bcbs, bb_to_bcb, successors, predecessors } - } - - fn compute_basic_coverage_blocks( - mir_body: &mir::Body<'tcx>, - ) -> ( - IndexVec, - IndexVec>, - ) { - let num_basic_blocks = mir_body.num_nodes(); - let mut bcbs = IndexVec::with_capacity(num_basic_blocks); - let mut bb_to_bcb = IndexVec::from_elem_n(None, num_basic_blocks); - - // Walk the MIR CFG using a Preorder traversal, which starts from `START_BLOCK` and follows - // each block terminator's `successors()`. Coverage spans must map to actual source code, - // so compiler generated blocks and paths can be ignored. To that end, the CFG traversal - // intentionally omits unwind paths. - let mir_cfg_without_unwind = ShortCircuitPreorder::new(&mir_body, bcb_filtered_successors); - - let mut basic_blocks = Vec::new(); - for (bb, data) in mir_cfg_without_unwind { - if let Some(last) = basic_blocks.last() { - let predecessors = &mir_body.predecessors()[bb]; - if predecessors.len() > 1 || !predecessors.contains(last) { - // The `bb` has more than one _incoming_ edge, and should start its own - // `BasicCoverageBlockData`. (Note, the `basic_blocks` vector does not yet - // include `bb`; it contains a sequence of one or more sequential basic_blocks - // with no intermediate branches in or out. Save these as a new - // `BasicCoverageBlockData` before starting the new one.) - Self::add_basic_coverage_block( - &mut bcbs, - &mut bb_to_bcb, - basic_blocks.split_off(0), - ); - debug!( - " because {}", - if predecessors.len() > 1 { - "predecessors.len() > 1".to_owned() - } else { - format!("bb {} is not in precessors: {:?}", bb.index(), predecessors) - } - ); - } - } - basic_blocks.push(bb); - - let term = data.terminator(); - - match term.kind { - TerminatorKind::Return { .. } - // FIXME(richkadel): Add test(s) for `Abort` coverage. - | TerminatorKind::Abort - // FIXME(richkadel): Add test(s) for `Assert` coverage. - // Should `Assert` be handled like `FalseUnwind` instead? Since we filter out unwind - // branches when creating the BCB CFG, aren't `Assert`s (without unwinds) just like - // `FalseUnwinds` (which are kind of like `Goto`s)? - | TerminatorKind::Assert { .. } - // FIXME(richkadel): Add test(s) for `Yield` coverage, and confirm coverage is - // sensible for code using the `yield` keyword. - | TerminatorKind::Yield { .. } - // FIXME(richkadel): Also add coverage tests using async/await, and threading. - - | TerminatorKind::SwitchInt { .. } => { - // The `bb` has more than one _outgoing_ edge, or exits the function. Save the - // current sequence of `basic_blocks` gathered to this point, as a new - // `BasicCoverageBlockData`. - Self::add_basic_coverage_block( - &mut bcbs, - &mut bb_to_bcb, - basic_blocks.split_off(0), - ); - debug!(" because term.kind = {:?}", term.kind); - // Note that this condition is based on `TerminatorKind`, even though it - // theoretically boils down to `successors().len() != 1`; that is, either zero - // (e.g., `Return`, `Abort`) or multiple successors (e.g., `SwitchInt`), but - // since the BCB CFG ignores things like unwind branches (which exist in the - // `Terminator`s `successors()` list) checking the number of successors won't - // work. - } - TerminatorKind::Goto { .. } - | TerminatorKind::Resume - | TerminatorKind::Unreachable - | TerminatorKind::Drop { .. } - | TerminatorKind::DropAndReplace { .. } - | TerminatorKind::Call { .. } - | TerminatorKind::GeneratorDrop - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::InlineAsm { .. } => {} - } - } - - if !basic_blocks.is_empty() { - // process any remaining basic_blocks into a final `BasicCoverageBlockData` - Self::add_basic_coverage_block(&mut bcbs, &mut bb_to_bcb, basic_blocks.split_off(0)); - debug!(" because the end of the MIR CFG was reached while traversing"); - } - - (bcbs, bb_to_bcb) - } - - fn add_basic_coverage_block( - bcbs: &mut IndexVec, - bb_to_bcb: &mut IndexVec>, - basic_blocks: Vec, - ) { - let bcb = BasicCoverageBlock::from_usize(bcbs.len()); - for &bb in basic_blocks.iter() { - bb_to_bcb[bb] = Some(bcb); - } - let bcb_data = BasicCoverageBlockData::from(basic_blocks); - debug!("adding bcb{}: {:?}", bcb.index(), bcb_data); - bcbs.push(bcb_data); - } - - #[inline(always)] - pub fn iter_enumerated( - &self, - ) -> impl Iterator { - self.bcbs.iter_enumerated() - } - - #[inline(always)] - pub fn iter_enumerated_mut( - &mut self, - ) -> impl Iterator { - self.bcbs.iter_enumerated_mut() - } - - #[inline(always)] - pub fn bcb_from_bb(&self, bb: BasicBlock) -> Option { - if bb.index() < self.bb_to_bcb.len() { self.bb_to_bcb[bb] } else { None } - } - - #[inline(always)] - pub fn compute_bcb_dominators(&self) -> Dominators { - dominators::dominators(self) - } -} - -impl Index for BasicCoverageBlocks { - type Output = BasicCoverageBlockData; - - #[inline] - fn index(&self, index: BasicCoverageBlock) -> &BasicCoverageBlockData { - &self.bcbs[index] - } -} - -impl IndexMut for BasicCoverageBlocks { - #[inline] - fn index_mut(&mut self, index: BasicCoverageBlock) -> &mut BasicCoverageBlockData { - &mut self.bcbs[index] - } -} - -impl graph::DirectedGraph for BasicCoverageBlocks { - type Node = BasicCoverageBlock; -} - -impl graph::WithNumNodes for BasicCoverageBlocks { - #[inline] - fn num_nodes(&self) -> usize { - self.bcbs.len() - } -} - -impl graph::WithStartNode for BasicCoverageBlocks { - #[inline] - fn start_node(&self) -> Self::Node { - self.bcb_from_bb(mir::START_BLOCK) - .expect("mir::START_BLOCK should be in a BasicCoverageBlock") - } -} - -type BcbSuccessors<'graph> = std::slice::Iter<'graph, BasicCoverageBlock>; - -impl graph::WithSuccessors for BasicCoverageBlocks { - #[inline] - fn successors(&self, node: Self::Node) -> >::Iter { - self.successors[node].iter().cloned() - } -} - -impl<'graph> graph::GraphSuccessors<'graph> for BasicCoverageBlocks { - type Item = BasicCoverageBlock; - type Iter = std::iter::Cloned>; -} - -type BcbPredecessors = Vec; - -impl graph::GraphPredecessors<'graph> for BasicCoverageBlocks { - type Item = BasicCoverageBlock; - type Iter = std::vec::IntoIter; -} - -impl graph::WithPredecessors for BasicCoverageBlocks { - #[inline] - fn predecessors(&self, node: Self::Node) -> >::Iter { - self.predecessors[node].clone().into_iter() - } -} - -#[derive(Debug, Copy, Clone)] -enum CoverageStatement { - Statement(BasicBlock, Span, usize), - Terminator(BasicBlock, Span), -} - -impl CoverageStatement { - pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String { - match *self { - Self::Statement(bb, span, stmt_index) => { - let stmt = &mir_body[bb].statements[stmt_index]; - format!( - "{}: @{}[{}]: {:?}", - spanview::source_range_no_file(tcx, &span), - bb.index(), - stmt_index, - stmt - ) - } - Self::Terminator(bb, span) => { - let term = mir_body[bb].terminator(); - format!( - "{}: @{}.{}: {:?}", - spanview::source_range_no_file(tcx, &span), - bb.index(), - term_type(&term.kind), - term.kind - ) - } - } - } - - pub fn span(&self) -> &Span { - match self { - Self::Statement(_, span, _) | Self::Terminator(_, span) => span, - } - } -} - -fn term_type(kind: &TerminatorKind<'tcx>) -> &'static str { - match kind { - TerminatorKind::Goto { .. } => "Goto", - TerminatorKind::SwitchInt { .. } => "SwitchInt", - TerminatorKind::Resume => "Resume", - TerminatorKind::Abort => "Abort", - TerminatorKind::Return => "Return", - TerminatorKind::Unreachable => "Unreachable", - TerminatorKind::Drop { .. } => "Drop", - TerminatorKind::DropAndReplace { .. } => "DropAndReplace", - TerminatorKind::Call { .. } => "Call", - TerminatorKind::Assert { .. } => "Assert", - TerminatorKind::Yield { .. } => "Yield", - TerminatorKind::GeneratorDrop => "GeneratorDrop", - TerminatorKind::FalseEdge { .. } => "FalseEdge", - TerminatorKind::FalseUnwind { .. } => "FalseUnwind", - TerminatorKind::InlineAsm { .. } => "InlineAsm", - } -} - -/// A BCB is deconstructed into one or more `Span`s. Each `Span` maps to a `CoverageSpan` that -/// references the originating BCB and one or more MIR `Statement`s and/or `Terminator`s. -/// Initially, the `Span`s come from the `Statement`s and `Terminator`s, but subsequent -/// transforms can combine adjacent `Span`s and `CoverageSpan` from the same BCB, merging the -/// `CoverageStatement` vectors, and the `Span`s to cover the extent of the combined `Span`s. -/// -/// Note: A `CoverageStatement` merged into another CoverageSpan may come from a `BasicBlock` that -/// is not part of the `CoverageSpan` bcb if the statement was included because it's `Span` matches -/// or is subsumed by the `Span` associated with this `CoverageSpan`, and it's `BasicBlock` -/// `is_dominated_by()` the `BasicBlock`s in this `CoverageSpan`. -#[derive(Debug, Clone)] -struct CoverageSpan { - span: Span, - bcb: BasicCoverageBlock, - coverage_statements: Vec, - is_closure: bool, -} - -impl CoverageSpan { - pub fn for_statement( - statement: &Statement<'tcx>, - span: Span, - bcb: BasicCoverageBlock, - bb: BasicBlock, - stmt_index: usize, - ) -> Self { - let is_closure = match statement.kind { - StatementKind::Assign(box ( - _, - Rvalue::Aggregate(box AggregateKind::Closure(_, _), _), - )) => true, - _ => false, - }; - - Self { - span, - bcb, - coverage_statements: vec![CoverageStatement::Statement(bb, span, stmt_index)], - is_closure, - } - } - - pub fn for_terminator(span: Span, bcb: BasicCoverageBlock, bb: BasicBlock) -> Self { - Self { - span, - bcb, - coverage_statements: vec![CoverageStatement::Terminator(bb, span)], - is_closure: false, - } - } - - pub fn merge_from(&mut self, mut other: CoverageSpan) { - debug_assert!(self.is_mergeable(&other)); - self.span = self.span.to(other.span); - if other.is_closure { - self.is_closure = true; - } - self.coverage_statements.append(&mut other.coverage_statements); - } - - pub fn cutoff_statements_at(&mut self, cutoff_pos: BytePos) { - self.coverage_statements.retain(|covstmt| covstmt.span().hi() <= cutoff_pos); - if let Some(highest_covstmt) = - self.coverage_statements.iter().max_by_key(|covstmt| covstmt.span().hi()) - { - self.span = self.span.with_hi(highest_covstmt.span().hi()); - } - } - - #[inline] - pub fn is_dominated_by( - &self, - other: &CoverageSpan, - bcb_dominators: &Dominators, - ) -> bool { - debug_assert!(!self.is_in_same_bcb(other)); - bcb_dominators.is_dominated_by(self.bcb, other.bcb) - } - - #[inline] - pub fn is_mergeable(&self, other: &Self) -> bool { - self.is_in_same_bcb(other) && !(self.is_closure || other.is_closure) - } - - #[inline] - pub fn is_in_same_bcb(&self, other: &Self) -> bool { - self.bcb == other.bcb - } - - pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String { - format!( - "{}\n {}", - spanview::source_range_no_file(tcx, &self.span), - self.format_coverage_statements(tcx, mir_body).replace("\n", "\n "), - ) - } - - pub fn format_coverage_statements( - &self, - tcx: TyCtxt<'tcx>, - mir_body: &'a mir::Body<'tcx>, - ) -> String { - let mut sorted_coverage_statements = self.coverage_statements.clone(); - sorted_coverage_statements.sort_unstable_by_key(|covstmt| match *covstmt { - CoverageStatement::Statement(bb, _, index) => (bb, index), - CoverageStatement::Terminator(bb, _) => (bb, usize::MAX), - }); - sorted_coverage_statements - .iter() - .map(|covstmt| covstmt.format(tcx, mir_body)) - .collect::>() - .join("\n") - } -} - -/// Maintains separate worklists for each loop in the BasicCoverageBlock CFG, plus one for the -/// BasicCoverageBlocks outside all loops. This supports traversing the BCB CFG in a way that -/// ensures a loop is completely traversed before processing Blocks after the end of the loop. -#[derive(Debug)] -struct TraversalContext { - /// From one or more backedges returning to a loop header. - loop_backedges: Option<(Vec, BasicCoverageBlock)>, - - /// worklist, to be traversed, of BasicCoverageBlocks in the loop with the given loop - /// backedges, such that the loop is the inner inner-most loop containing these - /// BasicCoverageBlocks - worklist: Vec, -} - -struct Instrumentor<'a, 'tcx> { - pass_name: &'a str, - tcx: TyCtxt<'tcx>, - mir_body: &'a mut mir::Body<'tcx>, - hir_body: &'tcx rustc_hir::Body<'tcx>, - bcb_dominators: Dominators, - basic_coverage_blocks: BasicCoverageBlocks, - function_source_hash: Option, - next_counter_id: u32, - num_expressions: u32, - debug_expressions_cache: Option>, - debug_counters: DebugCounters, -} - -impl<'a, 'tcx> Instrumentor<'a, 'tcx> { - fn new(pass_name: &'a str, tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self { - let hir_body = hir_body(tcx, mir_body.source.def_id()); - let basic_coverage_blocks = BasicCoverageBlocks::from_mir(mir_body); - let bcb_dominators = basic_coverage_blocks.compute_bcb_dominators(); - Self { - pass_name, - tcx, - mir_body, - hir_body, - basic_coverage_blocks, - bcb_dominators, - function_source_hash: None, - next_counter_id: CounterValueReference::START.as_u32(), - num_expressions: 0, - debug_expressions_cache: None, - debug_counters: DebugCounters::new(), - } - } - - /// Counter IDs start from one and go up. - fn next_counter(&mut self) -> CounterValueReference { - assert!(self.next_counter_id < u32::MAX - self.num_expressions); - let next = self.next_counter_id; - self.next_counter_id += 1; - CounterValueReference::from(next) - } - - /// Expression IDs start from u32::MAX and go down because a Expression can reference - /// (add or subtract counts) of both Counter regions and Expression regions. The counter - /// expression operand IDs must be unique across both types. - fn next_expression(&mut self) -> InjectedExpressionId { - assert!(self.next_counter_id < u32::MAX - self.num_expressions); - let next = u32::MAX - self.num_expressions; - self.num_expressions += 1; - InjectedExpressionId::from(next) - } - - fn function_source_hash(&mut self) -> u64 { - match self.function_source_hash { - Some(hash) => hash, - None => { - let hash = hash_mir_source(self.tcx, self.hir_body); - self.function_source_hash.replace(hash); - hash - } - } - } - - fn inject_counters(&'a mut self) { - let tcx = self.tcx; - let source_map = tcx.sess.source_map(); - let mir_source = self.mir_body.source; - let def_id = mir_source.def_id(); - let body_span = self.body_span(); - let source_file = source_map.lookup_source_file(body_span.lo()); - let file_name = Symbol::intern(&source_file.name.to_string()); - - debug!("instrumenting {:?}, span: {}", def_id, source_map.span_to_string(body_span)); - - let dump_spanview = pretty::dump_enabled(tcx, self.pass_name, def_id); - let dump_graphviz = tcx.sess.opts.debugging_opts.dump_mir_graphviz; - - if dump_graphviz { - self.debug_counters.enable(); - } - - let coverage_spans = self.coverage_spans(); - let span_viewables = - if dump_spanview { Some(self.span_viewables(&coverage_spans)) } else { None }; - let mut collect_intermediate_expressions = - Vec::with_capacity(self.basic_coverage_blocks.num_nodes()); - - // When debug logging, or generating the coverage graphviz output, initialize the following - // data structures: - let mut debug_used_expression_operands = None; - let mut debug_unused_expressions = None; - if level_enabled!(tracing::Level::DEBUG) || dump_graphviz { - debug_used_expression_operands = Some(FxHashMap::default()); - debug_unused_expressions = Some(Vec::new()); - if debug_options().simplify_expressions { - self.debug_expressions_cache.replace(FxHashMap::default()); - } - // CAUTION! The `simplify_expressions` option is only helpful for some debugging - // situations and it can change the generated MIR `Coverage` statements (resulting in - // differences in behavior when enabled, under `DEBUG`, compared to normal operation and - // testing). - // - // For debugging purposes, it is sometimes helpful to simplify some expression - // equations: - // - // * `x + (y - x)` becomes just `y` - // * `x + (y + 0)` becomes just x + y. - // - // Expression dependencies can deeply nested expressions, which can look quite long in - // printed debug messages and in graphs produced by `-Zdump-graphviz`. In reality, each - // referenced/nested expression is only present because that value is necessary to - // compute a counter value for another part of the coverage report. Simplifying - // expressions Does not result in less `Coverage` statements, so there is very little, - // if any, benefit to binary size or runtime to simplifying expressions, and adds - // additional compile-time complexity. Only enable this temporarily, if helpful to parse - // the debug output. - } - - // When debugging with BCB graphviz output, initialize additional data structures. - let mut debug_bcb_to_coverage_spans_with_counters = None; - let mut debug_bcb_to_dependency_counter = None; - let mut debug_edge_to_counter = None; - if dump_graphviz { - debug_bcb_to_coverage_spans_with_counters = Some(FxHashMap::default()); - debug_bcb_to_dependency_counter = Some(FxHashMap::default()); - debug_edge_to_counter = Some(FxHashMap::default()); - } - - let mut bcbs_with_coverage = BitSet::new_empty(self.basic_coverage_blocks.num_nodes()); - for covspan in &coverage_spans { - bcbs_with_coverage.insert(covspan.bcb); - } - - // Analyze the coverage graph (aka, BCB control flow graph), and inject expression-optimized - // counters. - self.make_bcb_counters(bcbs_with_coverage, &mut collect_intermediate_expressions); - - // If debugging, add any intermediate expressions (which are not associated with any BCB) to - // the `debug_used_expression_operands` map. - - if let Some(used_expression_operands) = debug_used_expression_operands.as_mut() { - for intermediate_expression in &collect_intermediate_expressions { - if let CoverageKind::Expression { id, lhs, rhs, .. } = *intermediate_expression { - used_expression_operands.entry(lhs).or_insert_with(|| Vec::new()).push(id); - used_expression_operands.entry(rhs).or_insert_with(|| Vec::new()).push(id); - } - } - } - - // Inject a counter for each `CoverageSpan`. There can be multiple `CoverageSpan`s for a - // given BCB, but only one actual counter needs to be incremented per BCB. `bb_counters` - // maps each `bcb` to its `Counter`, when injected. Subsequent `CoverageSpan`s - // for a BCB that already has a `Counter` will inject an `Expression` instead, and - // compute its value by adding `ZERO` to the BCB `Counter` value. - // - // If debugging, add every BCB `Expression` associated with a `CoverageSpan`s to the - // `debug_used_expression_operands` map. - let mut bcb_counters = IndexVec::from_elem_n(None, self.basic_coverage_blocks.num_nodes()); - for covspan in coverage_spans { - let bcb = covspan.bcb; - let span = covspan.span; - let counter_kind = if let Some(&counter_operand) = bcb_counters[bcb].as_ref() { - self.make_identity_counter(counter_operand) - } else if let Some(counter_kind) = self.bcb_data_mut(bcb).take_counter() { - bcb_counters[bcb] = Some(counter_kind.as_operand_id()); - if let Some(used_expression_operands) = debug_used_expression_operands.as_mut() { - if let CoverageKind::Expression { id, lhs, rhs, .. } = counter_kind { - used_expression_operands.entry(lhs).or_insert_with(|| Vec::new()).push(id); - used_expression_operands.entry(rhs).or_insert_with(|| Vec::new()).push(id); - } - } - counter_kind - } else { - bug!("Every BasicCoverageBlock should have a Counter or Expression"); - }; - if let Some(bcb_to_coverage_spans_with_counters) = - debug_bcb_to_coverage_spans_with_counters.as_mut() - { - bcb_to_coverage_spans_with_counters - .entry(bcb) - .or_insert_with(|| Vec::new()) - .push((covspan.clone(), counter_kind.clone())); - } - let some_code_region = if self.is_code_region_redundant(bcb, span, body_span) { - None - } else { - Some(make_code_region(file_name, &source_file, span, body_span)) - }; - self.inject_statement(counter_kind, self.bcb_last_bb(bcb), some_code_region); - } - - // The previous step looped through the `CoverageSpan`s and injected the counter from the - // `CoverageSpan`s `BasicCoverageBlock`, removing it from the BCB in the process (via - // `take_counter()`). - // - // Any other counter associated with a `BasicCoverageBlock`, or its incoming edge, but not - // associated with a `CoverageSpan`, should only exist if the counter is a - // `Expression` dependency (one of the expression operands). Collect them, and inject - // the additional counters into the MIR, without a reportable coverage span. - let mut bcb_counters_without_direct_coverage_spans = Vec::new(); - for (target_bcb, target_bcb_data) in self.basic_coverage_blocks.iter_enumerated_mut() { - if let Some(counter_kind) = target_bcb_data.take_counter() { - bcb_counters_without_direct_coverage_spans.push((None, target_bcb, counter_kind)); - } - if let Some(edge_counters) = target_bcb_data.take_edge_counters() { - for (from_bcb, counter_kind) in edge_counters { - bcb_counters_without_direct_coverage_spans.push(( - Some(from_bcb), - target_bcb, - counter_kind, - )); - } - } - } - - // Validate that every BCB or edge counter not directly associated with a coverage span is - // at least indirectly associated (it is a dependency of a BCB counter that _is_ associated - // with a coverage span). - if let Some(used_expression_operands) = debug_used_expression_operands.as_mut() { - let mut not_validated = bcb_counters_without_direct_coverage_spans - .iter() - .map(|(_, _, counter_kind)| counter_kind) - .collect::>(); - let mut validating_count = 0; - while not_validated.len() != validating_count { - let to_validate = not_validated.split_off(0); - validating_count = to_validate.len(); - for counter_kind in to_validate { - if used_expression_operands.contains_key(&counter_kind.as_operand_id()) { - if let CoverageKind::Expression { id, lhs, rhs, .. } = *counter_kind { - used_expression_operands - .entry(lhs) - .or_insert_with(|| Vec::new()) - .push(id); - used_expression_operands - .entry(rhs) - .or_insert_with(|| Vec::new()) - .push(id); - } - } else { - not_validated.push(counter_kind); - } - } - } - } - - for (edge_counter_from_bcb, target_bcb, counter_kind) in - bcb_counters_without_direct_coverage_spans - { - if let (Some(used_expression_operands), Some(unused_expressions)) = - (debug_used_expression_operands.as_ref(), debug_unused_expressions.as_mut()) - { - if !used_expression_operands.contains_key(&counter_kind.as_operand_id()) { - unused_expressions.push(( - counter_kind.clone(), - edge_counter_from_bcb, - target_bcb, - )); - } - } - - match counter_kind { - CoverageKind::Counter { .. } => { - let inject_to_bb = if let Some(from_bcb) = edge_counter_from_bcb { - // The MIR edge starts `from_bb` (the outgoing / last BasicBlock in - // `from_bcb`) and ends at `to_bb` (the incoming / first BasicBlock in the - // `target_bcb`; also called the `leader_bb`). - let from_bb = self.bcb_last_bb(from_bcb); - let to_bb = self.bcb_leader_bb(target_bcb); - - debug!( - "Edge {:?} (last {:?}) -> {:?} (leader {:?}) requires a new MIR \ - BasicBlock, for unclaimed edge counter {}", - edge_counter_from_bcb, - from_bb, - target_bcb, - to_bb, - self.format_counter(&counter_kind), - ); - debug!( - " from_bb {:?} has successors: {:?}", - from_bb, - self.mir_body[from_bb].terminator().successors(), - ); - let span = - self.mir_body[from_bb].terminator().source_info.span.shrink_to_hi(); - let new_bb = self.mir_body.basic_blocks_mut().push(BasicBlockData { - statements: vec![], // counter will be injected here - terminator: Some(Terminator { - source_info: SourceInfo::outermost(span), - kind: TerminatorKind::Goto { target: to_bb }, - }), - is_cleanup: false, - }); - let edge_ref = self.mir_body[from_bb] - .terminator_mut() - .successors_mut() - .find(|successor| **successor == to_bb) - .expect("from_bb should have a successor for to_bb"); - *edge_ref = new_bb; - - if let Some(edge_to_counter) = debug_edge_to_counter.as_mut() { - debug!( - "from_bcb={:?} to new_bb={:?} has edge_counter={}", - from_bcb, - new_bb, - self.format_counter(&counter_kind), - ); - edge_to_counter - .insert((from_bcb, new_bb), counter_kind.clone()) - .expect_none( - "invalid attempt to insert more than one edge counter for the \ - same edge", - ); - } - new_bb - } else { - if let Some(bcb_to_dependency_counter) = - debug_bcb_to_dependency_counter.as_mut() - { - bcb_to_dependency_counter - .entry(target_bcb) - .or_insert_with(|| Vec::new()) - .push(counter_kind.clone()); - } - let target_bb = self.bcb_last_bb(target_bcb); - debug!( - "{:?} ({:?}) gets a new Coverage statement for unclaimed counter {}", - target_bcb, - target_bb, - self.format_counter(&counter_kind), - ); - target_bb - }; - - self.inject_statement(counter_kind, inject_to_bb, None); - } - CoverageKind::Expression { .. } => { - self.inject_intermediate_expression(counter_kind) - } - _ => bug!("CoverageKind should be a counter"), - } - } - - if dump_graphviz { - let bcb_to_coverage_spans_with_counters = debug_bcb_to_coverage_spans_with_counters - .expect("graphviz data should exist if dump_graphviz is true"); - let bcb_to_dependency_counter = debug_bcb_to_dependency_counter - .expect("graphviz data should exist if dump_graphviz is true"); - let edge_to_counter = - debug_edge_to_counter.expect("graphviz data should exist if dump_graphviz is true"); - let graphviz_name = format!("Cov_{}_{}", def_id.krate.index(), def_id.index.index()); - let node_content = |bcb| { - self.bcb_data(bcb).to_string_sections( - tcx, - self.mir_body, - &self.debug_counters, - bcb_to_coverage_spans_with_counters.get(&bcb), - bcb_to_dependency_counter.get(&bcb), - // collect_intermediate_expressions are injected into the mir::START_BLOCK, so - // include them in the first BCB. - if bcb.index() == 0 { Some(&collect_intermediate_expressions) } else { None }, - ) - }; - let edge_labels = |from_bcb| { - let from_terminator = self.bcb_terminator(from_bcb); - let mut edge_labels = from_terminator.kind.fmt_successor_labels(); - edge_labels.retain(|label| label.to_string() != "unreachable"); - let edge_counters = from_terminator - .successors() - .map(|&successor| edge_to_counter.get(&(from_bcb, successor))); - edge_labels - .iter() - .zip(edge_counters) - .map(|(label, some_counter)| { - if let Some(counter) = some_counter { - format!("{}\n{}", label, self.format_counter(counter)) - } else { - label.to_string() - } - }) - .collect::>() - }; - let mut graphviz_writer = GraphvizWriter::new( - &self.basic_coverage_blocks, - &graphviz_name, - node_content, - edge_labels, - ); - if let Some(unused_expressions) = debug_unused_expressions.as_ref() { - if unused_expressions.len() > 0 { - graphviz_writer.set_graph_label(&format!( - "Unused expressions:\n {}", - unused_expressions - .as_slice() - .iter() - .map(|(counter_kind, edge_counter_from_bcb, target_bcb)| { - if let Some(from_bcb) = edge_counter_from_bcb.as_ref() { - format!( - "{:?}->{:?}: {}", - from_bcb, - target_bcb, - self.format_counter(&counter_kind), - ) - } else { - format!( - "{:?}: {}", - target_bcb, - self.format_counter(&counter_kind), - ) - } - }) - .collect::>() - .join("\n ") - )); - } - } - let mut file = - pretty::create_dump_file(tcx, "dot", None, self.pass_name, &0, mir_source) - .expect("Unexpected error creating BasicCoverageBlock graphviz DOT file"); - graphviz_writer - .write_graphviz(tcx, &mut file) - .expect("Unexpected error writing BasicCoverageBlock graphviz DOT file"); - } - - if let Some(unused_expressions) = debug_unused_expressions.as_ref() { - for (counter_kind, edge_counter_from_bcb, target_bcb) in unused_expressions { - let unused_counter_message = if let Some(from_bcb) = edge_counter_from_bcb.as_ref() - { - format!( - "non-coverage edge counter found without a dependent expression, in \ - {:?}->{:?}; counter={}", - from_bcb, - target_bcb, - self.format_counter(&counter_kind), - ) - } else { - format!( - "non-coverage counter found without a dependent expression, in {:?}; \ - counter={}", - target_bcb, - self.format_counter(&counter_kind), - ) - }; - - // FIXME(richkadel): Determine if unused expressions can and should be prevented, - // and if so, it would be a `bug!` if encountered in the future. At the present - // time, however we do sometimes encounter unused expressions (in only a subset of - // test cases) even when `simplify_expressions` is disabled. It's not yet clear what - // causes this, or if it should be allowed in the long term, but as long as the - // coverage capability still works, generate warning messages only, for now. - if self.debug_expressions_cache.is_some() - || debug_options().allow_unused_expressions - { - // Note, the debugging option `simplify_expressions`, which initializes the - // `debug_expressions_cache` can cause some counters to become unused, and - // is not a bug. - // - // For example, converting `x + (y - x)` to just `y` removes a dependency - // on `y - x`. If that expression is not a dependency elsewhere, and if it is - // not associated with a `CoverageSpan`, it is now considered `unused`. - debug!("WARNING: {}", unused_counter_message); - } else { - bug!("{}", unused_counter_message); - } - } - } - - for intermediate_expression in collect_intermediate_expressions { - self.inject_intermediate_expression(intermediate_expression); - } - - if let Some(span_viewables) = span_viewables { - let mut file = - pretty::create_dump_file(tcx, "html", None, self.pass_name, &0, mir_source) - .expect("Unexpected error creating MIR spanview HTML file"); - let crate_name = tcx.crate_name(def_id.krate); - let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate(); - let title = format!("{}.{} - Coverage Spans", crate_name, item_name); - spanview::write_document(tcx, def_id, span_viewables, &title, &mut file) - .expect("Unexpected IO error dumping coverage spans as HTML"); - } - } - - fn is_code_region_redundant( - &self, - bcb: BasicCoverageBlock, - span: Span, - body_span: Span, - ) -> bool { - if span.hi() == body_span.hi() { - // All functions execute a `Return`-terminated `BasicBlock`, regardless of how the - // function returns; but only some functions also _can_ return after a `Goto` block - // that ends on the closing brace of the function (with the `Return`). When this - // happens, the last character is counted 2 (or possibly more) times, when we know - // the function returned only once (of course). By giving all `Goto` terminators at - // the end of a function a `non-reportable` code region, they are still counted - // if appropriate, but they don't increment the line counter, as long as their is - // also a `Return` on that last line. - if let TerminatorKind::Goto { .. } = self.bcb_terminator(bcb).kind { - return true; - } - } - false - } - - /// Traverse the BCB CFG and add either a `Counter` or `Expression` to ever BCB, to be - /// injected with `CoverageSpan`s. `Expressions` have no runtime overhead, so if a viable - /// expression (adding or subtracting two other counters or expressions) can compute the same - /// result as an embedded counter, an `Expression` should be used. - /// - /// If two `BasicCoverageBlocks` branch from another `BasicCoverageBlock`, one of the branches - /// can be counted by `Expression` by subtracting the other branch from the branching - /// block. Otherwise, the `BasicCoverageBlock` executed the least should have the `Counter`. - /// One way to predict which branch executes the least is by considering loops. A loop is exited - /// at a branch, so the branch that jumps to a `BasicCoverageBlock` outside the loop is almost - /// always executed less than the branch that does not exit the loop. - /// - /// Returns non-code-span expressions created to represent intermediate values (if required), - /// such as to add two counters so the result can be subtracted from another counter. - fn make_bcb_counters( - &mut self, - bcbs_with_coverage: BitSet, - collect_intermediate_expressions: &mut Vec, - ) { - debug!("make_bcb_counters(): adding a counter or expression to each BasicCoverageBlock"); - let num_bcbs = self.basic_coverage_blocks.num_nodes(); - let mut backedges = IndexVec::from_elem_n(Vec::::new(), num_bcbs); - - // Identify loops by their backedges - for (bcb, _) in self.basic_coverage_blocks.iter_enumerated() { - for &successor in &self.basic_coverage_blocks.successors[bcb] { - if self.bcb_is_dominated_by(bcb, successor) { - let loop_header = successor; - let backedge_from_bcb = bcb; - debug!( - "Found BCB backedge: {:?} -> loop_header: {:?}", - backedge_from_bcb, loop_header - ); - backedges[loop_header].push(backedge_from_bcb); - } - } - } - - let start_bcb = self.basic_coverage_blocks.start_node(); - - // `context_stack` starts with a `TraversalContext` for the main function context (beginning - // with the `start` BasicCoverageBlock of the function). New worklists are pushed to the top - // of the stack as loops are entered, and popped off of the stack when a loop's worklist is - // exhausted. - let mut context_stack = Vec::new(); - context_stack.push(TraversalContext { loop_backedges: None, worklist: vec![start_bcb] }); - let mut visited = BitSet::new_empty(num_bcbs); - - while let Some(bcb) = { - // Strip contexts with empty worklists from the top of the stack - while context_stack.last().map_or(false, |context| context.worklist.is_empty()) { - context_stack.pop(); - } - // Pop the next bcb off of the current context_stack. If none, all BCBs were visited. - context_stack.last_mut().map_or(None, |context| context.worklist.pop()) - } { - if !visited.insert(bcb) { - debug!("Already visited: {:?}", bcb); - continue; - } - debug!("Visiting {:?}", bcb); - if backedges[bcb].len() > 0 { - debug!("{:?} is a loop header! Start a new TraversalContext...", bcb); - context_stack.push(TraversalContext { - loop_backedges: Some((backedges[bcb].clone(), bcb)), - worklist: Vec::new(), - }); - } - - debug!( - "{:?} has {} successors:", - bcb, - self.basic_coverage_blocks.successors[bcb].len() - ); - for &successor in &self.basic_coverage_blocks.successors[bcb] { - for context in context_stack.iter_mut().rev() { - if let Some((_, loop_header)) = context.loop_backedges { - if self.bcb_is_dominated_by(successor, loop_header) { - if self.bcb_successors(successor).len() > 1 { - debug!( - "Adding branching successor {:?} to the beginning of the \ - worklist of loop headed by {:?}", - successor, loop_header - ); - context.worklist.insert(0, successor); - } else { - debug!( - "Adding non-branching successor {:?} to the end of the \ - worklist of loop headed by {:?}", - successor, loop_header - ); - context.worklist.push(successor); - } - break; - } - } else { - if self.bcb_successors(successor).len() > 1 { - debug!( - "Adding branching successor {:?} to the beginning of the non-loop \ - worklist", - successor - ); - context.worklist.insert(0, successor); - } else { - debug!( - "Adding non-branching successor {:?} to the end of the non-loop \ - worklist", - successor - ); - context.worklist.push(successor); - } - } - } - } - - if !bcbs_with_coverage.contains(bcb) { - continue; - } - - let bcb_counter_operand = - self.get_or_make_counter_operand(bcb, collect_intermediate_expressions); - - let needs_branch_counters = { - let successors = self.bcb_successors(bcb); - successors.len() > 1 - && successors - .iter() - .any(|&successor| self.bcb_data(successor).counter().is_none()) - }; - if needs_branch_counters { - let branching_bcb = bcb; - let branching_counter_operand = bcb_counter_operand; - let branches = self.bcb_successors(branching_bcb).clone(); - - debug!("{:?} is branching, with branches: {:?}", branching_bcb, branches); - - // At most one of the branches (or its edge, from the branching_bcb, - // if the branch has multiple incoming edges) can have a counter computed by - // expression. - // - // If at least one of the branches leads outside of a loop (`found_loop_exit` is - // true), and at least one other branch does not exit the loop (the first of which - // is captured in `some_reloop_branch`), it's likely any reloop branch will be - // executed far more often than loop exit branch, making the reloop branch a better - // candidate for an expression. - let mut some_reloop_branch = None; - for context in context_stack.iter().rev() { - if let Some((backedge_from_bcbs, _)) = &context.loop_backedges { - let mut found_loop_exit = false; - for &branch in branches.iter() { - if backedge_from_bcbs.iter().any(|&backedge_from_bcb| { - self.bcb_is_dominated_by(backedge_from_bcb, branch) - }) { - // The path from branch leads back to the top of the loop - some_reloop_branch = Some(branch); - } else { - // The path from branch leads outside this loop - found_loop_exit = true; - } - if some_reloop_branch.is_some() && found_loop_exit { - break; - } - } - debug!( - "found_loop_exit={}, some_reloop_branch={:?}", - found_loop_exit, some_reloop_branch - ); - if !found_loop_exit { - // No branches exit a loop, so there is no specific recommended branch - // for an `Expression`. - break; - } - if some_reloop_branch.is_some() { - // A recommended branch for an `Expression` was found. - break; - } - // else all branches exited this loop context, so run the same checks with - // the outer loop(s) - } - } - - // Select a branch for the expression, either the recommended `reloop_branch`, or - // if none was found, select any branch. - let expression_branch = if let Some(reloop_branch) = some_reloop_branch { - debug!("Adding expression to reloop_branch={:?}", reloop_branch); - reloop_branch - } else { - let &branch_without_counter = branches - .iter() - .find(|&&branch| self.bcb_data(branch).counter().is_none()) - .expect( - "needs_branch_counters was `true` so there should be at least one \ - branch", - ); - debug!( - "No preferred expression branch. Selected the first branch without a \ - counter. That branch={:?}", - branch_without_counter - ); - branch_without_counter - }; - - // Assign a Counter or Expression to each branch, plus additional - // `Expression`s, as needed, to sum up intermediate results. - let mut some_sumup_counter_operand = None; - for branch in branches { - if branch != expression_branch { - let branch_counter_operand = if self.bcb_has_multiple_incoming_edges(branch) - { - debug!( - "{:?} has multiple incoming edges, so adding an edge counter from \ - {:?}", - branch, branching_bcb - ); - self.get_or_make_edge_counter_operand( - branching_bcb, - branch, - collect_intermediate_expressions, - ) - } else { - debug!( - "{:?} has only one incoming edge (from {:?}), so adding a counter", - branch, branching_bcb - ); - self.get_or_make_counter_operand( - branch, - collect_intermediate_expressions, - ) - }; - if let Some(sumup_counter_operand) = - some_sumup_counter_operand.replace(branch_counter_operand) - { - let intermediate_expression = self.make_expression( - branch_counter_operand, - Op::Add, - sumup_counter_operand, - || None, - ); - debug!( - " new intermediate expression: {}", - self.format_counter(&intermediate_expression) - ); - let intermediate_expression_operand = - intermediate_expression.as_operand_id(); - collect_intermediate_expressions.push(intermediate_expression); - some_sumup_counter_operand.replace(intermediate_expression_operand); - } - } - } - let sumup_counter_operand = - some_sumup_counter_operand.expect("sumup_counter_operand should have a value"); - let multiple_incoming_edges = - self.bcb_has_multiple_incoming_edges(expression_branch); - debug!( - "expression_branch is {:?}, multiple_incoming_edges={}, expression_branch \ - predecessors: {:?}", - expression_branch, - multiple_incoming_edges, - self.bcb_predecessors(expression_branch) - ); - let expression = self.make_expression( - branching_counter_operand, - Op::Subtract, - sumup_counter_operand, - || { - Some(if multiple_incoming_edges { - format!("{:?}->{:?}", branching_bcb, expression_branch) - } else { - format!("{:?}", expression_branch) - }) - }, - ); - if multiple_incoming_edges { - debug!( - "Edge {:?}->{:?} gets an expression: {}", - branching_bcb, - expression_branch, - self.format_counter(&expression) - ); - self.bcb_data_mut(expression_branch) - .set_edge_counter_from(branching_bcb, expression); - } else { - debug!( - "{:?} gets an expression: {}", - expression_branch, - self.format_counter(&expression) - ); - self.bcb_data_mut(expression_branch).set_counter(expression); - } - } - } - - debug_assert_eq!(visited.count(), visited.domain_size()); - } - - #[inline] - fn format_counter(&self, counter_kind: &CoverageKind) -> String { - self.debug_counters.format_counter(counter_kind) - } - - #[inline] - fn bcb_leader_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock { - self.bcb_data(bcb).leader_bb() - } - - #[inline] - fn bcb_last_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock { - self.bcb_data(bcb).last_bb() - } - - #[inline] - fn bcb_terminator(&self, bcb: BasicCoverageBlock) -> &Terminator<'tcx> { - self.bcb_data(bcb).terminator(self.mir_body) - } - - #[inline] - fn bcb_data(&self, bcb: BasicCoverageBlock) -> &BasicCoverageBlockData { - &self.basic_coverage_blocks[bcb] - } - - #[inline] - fn bcb_data_mut(&mut self, bcb: BasicCoverageBlock) -> &mut BasicCoverageBlockData { - &mut self.basic_coverage_blocks[bcb] - } - - #[inline] - fn bcb_predecessors(&self, bcb: BasicCoverageBlock) -> &BcbPredecessors { - &self.basic_coverage_blocks.predecessors[bcb] - } - - #[inline] - fn bcb_successors(&self, bcb: BasicCoverageBlock) -> &Vec { - &self.basic_coverage_blocks.successors[bcb] - } - - #[inline] - fn bcb_has_multiple_incoming_edges(&self, bcb: BasicCoverageBlock) -> bool { - self.bcb_predecessors(bcb).len() > 1 - } - - #[inline] - fn bcb_is_dominated_by(&self, node: BasicCoverageBlock, dom: BasicCoverageBlock) -> bool { - self.bcb_dominators.is_dominated_by(node, dom) - } - - fn get_or_make_counter_operand( - &mut self, - bcb: BasicCoverageBlock, - collect_intermediate_expressions: &mut Vec, - ) -> ExpressionOperandId { - if let Some(counter_kind) = self.basic_coverage_blocks[bcb].counter() { - debug!(" {:?} already has a counter: {}", bcb, self.format_counter(counter_kind)); - counter_kind.as_operand_id() - } else { - if self.bcb_has_multiple_incoming_edges(bcb) { - let mut predecessors = self.bcb_predecessors(bcb).clone().into_iter(); - let first_edge_counter_operand = self.get_or_make_edge_counter_operand( - predecessors.next().unwrap(), - bcb, - collect_intermediate_expressions, - ); - let mut some_sumup_edge_counter_operand = None; - for predecessor in predecessors { - let edge_counter_operand = self.get_or_make_edge_counter_operand( - predecessor, - bcb, - collect_intermediate_expressions, - ); - if let Some(sumup_edge_counter_operand) = - some_sumup_edge_counter_operand.replace(edge_counter_operand) - { - let intermediate_expression = self.make_expression( - sumup_edge_counter_operand, - Op::Add, - edge_counter_operand, - || None, - ); - debug!( - " new intermediate expression: {}", - self.format_counter(&intermediate_expression) - ); - let intermediate_expression_operand = - intermediate_expression.as_operand_id(); - collect_intermediate_expressions.push(intermediate_expression); - some_sumup_edge_counter_operand.replace(intermediate_expression_operand); - } - } - let counter_kind = self.make_expression( - first_edge_counter_operand, - Op::Add, - some_sumup_edge_counter_operand.unwrap(), - || Some(format!("{:?}", bcb)), - ); - debug!( - " {:?} gets a new counter (sum of predecessor counters): {}", - bcb, - self.format_counter(&counter_kind) - ); - self.basic_coverage_blocks[bcb].set_counter(counter_kind) - } else { - let counter_kind = self.make_counter(|| Some(format!("{:?}", bcb))); - debug!(" {:?} gets a new counter: {}", bcb, self.format_counter(&counter_kind)); - self.basic_coverage_blocks[bcb].set_counter(counter_kind) - } - } - } - - fn get_or_make_edge_counter_operand( - &mut self, - from_bcb: BasicCoverageBlock, - to_bcb: BasicCoverageBlock, - collect_intermediate_expressions: &mut Vec, - ) -> ExpressionOperandId { - let successors = self.bcb_successors(from_bcb).iter(); - if successors.len() > 1 { - if let Some(counter_kind) = - self.basic_coverage_blocks[to_bcb].edge_counter_from(from_bcb) - { - debug!( - " Edge {:?}->{:?} already has a counter: {}", - from_bcb, - to_bcb, - self.format_counter(counter_kind) - ); - counter_kind.as_operand_id() - } else { - let counter_kind = - self.make_counter(|| Some(format!("{:?}->{:?}", from_bcb, to_bcb))); - debug!( - " Edge {:?}->{:?} gets a new counter: {}", - from_bcb, - to_bcb, - self.format_counter(&counter_kind) - ); - self.basic_coverage_blocks[to_bcb].set_edge_counter_from(from_bcb, counter_kind) - } - } else { - self.get_or_make_counter_operand(from_bcb, collect_intermediate_expressions) - } - } - - fn make_counter(&mut self, block_label_fn: F) -> CoverageKind - where - F: Fn() -> Option, - { - let counter = CoverageKind::Counter { - function_source_hash: self.function_source_hash(), - id: self.next_counter(), - }; - if self.debug_counters.is_enabled() { - self.debug_counters.add_counter(&counter, (block_label_fn)()); - } - counter - } - - fn make_expression( - &mut self, - mut lhs: ExpressionOperandId, - op: Op, - mut rhs: ExpressionOperandId, - block_label_fn: F, - ) -> CoverageKind - where - F: Fn() -> Option, - { - if let Some(expressions_cache) = self.debug_expressions_cache.as_ref() { - if let Some(CoverageKind::Expression { lhs: lhs_lhs, op, rhs: lhs_rhs, .. }) = - expressions_cache.get(&lhs) - { - if *lhs_rhs == ExpressionOperandId::ZERO { - lhs = *lhs_lhs; - } else if *op == Op::Subtract && *lhs_rhs == rhs { - if let Some(lhs_expression) = expressions_cache.get(lhs_lhs) { - let expression = lhs_expression.clone(); - return self.as_duplicate_expression(expression); - } else { - let counter = *lhs_lhs; - return self.make_identity_counter(counter); - } - } - } - - if let Some(CoverageKind::Expression { lhs: rhs_lhs, op, rhs: rhs_rhs, .. }) = - expressions_cache.get(&rhs) - { - if *rhs_rhs == ExpressionOperandId::ZERO { - rhs = *rhs_rhs; - } else if *op == Op::Subtract && *rhs_rhs == lhs { - if let Some(rhs_expression) = expressions_cache.get(rhs_lhs) { - let expression = rhs_expression.clone(); - return self.as_duplicate_expression(expression); - } else { - let counter = *rhs_lhs; - return self.make_identity_counter(counter); - } - } - } - } - - let id = self.next_expression(); - let expression = CoverageKind::Expression { id, lhs, op, rhs }; - if let Some(expressions_cache) = self.debug_expressions_cache.as_mut() { - expressions_cache.insert(id.into(), expression.clone()); - } - if self.debug_counters.is_enabled() { - self.debug_counters.add_counter(&expression, (block_label_fn)()); - } - expression - } - - fn as_duplicate_expression(&mut self, mut expression: CoverageKind) -> CoverageKind { - let next_expression_id = if self.debug_expressions_cache.is_some() { - Some(self.next_expression()) - } else { - None - }; - let expressions_cache = self - .debug_expressions_cache - .as_mut() - .expect("`as_duplicate_expression()` requires the debug_expressions_cache"); - match expression { - CoverageKind::Expression { ref mut id, .. } => { - *id = next_expression_id.expect( - "next_expression_id should be Some if there is a debug_expressions_cache", - ); - expressions_cache.insert(id.into(), expression.clone()); - } - _ => { - bug!("make_duplicate_expression called with non-expression type: {:?}", expression) - } - } - expression - } - - fn make_identity_counter(&mut self, counter_operand: ExpressionOperandId) -> CoverageKind { - if let Some(expression) = - self.debug_expressions_cache.as_ref().map_or(None, |c| c.get(&counter_operand)) - { - let new_expression = expression.clone(); - self.as_duplicate_expression(new_expression) - } else { - let some_block_label = if self.debug_counters.is_enabled() { - self.debug_counters.some_block_label(counter_operand).cloned() - } else { - None - }; - self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO, || { - some_block_label.clone() - }) - } - } - - fn inject_statement( - &mut self, - counter_kind: CoverageKind, - bb: BasicBlock, - some_code_region: Option, - ) { - debug!( - " injecting statement {:?} for {:?} at code region: {:?}", - counter_kind, bb, some_code_region - ); - let data = &mut self.mir_body[bb]; - let source_info = data.terminator().source_info; - let statement = Statement { - source_info, - kind: StatementKind::Coverage(box Coverage { - kind: counter_kind, - code_region: some_code_region, - }), - }; - data.statements.push(statement); - } - - // Non-code expressions are injected into the coverage map, without generating executable code. - fn inject_intermediate_expression(&mut self, expression: CoverageKind) { - debug_assert!(if let CoverageKind::Expression { .. } = expression { true } else { false }); - debug!(" injecting non-code expression {:?}", expression); - let inject_in_bb = mir::START_BLOCK; - let data = &mut self.mir_body[inject_in_bb]; - let source_info = data.terminator().source_info; - let statement = Statement { - source_info, - kind: StatementKind::Coverage(box Coverage { kind: expression, code_region: None }), - }; - data.statements.push(statement); - } - - /// Converts the computed `BasicCoverageBlockData`s into `SpanViewable`s. - fn span_viewables(&self, coverage_spans: &Vec) -> Vec { - let tcx = self.tcx; - let mut span_viewables = Vec::new(); - for coverage_span in coverage_spans { - let tooltip = coverage_span.format_coverage_statements(tcx, self.mir_body); - let CoverageSpan { span, bcb, .. } = coverage_span; - let bcb_data = self.bcb_data(*bcb); - let id = bcb_data.id(); - let leader_bb = bcb_data.leader_bb(); - span_viewables.push(SpanViewable { bb: leader_bb, span: *span, id, tooltip }); - } - span_viewables - } - - #[inline(always)] - fn body_span(&self) -> Span { - self.hir_body.value.span - } - - // Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of - // the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One `CoverageSpan` is generated - // for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will - // merge some `CoverageSpan`s, at which point a `CoverageSpan` may represent multiple - // `Statement`s and/or `Terminator`s.) - fn extract_spans( - &self, - bcb: BasicCoverageBlock, - bcb_data: &'a BasicCoverageBlockData, - ) -> Vec { - let body_span = self.body_span(); - bcb_data - .basic_blocks() - .map(|bbref| { - let bb = *bbref; - let data = &self.mir_body[bb]; - data.statements - .iter() - .enumerate() - .filter_map(move |(index, statement)| { - filtered_statement_span(statement, body_span).map(|span| { - CoverageSpan::for_statement(statement, span, bcb, bb, index) - }) - }) - .chain( - filtered_terminator_span(data.terminator(), body_span) - .map(|span| CoverageSpan::for_terminator(span, bcb, bb)), - ) - }) - .flatten() - .collect() - } - - /// Generate a minimal set of `CoverageSpan`s, each representing a contiguous code region to be - /// counted. - /// - /// The basic steps are: - /// - /// 1. Extract an initial set of spans from the `Statement`s and `Terminator`s of each - /// `BasicCoverageBlockData`. - /// 2. Sort the spans by span.lo() (starting position). Spans that start at the same position - /// are sorted with longer spans before shorter spans; and equal spans are sorted - /// (deterministically) based on "dominator" relationship (if any). - /// 3. Traverse the spans in sorted order to identify spans that can be dropped (for instance, - /// if another span or spans are already counting the same code region), or should be merged - /// into a broader combined span (because it represents a contiguous, non-branching, and - /// uninterrupted region of source code). - /// - /// Closures are exposed in their enclosing functions as `Assign` `Rvalue`s, and since - /// closures have their own MIR, their `Span` in their enclosing function should be left - /// "uncovered". - /// - /// Note the resulting vector of `CoverageSpan`s does may not be fully sorted (and does not need - /// to be). - fn coverage_spans(&self) -> Vec { - let mut initial_spans = Vec::::with_capacity(self.mir_body.num_nodes() * 2); - for (bcb, bcb_data) in self.basic_coverage_blocks.iter_enumerated() { - for coverage_span in self.extract_spans(bcb, bcb_data) { - initial_spans.push(coverage_span); - } - } - - if initial_spans.is_empty() { - // This can happen if, for example, the function is unreachable (contains only a - // `BasicBlock`(s) with an `Unreachable` terminator). - return initial_spans; - } - - initial_spans.sort_unstable_by(|a, b| { - if a.span.lo() == b.span.lo() { - if a.span.hi() == b.span.hi() { - if a.is_in_same_bcb(b) { - Some(Ordering::Equal) - } else { - // Sort equal spans by dominator relationship, in reverse order (so - // dominators always come after the dominated equal spans). When later - // comparing two spans in order, the first will either dominate the second, - // or they will have no dominator relationship. - self.bcb_dominators.rank_partial_cmp(b.bcb, a.bcb) - } - } else { - // Sort hi() in reverse order so shorter spans are attempted after longer spans. - // This guarantees that, if a `prev` span overlaps, and is not equal to, a - // `curr` span, the prev span either extends further left of the curr span, or - // they start at the same position and the prev span extends further right of - // the end of the curr span. - b.span.hi().partial_cmp(&a.span.hi()) - } - } else { - a.span.lo().partial_cmp(&b.span.lo()) - } - .unwrap() - }); - - let refinery = CoverageSpanRefinery::from_sorted_spans( - initial_spans, - &self.mir_body, - &self.basic_coverage_blocks, - &self.bcb_dominators, - ); - refinery.to_refined_spans() - } -} - -/// Converts the initial set of `CoverageSpan`s (one per MIR `Statement` or `Terminator`) into a -/// minimal set of `CoverageSpan`s, using the BCB CFG to determine where it is safe and useful to: -/// -/// * Remove duplicate source code coverage regions -/// * Merge spans that represent continuous (both in source code and control flow), non-branching -/// execution -/// * Carve out (leave uncovered) any span that will be counted by another MIR (notably, closures) -struct CoverageSpanRefinery<'a, 'tcx> { - /// The initial set of `CoverageSpan`s, sorted by `Span` (`lo` and `hi`) and by relative - /// dominance between the `BasicCoverageBlock`s of equal `Span`s. - sorted_spans_iter: std::vec::IntoIter, - - /// The MIR, used to look up `BasicBlockData`. - mir_body: &'a mir::Body<'tcx>, - - /// The BasicCoverageBlock Control Flow Graph (BCB CFG). - basic_coverage_blocks: &'a BasicCoverageBlocks, - - /// The BCB CFG's dominators tree, used to compute the dominance relationships, if any. - bcb_dominators: &'a Dominators, - - /// The current `CoverageSpan` to compare to its `prev`, to possibly merge, discard, force the - /// discard of the `prev` (and or `pending_dups`), or keep both (with `prev` moved to - /// `pending_dups`). If `curr` is not discarded or merged, it becomes `prev` for the next - /// iteration. - some_curr: Option, - - /// The original `span` for `curr`, in case the `curr` span is modified. - curr_original_span: Span, - - /// The CoverageSpan from a prior iteration; typically assigned from that iteration's `curr`. - /// If that `curr` was discarded, `prev` retains its value from the previous iteration. - some_prev: Option, - - /// Assigned from `curr_original_span` from the previous iteration. - prev_original_span: Span, - - /// One or more `CoverageSpan`s with the same `Span` but different `BasicCoverageBlock`s, and - /// no `BasicCoverageBlock` in this list dominates another `BasicCoverageBlock` in the list. - /// If a new `curr` span also fits this criteria (compared to an existing list of - /// `pending_dups`), that `curr` `CoverageSpan` moves to `prev` before possibly being added to - /// the `pending_dups` list, on the next iteration. As a result, if `prev` and `pending_dups` - /// have the same `Span`, the criteria for `pending_dups` holds for `prev` as well: a `prev` - /// with a matching `Span` does not dominate any `pending_dup` and no `pending_dup` dominates a - /// `prev` with a matching `Span`) - pending_dups: Vec, - - /// The final `CoverageSpan`s to add to the coverage map. A `Counter` or `Expression` - /// will also be injected into the MIR for each `CoverageSpan`. - refined_spans: Vec, -} - -impl<'a, 'tcx> CoverageSpanRefinery<'a, 'tcx> { - fn from_sorted_spans( - sorted_spans: Vec, - mir_body: &'a mir::Body<'tcx>, - basic_coverage_blocks: &'a BasicCoverageBlocks, - bcb_dominators: &'a Dominators, - ) -> Self { - let refined_spans = Vec::with_capacity(sorted_spans.len()); - let mut sorted_spans_iter = sorted_spans.into_iter(); - let prev = sorted_spans_iter.next().expect("at least one span"); - let prev_original_span = prev.span; - Self { - sorted_spans_iter, - mir_body, - basic_coverage_blocks, - bcb_dominators, - refined_spans, - some_curr: None, - curr_original_span: Span::with_root_ctxt(BytePos(0), BytePos(0)), - some_prev: Some(prev), - prev_original_span, - pending_dups: Vec::new(), - } - } - - /// Iterate through the sorted `CoverageSpan`s, and return the refined list of merged and - /// de-duplicated `CoverageSpan`s. - fn to_refined_spans(mut self) -> Vec { - while self.next_coverage_span() { - if self.curr().is_mergeable(self.prev()) { - debug!(" same bcb (and neither is a closure), merge with prev={:?}", self.prev()); - let prev = self.take_prev(); - self.curr_mut().merge_from(prev); - // Note that curr.span may now differ from curr_original_span - } else if self.prev_ends_before_curr() { - debug!( - " different bcbs and disjoint spans, so keep curr for next iter, and add \ - prev={:?}", - self.prev() - ); - let prev = self.take_prev(); - self.refined_spans.push(prev); - } else if self.prev().is_closure { - // drop any equal or overlapping span (`curr`) and keep `prev` to test again in the - // next iter - debug!( - " curr overlaps a closure (prev). Drop curr and keep prev for next iter. \ - prev={:?}", - self.prev() - ); - self.discard_curr(); - } else if self.curr().is_closure { - self.carve_out_span_for_closure(); - } else if self.prev_original_span == self.curr().span { - // Note that this compares the new span to `prev_original_span`, which may not - // be the full `prev.span` (if merged during the previous iteration). - self.hold_pending_dups_unless_dominated(); - } else { - self.cutoff_prev_at_overlapping_curr(); - } - } - - debug!(" AT END, adding last prev={:?}", self.prev()); - let prev = self.take_prev(); - let CoverageSpanRefinery { - mir_body, - basic_coverage_blocks, - pending_dups, - mut refined_spans, - .. - } = self; - for dup in pending_dups { - debug!(" ...adding at least one pending dup={:?}", dup); - refined_spans.push(dup); - } - refined_spans.push(prev); - - // Remove `CoverageSpan`s with empty spans ONLY if the empty `CoverageSpan`s BCB also has at - // least one other non-empty `CoverageSpan`. - let mut has_coverage = BitSet::new_empty(basic_coverage_blocks.num_nodes()); - for covspan in &refined_spans { - if !covspan.span.is_empty() { - has_coverage.insert(covspan.bcb); - } - } - refined_spans.retain(|covspan| { - !(covspan.span.is_empty() - && is_goto(&basic_coverage_blocks[covspan.bcb].terminator(mir_body).kind) - && has_coverage.contains(covspan.bcb)) - }); - - // Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage - // regions for the current function leave room for the closure's own coverage regions - // (injected separately, from the closure's own MIR). - refined_spans.retain(|covspan| !covspan.is_closure); - refined_spans - } - - fn curr(&self) -> &CoverageSpan { - self.some_curr - .as_ref() - .unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_curr")) - } - - fn curr_mut(&mut self) -> &mut CoverageSpan { - self.some_curr - .as_mut() - .unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_curr")) - } - - fn prev(&self) -> &CoverageSpan { - self.some_prev - .as_ref() - .unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_prev")) - } - - fn prev_mut(&mut self) -> &mut CoverageSpan { - self.some_prev - .as_mut() - .unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_prev")) - } - - fn take_prev(&mut self) -> CoverageSpan { - self.some_prev.take().unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_prev")) - } - - /// If there are `pending_dups` but `prev` is not a matching dup (`prev.span` doesn't match the - /// `pending_dups` spans), then one of the following two things happened during the previous - /// iteration: - /// * the previous `curr` span (which is now `prev`) was not a duplicate of the pending_dups - /// (in which case there should be at least two spans in `pending_dups`); or - /// * the `span` of `prev` was modified by `curr_mut().merge_from(prev)` (in which case - /// `pending_dups` could have as few as one span) - /// In either case, no more spans will match the span of `pending_dups`, so - /// add the `pending_dups` if they don't overlap `curr`, and clear the list. - fn check_pending_dups(&mut self) { - if let Some(dup) = self.pending_dups.last() { - if dup.span != self.prev().span { - debug!( - " SAME spans, but pending_dups are NOT THE SAME, so BCBs matched on \ - previous iteration, or prev started a new disjoint span" - ); - if dup.span.hi() <= self.curr().span.lo() { - let pending_dups = self.pending_dups.split_off(0); - for dup in pending_dups.into_iter() { - debug!(" ...adding at least one pending={:?}", dup); - self.refined_spans.push(dup); - } - } else { - self.pending_dups.clear(); - } - } - } - } - - /// Advance `prev` to `curr` (if any), and `curr` to the next `CoverageSpan` in sorted order. - fn next_coverage_span(&mut self) -> bool { - if let Some(curr) = self.some_curr.take() { - self.some_prev = Some(curr); - self.prev_original_span = self.curr_original_span; - } - while let Some(curr) = self.sorted_spans_iter.next() { - debug!("FOR curr={:?}", curr); - if self.prev_starts_after_next(&curr) { - debug!( - " prev.span starts after curr.span, so curr will be dropped (skipping past \ - closure?); prev={:?}", - self.prev() - ); - } else { - // Save a copy of the original span for `curr` in case the `CoverageSpan` is changed - // by `self.curr_mut().merge_from(prev)`. - self.curr_original_span = curr.span; - self.some_curr.replace(curr); - self.check_pending_dups(); - return true; - } - } - false - } - - /// If called, then the next call to `next_coverage_span()` will *not* update `prev` with the - /// `curr` coverage span. - fn discard_curr(&mut self) { - self.some_curr = None; - } - - /// Returns true if the curr span should be skipped because prev has already advanced beyond the - /// end of curr. This can only happen if a prior iteration updated `prev` to skip past a region - /// of code, such as skipping past a closure. - fn prev_starts_after_next(&self, next_curr: &CoverageSpan) -> bool { - self.prev().span.lo() > next_curr.span.lo() - } - - /// Returns true if the curr span starts past the end of the prev span, which means they don't - /// overlap, so we now know the prev can be added to the refined coverage spans. - fn prev_ends_before_curr(&self) -> bool { - self.prev().span.hi() <= self.curr().span.lo() - } - - /// If `prev`s span extends left of the closure (`curr`), carve out the closure's - /// span from `prev`'s span. (The closure's coverage counters will be injected when - /// processing the closure's own MIR.) Add the portion of the span to the left of the - /// closure; and if the span extends to the right of the closure, update `prev` to - /// that portion of the span. For any `pending_dups`, repeat the same process. - fn carve_out_span_for_closure(&mut self) { - let curr_span = self.curr().span; - let left_cutoff = curr_span.lo(); - let right_cutoff = curr_span.hi(); - let has_pre_closure_span = self.prev().span.lo() < right_cutoff; - let has_post_closure_span = self.prev().span.hi() > right_cutoff; - let mut pending_dups = self.pending_dups.split_off(0); - if has_pre_closure_span { - let mut pre_closure = self.prev().clone(); - pre_closure.span = pre_closure.span.with_hi(left_cutoff); - debug!(" prev overlaps a closure. Adding span for pre_closure={:?}", pre_closure); - if !pending_dups.is_empty() { - for mut dup in pending_dups.iter().cloned() { - dup.span = dup.span.with_hi(left_cutoff); - debug!(" ...and at least one pre_closure dup={:?}", dup); - self.refined_spans.push(dup); - } - } - self.refined_spans.push(pre_closure); - } - if has_post_closure_span { - // Update prev.span to start after the closure (and discard curr) - self.prev_mut().span = self.prev().span.with_lo(right_cutoff); - self.prev_original_span = self.prev().span; - for dup in pending_dups.iter_mut() { - dup.span = dup.span.with_lo(right_cutoff); - } - self.pending_dups.append(&mut pending_dups); - self.discard_curr(); // since self.prev() was already updated - } else { - pending_dups.clear(); - } - } - - /// Called if `curr.span` equals `prev_original_span` (and potentially equal to all - /// `pending_dups` spans, if any); but keep in mind, `prev.span` may start at a `Span.lo()` that - /// is less than (further left of) `prev_original_span.lo()`. - /// - /// When two `CoverageSpan`s have the same `Span`, dominated spans can be discarded; but if - /// neither `CoverageSpan` dominates the other, both (or possibly more than two) are held, - /// until their disposition is determined. In this latter case, the `prev` dup is moved into - /// `pending_dups` so the new `curr` dup can be moved to `prev` for the next iteration. - fn hold_pending_dups_unless_dominated(&mut self) { - // Equal coverage spans are ordered by dominators before dominated (if any), so it should be - // impossible for `curr` to dominate any previous `CoverageSpan`. - debug_assert!(!self.prev().is_dominated_by(self.curr(), self.bcb_dominators)); - - let initial_pending_count = self.pending_dups.len(); - if initial_pending_count > 0 { - let mut pending_dups = self.pending_dups.split_off(0); - pending_dups.retain(|dup| !self.curr().is_dominated_by(dup, self.bcb_dominators)); - self.pending_dups.append(&mut pending_dups); - if self.pending_dups.len() < initial_pending_count { - debug!( - " discarded {} of {} pending_dups that dominated curr", - initial_pending_count - self.pending_dups.len(), - initial_pending_count - ); - } - } - - if self.curr().is_dominated_by(self.prev(), self.bcb_dominators) { - debug!( - " different bcbs but SAME spans, and prev dominates curr. Discard prev={:?}", - self.prev() - ); - self.cutoff_prev_at_overlapping_curr(); - // If one span dominates the other, assocate the span with the code from the dominated - // block only (`curr`), and discard the overlapping portion of the `prev` span. (Note - // that if `prev.span` is wider than `prev_original_span`, a `CoverageSpan` will still - // be created for `prev`s block, for the non-overlapping portion, left of `curr.span`.) - // - // For example: - // match somenum { - // x if x < 1 => { ... } - // }... - // - // The span for the first `x` is referenced by both the pattern block (every time it is - // evaluated) and the arm code (only when matched). The counter will be applied only to - // the dominated block. This allows coverage to track and highlight things like the - // assignment of `x` above, if the branch is matched, making `x` available to the arm - // code; and to track and highlight the question mark `?` "try" operator at the end of - // a function call returning a `Result`, so the `?` is covered when the function returns - // an `Err`, and not counted as covered if the function always returns `Ok`. - } else { - // Save `prev` in `pending_dups`. (`curr` will become `prev` in the next iteration.) - // If the `curr` CoverageSpan is later discarded, `pending_dups` can be discarded as - // well; but if `curr` is added to refined_spans, the `pending_dups` will also be added. - debug!( - " different bcbs but SAME spans, and neither dominates, so keep curr for \ - next iter, and, pending upcoming spans (unless overlapping) add prev={:?}", - self.prev() - ); - let prev = self.take_prev(); - self.pending_dups.push(prev); - } - } - - /// `curr` overlaps `prev`. If `prev`s span extends left of `curr`s span, keep _only_ - /// statements that end before `curr.lo()` (if any), and add the portion of the - /// combined span for those statements. Any other statements have overlapping spans - /// that can be ignored because `curr` and/or other upcoming statements/spans inside - /// the overlap area will produce their own counters. This disambiguation process - /// avoids injecting multiple counters for overlapping spans, and the potential for - /// double-counting. - fn cutoff_prev_at_overlapping_curr(&mut self) { - debug!( - " different bcbs, overlapping spans, so ignore/drop pending and only add prev \ - if it has statements that end before curr; prev={:?}", - self.prev() - ); - if self.pending_dups.is_empty() { - let curr_span = self.curr().span; - self.prev_mut().cutoff_statements_at(curr_span.lo()); - if self.prev().coverage_statements.is_empty() { - debug!(" ... no non-overlapping statements to add"); - } else { - debug!(" ... adding modified prev={:?}", self.prev()); - let prev = self.take_prev(); - self.refined_spans.push(prev); - } - } else { - // with `pending_dups`, `prev` cannot have any statements that don't overlap - self.pending_dups.clear(); - } - } -} - -fn filtered_statement_span(statement: &'a Statement<'tcx>, body_span: Span) -> Option { - match statement.kind { - // These statements have spans that are often outside the scope of the executed source code - // for their parent `BasicBlock`. - StatementKind::StorageLive(_) - | StatementKind::StorageDead(_) - // Coverage should not be encountered, but don't inject coverage coverage - | StatementKind::Coverage(_) - // Ignore `Nop`s - | StatementKind::Nop => None, - - // FIXME(richkadel): Look into a possible issue assigning the span to a - // FakeReadCause::ForGuardBinding, in this example: - // match somenum { - // x if x < 1 => { ... } - // }... - // The BasicBlock within the match arm code included one of these statements, but the span - // for it covered the `1` in this source. The actual statements have nothing to do with that - // source span: - // FakeRead(ForGuardBinding, _4); - // where `_4` is: - // _4 = &_1; (at the span for the first `x`) - // and `_1` is the `Place` for `somenum`. - // - // The arm code BasicBlock already has its own assignment for `x` itself, `_3 = 1`, and I've - // decided it's reasonable for that span (even though outside the arm code) to be part of - // the counted coverage of the arm code execution, but I can't justify including the literal - // `1` in the arm code. I'm pretty sure that, if the `FakeRead(ForGuardBinding)` has a - // purpose in codegen, it's probably in the right BasicBlock, but if so, the `Statement`s - // `source_info.span` can't be right. - // - // Consider correcting the span assignment, assuming there is a better solution, and see if - // the following pattern can be removed here: - StatementKind::FakeRead(cause, _) if cause == FakeReadCause::ForGuardBinding => None, - - // Retain spans from all other statements - StatementKind::FakeRead(_, _) // Not including `ForGuardBinding` - | StatementKind::Assign(_) - | StatementKind::SetDiscriminant { .. } - | StatementKind::LlvmInlineAsm(_) - | StatementKind::Retag(_, _) - | StatementKind::AscribeUserType(_, _) => { - Some(function_source_span(statement.source_info.span, body_span)) - } - } -} - -fn filtered_terminator_span(terminator: &'a Terminator<'tcx>, body_span: Span) -> Option { - match terminator.kind { - // These terminators have spans that don't positively contribute to computing a reasonable - // span of actually executed source code. (For example, SwitchInt terminators extracted from - // an `if condition { block }` has a span that includes the executed block, if true, - // but for coverage, the code region executed, up to *and* through the SwitchInt, - // actually stops before the if's block.) - TerminatorKind::Unreachable // Unreachable blocks are not connected to the MIR CFG - | TerminatorKind::Assert { .. } - | TerminatorKind::Drop { .. } - | TerminatorKind::DropAndReplace { .. } - | TerminatorKind::SwitchInt { .. } - // For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`. - // FIXME(richkadel): Note that `Goto` was moved to it's own match arm, for the reasons - // described below. Add tests to confirm whether or not similar cases also apply to - // `FalseEdge`. - | TerminatorKind::FalseEdge { .. } => None, - - // FIXME(richkadel): Note that `Goto` was initially filtered out (by returning `None`, as - // with the `TerminatorKind`s above) because its `Span` was way to broad to be beneficial, - // and, at the time, `Goto` didn't seem to provide any additional contributions to the - // coverage analysis. Upon further review, `Goto` terminated blocks do appear to benefit - // the coverage analysis, and the BCB CFG. To overcome the issues with the `Spans`, the - // coverage algorithms--and the final coverage map generation--include some exceptional - // behaviors. - // - // `Goto`s are often the targets of `SwitchInt` branches, and certain important - // optimizations to replace some `Counter`s with `Expression`s require a separate - // `BasicCoverageBlock` for each branch, to support the `Counter`, when needed. - // - // Also, some test cases showed that `Goto` terminators, and to some degree their `Span`s, - // provided useful context for coverage, such as to count and show when `if` blocks - // _without_ `else` blocks execute the `false` case (counting when the body of the `if` - // was _not_ taken). In these cases, the `Goto` span is ultimately given a `CoverageSpan` - // of 1 character, at the end of it's original `Span`. - // - // However, in other cases, a visible `CoverageSpan` is not wanted, but the `Goto` - // block must still be counted (for example, to contribute its count to an `Expression` - // that reports the execution count for some other block). In these cases, the code region - // is set to `None`. - TerminatorKind::Goto { .. } => { - Some(function_source_span(terminator.source_info.span.shrink_to_hi(), body_span)) - } - - // Retain spans from all other terminators - TerminatorKind::Resume - | TerminatorKind::Abort - | TerminatorKind::Return - | TerminatorKind::Call { .. } - | TerminatorKind::Yield { .. } - | TerminatorKind::GeneratorDrop - | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::InlineAsm { .. } => { - Some(function_source_span(terminator.source_info.span, body_span)) - } - } -} - -#[inline(always)] -fn function_source_span(span: Span, body_span: Span) -> Span { - let span = original_sp(span, body_span).with_ctxt(SyntaxContext::root()); - if body_span.contains(span) { span } else { body_span } -} - -/// Convert the Span into its file name, start line and column, and end line and column -fn make_code_region( - file_name: Symbol, - source_file: &Lrc, - span: Span, - body_span: Span, -) -> CodeRegion { - let (start_line, mut start_col) = source_file.lookup_file_pos(span.lo()); - let (end_line, end_col) = if span.hi() == span.lo() { - let (end_line, mut end_col) = (start_line, start_col); - // Extend an empty span by one character so the region will be counted. - let CharPos(char_pos) = start_col; - if span.hi() == body_span.hi() { - start_col = CharPos(char_pos - 1); - } else { - end_col = CharPos(char_pos + 1); - } - (end_line, end_col) - } else { - source_file.lookup_file_pos(span.hi()) - }; - CodeRegion { - file_name, - start_line: start_line as u32, - start_col: start_col.to_u32() + 1, - end_line: end_line as u32, - end_col: end_col.to_u32() + 1, - } -} - -#[inline(always)] -fn is_goto(term_kind: &TerminatorKind<'tcx>) -> bool { - match term_kind { - TerminatorKind::Goto { .. } => true, - _ => false, - } -} - -fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx rustc_hir::Body<'tcx> { - let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local"); - let fn_body_id = hir::map::associated_body(hir_node).expect("HIR node is a function with body"); - tcx.hir().body(fn_body_id) -} - -fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx>) -> u64 { - let mut hcx = tcx.create_no_span_stable_hashing_context(); - hash(&mut hcx, &hir_body.value).to_smaller_hash() -} - -fn hash( - hcx: &mut StableHashingContext<'tcx>, - node: &impl HashStable>, -) -> Fingerprint { - let mut stable_hasher = StableHasher::new(); - node.hash_stable(hcx, &mut stable_hasher); - stable_hasher.finish() -} - -fn bcb_filtered_successors<'a, 'tcx>( - body: &'tcx &'a mir::Body<'tcx>, - term_kind: &'tcx TerminatorKind<'tcx>, -) -> Box + 'a> { - let mut successors = term_kind.successors(); - box match &term_kind { - // SwitchInt successors are never unwind, and all of them should be traversed. - TerminatorKind::SwitchInt { .. } => successors, - // For all other kinds, return only the first successor, if any, and ignore unwinds. - // NOTE: `chain(&[])` is required to coerce the `option::iter` (from - // `next().into_iter()`) into the `mir::Successors` aliased type. - _ => successors.next().into_iter().chain(&[]), - } - .filter(move |&&successor| body[successor].terminator().kind != TerminatorKind::Unreachable) -} - -pub struct ShortCircuitPreorder< - 'a, - 'tcx, - F: Fn( - &'tcx &'a mir::Body<'tcx>, - &'tcx TerminatorKind<'tcx>, - ) -> Box + 'a>, -> { - body: &'tcx &'a mir::Body<'tcx>, - visited: BitSet, - worklist: Vec, - filtered_successors: F, -} - -impl< - 'a, - 'tcx, - F: Fn( - &'tcx &'a mir::Body<'tcx>, - &'tcx TerminatorKind<'tcx>, - ) -> Box + 'a>, -> ShortCircuitPreorder<'a, 'tcx, F> -{ - pub fn new( - body: &'tcx &'a mir::Body<'tcx>, - filtered_successors: F, - ) -> ShortCircuitPreorder<'a, 'tcx, F> { - let worklist = vec![mir::START_BLOCK]; - - ShortCircuitPreorder { - body, - visited: BitSet::new_empty(body.basic_blocks().len()), - worklist, - filtered_successors, - } - } -} - -impl< - 'a: 'tcx, - 'tcx, - F: Fn( - &'tcx &'a mir::Body<'tcx>, - &'tcx TerminatorKind<'tcx>, - ) -> Box + 'a>, -> Iterator for ShortCircuitPreorder<'a, 'tcx, F> -{ - type Item = (BasicBlock, &'a BasicBlockData<'tcx>); - - fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> { - while let Some(idx) = self.worklist.pop() { - if !self.visited.insert(idx) { - continue; - } - - let data = &self.body[idx]; - - if let Some(ref term) = data.terminator { - self.worklist.extend((self.filtered_successors)(&self.body, &term.kind)); - } - - return Some((idx, data)); - } - - None - } - - fn size_hint(&self) -> (usize, Option) { - let size = self.body.basic_blocks().len() - self.visited.count(); - (size, Some(size)) - } -} diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs index 20b8c90a9dcad..599a979f4be25 100644 --- a/compiler/rustc_mir/src/transform/mod.rs +++ b/compiler/rustc_mir/src/transform/mod.rs @@ -22,6 +22,7 @@ pub mod check_packed_ref; pub mod check_unsafety; pub mod cleanup_post_borrowck; pub mod const_prop; +pub mod coverage; pub mod deaggregator; pub mod dest_prop; pub mod dump_mir; @@ -30,7 +31,6 @@ pub mod elaborate_drops; pub mod generator; pub mod inline; pub mod instcombine; -pub mod instrument_coverage; pub mod match_branches; pub mod multiple_return_terminators; pub mod no_landing_pads; @@ -84,7 +84,7 @@ pub(crate) fn provide(providers: &mut Providers) { }, ..*providers }; - instrument_coverage::provide(providers); + coverage::query::provide(providers); } fn is_mir_available(tcx: TyCtxt<'_>, def_id: DefId) -> bool { @@ -304,7 +304,7 @@ fn mir_promoted( ]; let opt_coverage: &[&dyn MirPass<'tcx>] = if tcx.sess.opts.debugging_opts.instrument_coverage { - &[&instrument_coverage::InstrumentCoverage] + &[&coverage::InstrumentCoverage] } else { &[] }; From 1296fe30ad434890e3ca6662f8832a337efdf528 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Mon, 19 Oct 2020 12:15:14 -0700 Subject: [PATCH 14/18] Apparently there were a few recent changes affecting tests I should have re-tested for these, in the last PR. Here are those updates. --- .../mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot | 2 +- .../mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot | 6 +++--- .../try_error_result.main.-------.InstrumentCoverage.0.html | 6 ++---- .../try_error_result.main.-------.InstrumentCoverage.0.html | 6 ++---- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/test/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot b/src/test/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot index 49228328e011f..efc06bdea57a6 100644 --- a/src/test/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot +++ b/src/test/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot @@ -2,5 +2,5 @@ digraph Cov_0_4 { graph [fontname="Courier, monospace"]; node [fontname="Courier, monospace"]; edge [fontname="Courier, monospace"]; - bcb0__Cov_0_4 [shape="none", label=<
bcb0
Counter(#1) at 19:5-20:2
19:5-19:9: @0[0]: _0 = const true
20:2-20:2: @0.Return: return
bb0: Return
>]; + bcb0__Cov_0_4 [shape="none", label=<
bcb0
Counter(bcb0) at 19:5-20:2
19:5-19:9: @0[0]: _0 = const true
20:2-20:2: @0.Return: return
bb0: Return
>]; } diff --git a/src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot b/src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot index 3554dad09fb75..5ddd112fe62e6 100644 --- a/src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot +++ b/src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot @@ -2,9 +2,9 @@ digraph Cov_0_3 { graph [fontname="Courier, monospace"]; node [fontname="Courier, monospace"]; edge [fontname="Courier, monospace"]; - bcb2__Cov_0_3 [shape="none", label=<
bcb2
Expression(#1 - #2) at 14:6-14:6
14:6-14:6: @4.Goto: goto -> bb0
bb4: Goto
>]; - bcb1__Cov_0_3 [shape="none", label=<
bcb1
Counter(#2) at 12:13-12:18
12:13-12:18: @5[0]: _0 = const ()
Expression(#2 + 0) at 15:2-15:2
15:2-15:2: @5.Return: return
bb3: FalseEdge
bb5: Return
>]; - bcb0__Cov_0_3 [shape="none", label=<
bcb0
Counter(#1) at 11:12-11:17
11:12-11:17: @1.Call: _2 = bar() -> [return: bb2, unwind: bb6]
11:12-11:17: @2[0]: FakeRead(ForMatchedPlace, _2)
bb0: FalseUnwind
bb1: Call
bb2: SwitchInt
>]; + bcb2__Cov_0_3 [shape="none", label=<
bcb2
Expression(bcb0 - bcb1) at 14:6-14:6
14:6-14:6: @4.Goto: goto -> bb0
bb4: Goto
>]; + bcb1__Cov_0_3 [shape="none", label=<
bcb1
Counter(bcb1) at 12:13-12:18
12:13-12:18: @5[0]: _0 = const ()
Expression(bcb1 + 0) at 15:2-15:2
15:2-15:2: @5.Return: return
bb3: FalseEdge
bb5: Return
>]; + bcb0__Cov_0_3 [shape="none", label=<
bcb0
Counter(bcb0) at 11:12-11:17
11:12-11:17: @1.Call: _2 = bar() -> [return: bb2, unwind: bb6]
11:12-11:17: @2[0]: FakeRead(ForMatchedPlace, _2)
bb0: FalseUnwind
bb1: Call
bb2: SwitchInt
>]; bcb2__Cov_0_3 -> bcb0__Cov_0_3 [label=<>]; bcb0__Cov_0_3 -> bcb2__Cov_0_3 [label=]; bcb0__Cov_0_3 -> bcb1__Cov_0_3 [label=]; diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html index cc0e7abb1f3ad..c273c8c88e6f3 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html @@ -99,15 +99,13 @@ { @10,12,13,14⦊call(/*return_error=*/ true)⦉@10,12,13,14@16,18,19,20⦊?⦉@16,18,19,20; +27:41-27:42: @18.Call: _26 = <() as From<()>>::from(move _27) -> [return: bb19, unwind: bb32]">@16,18,19,20⦊?⦉@16,18,19,20
;
} else { @11,21,22⦊call(/*return_error=*/ false)⦉@11,21,22@24,26,27,28⦊?⦉@24,26,27,28; +31:42-31:43: @26.Call: _35 = <() as From<()>>::from(move _36) -> [return: bb27, unwind: bb32]">@24,26,27,28⦊?⦉@24,26,27,28
; }@15⦊‸⦉@15@23⦊‸⦉@23 }@29⦊‸⦉@29 { @10,12,13,14⦊call(/*return_error=*/ true)⦉@10,12,13,14@16,18,19,20⦊?⦉@16,18,19,20; +27:41-27:42: @18.Call: _26 = <() as From<()>>::from(move _27) -> [return: bb19, unwind: bb32]">@16,18,19,20⦊?⦉@16,18,19,20; } else { @11,21,22⦊call(/*return_error=*/ false)⦉@11,21,22@24,26,27,28⦊?⦉@24,26,27,28; +31:42-31:43: @26.Call: _35 = <() as From<()>>::from(move _36) -> [return: bb27, unwind: bb32]">@24,26,27,28⦊?⦉@24,26,27,28; }@15⦊‸⦉@15@23⦊‸⦉@23 }@29⦊‸⦉@29 , diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs index b3f5b7c6a0aa2..006d6662196a0 100644 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs @@ -28,21 +28,23 @@ 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." -pub struct FunctionCoverage { +pub struct FunctionCoverage<'tcx> { + instance: Instance<'tcx>, source_hash: u64, counters: IndexVec>, expressions: IndexVec>, unreachable_regions: Vec, } -impl FunctionCoverage { - pub fn new<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { +impl<'tcx> FunctionCoverage<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { let coverageinfo = tcx.coverageinfo(instance.def_id()); debug!( "FunctionCoverage::new(instance={:?}) has coverageinfo={:?}", instance, coverageinfo ); Self { + instance, source_hash: 0, // will be set with the first `add_counter()` counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize), expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize), @@ -50,6 +52,19 @@ impl FunctionCoverage { } } + /// Although every function should have at least one `Counter`, the `Counter` isn't required to + /// have a `CodeRegion`. (The `CodeRegion` may be associated only with `Expressions`.) This + /// method supports the ability to ensure the `function_source_hash` is set from `Counters` that + /// do not trigger the call to `add_counter()` because they don't have an associated + /// `CodeRegion` to add. + pub fn set_function_source_hash(&mut self, source_hash: u64) { + if self.source_hash == 0 { + self.source_hash = source_hash; + } else { + debug_assert_eq!(source_hash, self.source_hash); + } + } + /// Adds a code region to be counted by an injected counter intrinsic. /// The source_hash (computed during coverage instrumentation) should also be provided, and /// should be the same for all counters in a given function. @@ -111,7 +126,11 @@ impl FunctionCoverage { pub fn get_expressions_and_counter_regions<'a>( &'a self, ) -> (Vec, impl Iterator) { - assert!(self.source_hash != 0); + assert!( + self.source_hash != 0, + "No counters provided the source_hash for function: {:?}", + self.instance + ); let counter_regions = self.counter_regions(); let (counter_expressions, expression_regions) = self.expressions_with_regions(); diff --git a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs index 9daf097eaf2b4..c201782138127 100644 --- a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs @@ -24,6 +24,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if let Some(code_region) = code_region { bx.add_coverage_counter(self.instance, function_source_hash, id, code_region); + } else { + bx.set_function_source_hash(self.instance, function_source_hash); } // Note: Some counters do not have code regions, but may still be referenced from // expressions. diff --git a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs index 5aca48dc3f90e..59dd28d05a9aa 100644 --- a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs @@ -9,6 +9,8 @@ pub trait CoverageInfoMethods: BackendTypes { pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { fn create_pgo_func_name_var(&self, instance: Instance<'tcx>) -> Self::Value; + fn set_function_source_hash(&mut self, instance: Instance<'tcx>, function_source_hash: u64); + fn add_coverage_counter( &mut self, instance: Instance<'tcx>, diff --git a/compiler/rustc_mir/src/transform/coverage/debug.rs b/compiler/rustc_mir/src/transform/coverage/debug.rs index 5083ca8cedac2..5b8d50bf99ffe 100644 --- a/compiler/rustc_mir/src/transform/coverage/debug.rs +++ b/compiler/rustc_mir/src/transform/coverage/debug.rs @@ -7,6 +7,8 @@ use rustc_middle::mir::{BasicBlock, TerminatorKind}; use std::lazy::SyncOnceCell; +pub const NESTED_INDENT: &str = " "; + const RUSTC_COVERAGE_DEBUG_OPTIONS: &str = "RUSTC_COVERAGE_DEBUG_OPTIONS"; pub(crate) fn debug_options<'a>() -> &'a DebugOptions { diff --git a/compiler/rustc_mir/src/transform/coverage/graph.rs b/compiler/rustc_mir/src/transform/coverage/graph.rs index 6885fc5c61cbe..23ec026468089 100644 --- a/compiler/rustc_mir/src/transform/coverage/graph.rs +++ b/compiler/rustc_mir/src/transform/coverage/graph.rs @@ -1,3 +1,5 @@ +use super::Error; + use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::graph::dominators::{self, Dominators}; use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode}; @@ -313,16 +315,28 @@ impl BasicCoverageBlockData { } #[inline(always)] - pub fn set_counter(&mut self, counter_kind: CoverageKind) -> ExpressionOperandId { + pub fn set_counter( + &mut self, + counter_kind: CoverageKind, + ) -> Result { debug_assert!( + // If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also + // have an expression (to be injected into an existing `BasicBlock` represented by this + // `BasicCoverageBlock`). self.edge_from_bcbs.is_none() || counter_kind.is_expression(), "attempt to add a `Counter` to a BCB target with existing incoming edge counters" ); let operand = counter_kind.as_operand_id(); - self.counter_kind - .replace(counter_kind) - .expect_none("attempt to set a BasicCoverageBlock coverage counter more than once"); - operand + let expect_none = self.counter_kind.replace(counter_kind); + if expect_none.is_some() { + return Error::from_string(format!( + "attempt to set a BasicCoverageBlock coverage counter more than once; \ + {:?} already had counter {:?}", + self, + expect_none.unwrap(), + )); + } + Ok(operand) } #[inline(always)] @@ -340,19 +354,33 @@ impl BasicCoverageBlockData { &mut self, from_bcb: BasicCoverageBlock, counter_kind: CoverageKind, - ) -> ExpressionOperandId { - debug_assert!( - self.counter_kind.as_ref().map_or(true, |c| c.is_expression()), - "attempt to add an incoming edge counter from {:?} when the target BCB already has a \ - `Counter`", - from_bcb - ); + ) -> Result { + if level_enabled!(tracing::Level::DEBUG) { + // If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also + // have an expression (to be injected into an existing `BasicBlock` represented by this + // `BasicCoverageBlock`). + if !self.counter_kind.as_ref().map_or(true, |c| c.is_expression()) { + return Error::from_string(format!( + "attempt to add an incoming edge counter from {:?} when the target BCB already \ + has a `Counter`", + from_bcb + )); + } + } let operand = counter_kind.as_operand_id(); - self.edge_from_bcbs + let expect_none = self + .edge_from_bcbs .get_or_insert_with(|| FxHashMap::default()) - .insert(from_bcb, counter_kind) - .expect_none("attempt to set an edge counter more than once"); - operand + .insert(from_bcb, counter_kind); + if expect_none.is_some() { + return Error::from_string(format!( + "attempt to set an edge counter more than once; from_bcb: \ + {:?} already had counter {:?}", + from_bcb, + expect_none.unwrap(), + )); + } + Ok(operand) } #[inline(always)] @@ -383,6 +411,56 @@ impl BasicCoverageBlockData { } } +/// Represents a successor from a branching BasicCoverageBlock (such as the arms of a `SwitchInt`) +/// as either the successor BCB itself, if it has only one incoming edge, or the successor _plus_ +/// the specific branching BCB, representing the edge between the two. The latter case +/// distinguishes this incoming edge from other incoming edges to the same `target_bcb`. +#[derive(Clone, Copy, PartialEq, Eq)] +pub(crate) struct BcbBranch { + pub edge_from_bcb: Option, + pub target_bcb: BasicCoverageBlock, +} + +impl BcbBranch { + pub fn from_to( + from_bcb: BasicCoverageBlock, + to_bcb: BasicCoverageBlock, + basic_coverage_blocks: &CoverageGraph, + ) -> Self { + let edge_from_bcb = if basic_coverage_blocks.predecessors[to_bcb].len() > 1 { + Some(from_bcb) + } else { + None + }; + Self { edge_from_bcb, target_bcb: to_bcb } + } + + pub fn counter<'a>( + &self, + basic_coverage_blocks: &'a CoverageGraph, + ) -> Option<&'a CoverageKind> { + if let Some(from_bcb) = self.edge_from_bcb { + basic_coverage_blocks[self.target_bcb].edge_counter_from(from_bcb) + } else { + basic_coverage_blocks[self.target_bcb].counter() + } + } + + pub fn is_only_path_to_target(&self) -> bool { + self.edge_from_bcb.is_none() + } +} + +impl std::fmt::Debug for BcbBranch { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(from_bcb) = self.edge_from_bcb { + write!(fmt, "{:?}->{:?}", from_bcb, self.target_bcb) + } else { + write!(fmt, "{:?}", self.target_bcb) + } + } +} + fn bcb_filtered_successors<'a, 'tcx>( body: &'tcx &'a mir::Body<'tcx>, term_kind: &'tcx TerminatorKind<'tcx>, @@ -437,14 +515,18 @@ impl TraverseCoverageGraphWithLoops { } pub fn next(&mut self) -> Option { - // Strip contexts with empty worklists from the top of the stack - while self.context_stack.last().map_or(false, |context| context.worklist.is_empty()) { - self.context_stack.pop(); - } - // Pop the next bcb off of the current context_stack. If none, all BCBs were visited. - while let Some(next_bcb) = + debug!( + "TraverseCoverageGraphWithLoops::next - context_stack: {:?}", + self.context_stack.iter().rev().collect::>() + ); + while let Some(next_bcb) = { + // Strip contexts with empty worklists from the top of the stack + while self.context_stack.last().map_or(false, |context| context.worklist.is_empty()) { + self.context_stack.pop(); + } + // Pop the next bcb off of the current context_stack. If none, all BCBs were visited. self.context_stack.last_mut().map_or(None, |context| context.worklist.pop()) - { + } { if !self.visited.insert(next_bcb) { debug!("Already visited: {:?}", next_bcb); continue; @@ -459,9 +541,19 @@ impl TraverseCoverageGraphWithLoops { } return Some(next_bcb); } - debug_assert_eq!(self.visited.count(), self.visited.domain_size()); None } + + pub fn is_complete(&self) -> bool { + self.visited.count() == self.visited.domain_size() + } + + pub fn unvisited(&self) -> Vec { + let mut unvisited_set: BitSet = + BitSet::new_filled(self.visited.domain_size()); + unvisited_set.subtract(&self.visited); + unvisited_set.iter().collect::>() + } } fn find_loop_backedges( diff --git a/compiler/rustc_mir/src/transform/coverage/mod.rs b/compiler/rustc_mir/src/transform/coverage/mod.rs index dd963744ec865..6765f22c36c2a 100644 --- a/compiler/rustc_mir/src/transform/coverage/mod.rs +++ b/compiler/rustc_mir/src/transform/coverage/mod.rs @@ -4,9 +4,10 @@ mod debug; mod graph; mod spans; -use debug::{debug_options, term_type}; +use debug::{debug_options, term_type, NESTED_INDENT}; use graph::{ - BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, TraverseCoverageGraphWithLoops, + BasicCoverageBlock, BasicCoverageBlockData, BcbBranch, CoverageGraph, + TraverseCoverageGraphWithLoops, }; use spans::{CoverageSpan, CoverageSpans}; @@ -35,6 +36,18 @@ use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; use rustc_span::{CharPos, Pos, SourceFile, Span, Symbol}; +/// A simple error message wrapper for `coverage::Error`s. +#[derive(Debug)] +pub(crate) struct Error { + message: String, +} + +impl Error { + pub fn from_string(message: String) -> Result { + Err(Self { message }) + } +} + /// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected /// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen /// to construct the coverage map. @@ -165,9 +178,16 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { ); // When dumping coverage spanview files, create `SpanViewables` from the `coverage_spans`. - let mut debug_span_viewables = None; if dump_spanview { - debug_span_viewables = Some(self.span_viewables(&coverage_spans)); + let span_viewables = self.span_viewables(&coverage_spans); + let mut file = + pretty::create_dump_file(tcx, "html", None, self.pass_name, &0, mir_source) + .expect("Unexpected error creating MIR spanview HTML file"); + let crate_name = tcx.crate_name(def_id.krate); + let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate(); + let title = format!("{}.{} - Coverage Spans", crate_name, item_name); + spanview::write_document(tcx, def_id, span_viewables, &title, &mut file) + .expect("Unexpected IO error dumping coverage spans as HTML"); } // When debug logging, or generating the coverage graphviz output, initialize the following @@ -210,75 +230,81 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // counters. let mut collect_intermediate_expressions = Vec::with_capacity(self.basic_coverage_blocks.num_nodes()); - self.make_bcb_counters(&coverage_spans, &mut collect_intermediate_expressions); - // If debugging, add any intermediate expressions (which are not associated with any BCB) to - // the `debug_used_expressions` map. + let result = self.make_bcb_counters(&coverage_spans, &mut collect_intermediate_expressions); + if result.is_ok() { + // If debugging, add any intermediate expressions (which are not associated with any + // BCB) to the `debug_used_expressions` map. - if debug_used_expressions.is_enabled() { - for intermediate_expression in &collect_intermediate_expressions { - debug_used_expressions.add_expression_operands(intermediate_expression); + if debug_used_expressions.is_enabled() { + for intermediate_expression in &collect_intermediate_expressions { + debug_used_expressions.add_expression_operands(intermediate_expression); + } } - } - // Inject a counter for each `CoverageSpan`. - self.inject_coverage_span_counters( - coverage_spans, - &mut graphviz_data, - &mut debug_used_expressions, - ); + // Inject a counter for each `CoverageSpan`. + self.inject_coverage_span_counters( + coverage_spans, + &mut graphviz_data, + &mut debug_used_expressions, + ); - // The previous step looped through the `CoverageSpan`s and injected the counter from the - // `CoverageSpan`s `BasicCoverageBlock`, removing it from the BCB in the process (via - // `take_counter()`). - // - // Any other counter associated with a `BasicCoverageBlock`, or its incoming edge, but not - // associated with a `CoverageSpan`, should only exist if the counter is a - // `Expression` dependency (one of the expression operands). Collect them, and inject - // the additional counters into the MIR, without a reportable coverage span. - let mut bcb_counters_without_direct_coverage_spans = Vec::new(); - for (target_bcb, target_bcb_data) in self.basic_coverage_blocks.iter_enumerated_mut() { - if let Some(counter_kind) = target_bcb_data.take_counter() { - bcb_counters_without_direct_coverage_spans.push((None, target_bcb, counter_kind)); - } - if let Some(edge_counters) = target_bcb_data.take_edge_counters() { - for (from_bcb, counter_kind) in edge_counters { + // The previous step looped through the `CoverageSpan`s and injected the counter from + // the `CoverageSpan`s `BasicCoverageBlock`, removing it from the BCB in the process + // (via `take_counter()`). + // + // Any other counter associated with a `BasicCoverageBlock`, or its incoming edge, but + // not associated with a `CoverageSpan`, should only exist if the counter is a + // `Expression` dependency (one of the expression operands). Collect them, and inject + // the additional counters into the MIR, without a reportable coverage span. + let mut bcb_counters_without_direct_coverage_spans = Vec::new(); + for (target_bcb, target_bcb_data) in self.basic_coverage_blocks.iter_enumerated_mut() { + if let Some(counter_kind) = target_bcb_data.take_counter() { bcb_counters_without_direct_coverage_spans.push(( - Some(from_bcb), + None, target_bcb, counter_kind, )); } + if let Some(edge_counters) = target_bcb_data.take_edge_counters() { + for (from_bcb, counter_kind) in edge_counters { + bcb_counters_without_direct_coverage_spans.push(( + Some(from_bcb), + target_bcb, + counter_kind, + )); + } + } } - } - if debug_used_expressions.is_enabled() { - // Validate that every BCB or edge counter not directly associated with a coverage span - // is at least indirectly associated (it is a dependency of a BCB counter that _is_ - // associated with a coverage span). - let mut not_validated = bcb_counters_without_direct_coverage_spans - .iter() - .map(|(_, _, counter_kind)| counter_kind) - .collect::>(); - let mut validating_count = 0; - while not_validated.len() != validating_count { - let to_validate = not_validated.split_off(0); - validating_count = to_validate.len(); - for counter_kind in to_validate { - if debug_used_expressions.expression_is_used(counter_kind) { - debug_used_expressions.add_expression_operands(counter_kind); - } else { - not_validated.push(counter_kind); + if debug_used_expressions.is_enabled() { + // Validate that every BCB or edge counter not directly associated with a coverage + // span is at least indirectly associated (it is a dependency of a BCB counter that + // _is_ associated with a coverage span). + let mut not_validated = bcb_counters_without_direct_coverage_spans + .iter() + .map(|(_, _, counter_kind)| counter_kind) + .collect::>(); + let mut validating_count = 0; + while not_validated.len() != validating_count { + let to_validate = not_validated.split_off(0); + validating_count = to_validate.len(); + for counter_kind in to_validate { + if debug_used_expressions.expression_is_used(counter_kind) { + debug_used_expressions.add_expression_operands(counter_kind); + } else { + not_validated.push(counter_kind); + } } } } - } - self.inject_indirect_counters( - bcb_counters_without_direct_coverage_spans, - &mut graphviz_data, - &mut debug_used_expressions, - ); + self.inject_indirect_counters( + bcb_counters_without_direct_coverage_spans, + &mut graphviz_data, + &mut debug_used_expressions, + ); + } if graphviz_data.is_enabled() { let node_content = |bcb| { @@ -348,22 +374,15 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { .expect("Unexpected error writing BasicCoverageBlock graphviz DOT file"); } + result.unwrap_or_else(|e: Error| { + bug!("Error processing: {:?}: {:?}", self.mir_body.source.def_id(), e) + }); + debug_used_expressions.check_no_unused(&self.debug_counters); for intermediate_expression in collect_intermediate_expressions { self.inject_intermediate_expression(intermediate_expression); } - - if let Some(span_viewables) = debug_span_viewables { - let mut file = - pretty::create_dump_file(tcx, "html", None, self.pass_name, &0, mir_source) - .expect("Unexpected error creating MIR spanview HTML file"); - let crate_name = tcx.crate_name(def_id.krate); - let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate(); - let title = format!("{}.{} - Coverage Spans", crate_name, item_name); - spanview::write_document(tcx, def_id, span_viewables, &title, &mut file) - .expect("Unexpected IO error dumping coverage spans as HTML"); - } } /// Inject a counter for each `CoverageSpan`. There can be multiple `CoverageSpan`s for a given @@ -518,6 +537,127 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } } + #[inline] + fn format_counter(&self, counter_kind: &CoverageKind) -> String { + self.debug_counters.format_counter(counter_kind) + } + + #[inline] + fn bcb_leader_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock { + self.bcb_data(bcb).leader_bb() + } + + #[inline] + fn bcb_last_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock { + self.bcb_data(bcb).last_bb() + } + + #[inline] + fn bcb_terminator(&self, bcb: BasicCoverageBlock) -> &Terminator<'tcx> { + self.bcb_data(bcb).terminator(self.mir_body) + } + + #[inline] + fn bcb_data(&self, bcb: BasicCoverageBlock) -> &BasicCoverageBlockData { + &self.basic_coverage_blocks[bcb] + } + + #[inline] + fn bcb_data_mut(&mut self, bcb: BasicCoverageBlock) -> &mut BasicCoverageBlockData { + &mut self.basic_coverage_blocks[bcb] + } + + #[inline] + fn bcb_predecessors(&self, bcb: BasicCoverageBlock) -> &Vec { + &self.basic_coverage_blocks.predecessors[bcb] + } + + #[inline] + fn bcb_successors(&self, bcb: BasicCoverageBlock) -> &Vec { + &self.basic_coverage_blocks.successors[bcb] + } + + #[inline] + fn bcb_branches(&self, from_bcb: BasicCoverageBlock) -> Vec { + self.basic_coverage_blocks.successors[from_bcb] + .iter() + .map(|&to_bcb| BcbBranch::from_to(from_bcb, to_bcb, &self.basic_coverage_blocks)) + .collect::>() + } + + /// Returns true if the BasicCoverageBlock has zero or one incoming edge. (If zero, it should be + /// the entry point for the function.) + #[inline] + fn bcb_has_one_path_to_target(&self, bcb: BasicCoverageBlock) -> bool { + self.bcb_predecessors(bcb).len() <= 1 + } + + #[inline] + fn bcb_is_dominated_by(&self, node: BasicCoverageBlock, dom: BasicCoverageBlock) -> bool { + self.bcb_dominators.is_dominated_by(node, dom) + } + + fn bcb_to_string_sections( + &self, + bcb_data: &BasicCoverageBlockData, + some_coverage_spans_with_counters: Option<&Vec<(CoverageSpan, CoverageKind)>>, + some_dependency_counters: Option<&Vec>, + some_intermediate_expressions: Option<&Vec>, + ) -> Vec { + let len = bcb_data.basic_blocks.len(); + let mut sections = Vec::new(); + if let Some(collect_intermediate_expressions) = some_intermediate_expressions { + sections.push( + collect_intermediate_expressions + .iter() + .map(|expression| format!("Intermediate {}", self.format_counter(expression))) + .collect::>() + .join("\n"), + ); + } + if let Some(coverage_spans_with_counters) = some_coverage_spans_with_counters { + sections.push( + coverage_spans_with_counters + .iter() + .map(|(covspan, counter)| { + format!( + "{} at {}", + self.format_counter(counter), + covspan.format(self.tcx, self.mir_body) + ) + }) + .collect::>() + .join("\n"), + ); + } + if let Some(dependency_counters) = some_dependency_counters { + sections.push(format!( + "Non-coverage counters:\n {}", + dependency_counters + .iter() + .map(|counter| self.format_counter(counter)) + .collect::>() + .join(" \n"), + )); + } + if let Some(counter_kind) = &bcb_data.counter_kind { + sections.push(format!("{:?}", counter_kind)); + } + let non_term_blocks = bcb_data.basic_blocks[0..len - 1] + .iter() + .map(|&bb| format!("{:?}: {}", bb, term_type(&self.mir_body[bb].terminator().kind))) + .collect::>(); + if non_term_blocks.len() > 0 { + sections.push(non_term_blocks.join("\n")); + } + sections.push(format!( + "{:?}: {}", + bcb_data.basic_blocks.last().unwrap(), + term_type(&bcb_data.terminator(self.mir_body).kind) + )); + sections + } + /// Traverse the BCB CFG and add either a `Counter` or `Expression` to ever BCB, to be /// injected with `CoverageSpan`s. `Expressions` have no runtime overhead, so if a viable /// expression (adding or subtracting two other counters or expressions) can compute the same @@ -536,7 +676,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { &mut self, coverage_spans: &Vec, collect_intermediate_expressions: &mut Vec, - ) { + ) -> Result<(), Error> { debug!("make_bcb_counters(): adding a counter or expression to each BasicCoverageBlock"); let num_bcbs = self.basic_coverage_blocks.num_nodes(); @@ -558,73 +698,105 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { self.basic_coverage_blocks.successors[bcb].len() ); for &successor in &self.basic_coverage_blocks.successors[bcb] { + if successor == bcb { + debug!( + "{:?} has itself as its own successor. (Note, the compiled code will \ + generate an infinite loop.)", + bcb + ); + // Don't re-add this successor to the worklist. We are already processing it. + break; + } for context in traversal.context_stack.iter_mut().rev() { // Add successors of the current BCB to the appropriate context. Successors that - // stay within a loop are added to the BCBs context worklist. + // stay within a loop are added to the BCBs context worklist. Successors that + // exit the loop (they are not dominated by the loop header) must be reachable + // from other BCBs outside the loop, and they will be added to a different + // worklist. // // Branching blocks (with more than one successor) must be processed before // blocks with only one successor, to prevent unnecessarily complicating // Expression`s by creating a Counter in a `BasicCoverageBlock` that the // branching block would have given an `Expression` (or vice versa). - if let Some((_, loop_header)) = context.loop_backedges { - if self.bcb_is_dominated_by(successor, loop_header) { - if self.bcb_successors(successor).len() > 1 { - debug!( - "Adding branching successor {:?} to the beginning of the \ - worklist of loop headed by {:?}", - successor, loop_header - ); - context.worklist.insert(0, successor); + let (some_successor_to_add, some_loop_header) = + if let Some((_, loop_header)) = context.loop_backedges { + if self.bcb_is_dominated_by(successor, loop_header) { + (Some(successor), Some(loop_header)) } else { - debug!( - "Adding non-branching successor {:?} to the end of the \ - worklist of loop headed by {:?}", - successor, loop_header - ); - context.worklist.push(successor); + (None, None) } - break; - } - } else { - if self.bcb_successors(successor).len() > 1 { + } else { + (Some(successor), None) + }; + if let Some(successor_to_add) = some_successor_to_add { + if self.bcb_successors(successor_to_add).len() > 1 { debug!( - "Adding branching successor {:?} to the beginning of the non-loop \ - worklist", - successor + "{:?} successor is branching. Prioritize it at the beginning of \ + the {}", + successor_to_add, + if let Some(loop_header) = some_loop_header { + format!("worklist for the loop headed by {:?}", loop_header) + } else { + String::from("non-loop worklist") + }, ); - context.worklist.insert(0, successor); + context.worklist.insert(0, successor_to_add); } else { debug!( - "Adding non-branching successor {:?} to the end of the non-loop \ - worklist", - successor + "{:?} successor is non-branching. Defer it to the end of the {}", + successor_to_add, + if let Some(loop_header) = some_loop_header { + format!("worklist for the loop headed by {:?}", loop_header) + } else { + String::from("non-loop worklist") + }, ); - context.worklist.push(successor); + context.worklist.push(successor_to_add); } + break; } } } if !bcbs_with_coverage.contains(bcb) { + debug!( + "{:?} does not have any `CoverageSpan`s. A counter will only be added if \ + and when a covered BCB has an expression dependency.", + bcb, + ); continue; } + debug!("{:?} has at least one `CoverageSpan`. Get or make its counter", bcb); let bcb_counter_operand = - self.get_or_make_counter_operand(bcb, collect_intermediate_expressions); + self.get_or_make_counter_operand(bcb, collect_intermediate_expressions)?; + + let branch_needs_a_counter = + |branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none(); + + let branches = self.bcb_branches(bcb); + let needs_branch_counters = + branches.len() > 1 && branches.iter().any(branch_needs_a_counter); - let needs_branch_counters = { - let successors = self.bcb_successors(bcb); - successors.len() > 1 - && successors - .iter() - .any(|&successor| self.bcb_data(successor).counter().is_none()) - }; if needs_branch_counters { let branching_bcb = bcb; let branching_counter_operand = bcb_counter_operand; - let branches = self.bcb_successors(branching_bcb).clone(); - debug!("{:?} is branching, with branches: {:?}", branching_bcb, branches); + debug!( + "{:?} has some branch(es) without counters:\n {}", + branching_bcb, + branches + .iter() + .map(|branch| { + format!( + "{:?}: {:?}", + branch, + branch.counter(&self.basic_coverage_blocks) + ) + }) + .collect::>() + .join("\n "), + ); // At most one of the branches (or its edge, from the branching_bcb, // if the branch has multiple incoming edges) can have a counter computed by @@ -635,35 +807,54 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // is captured in `some_reloop_branch`), it's likely any reloop branch will be // executed far more often than loop exit branch, making the reloop branch a better // candidate for an expression. - let mut some_reloop_branch = None; + let mut some_reloop_branch: Option = None; for context in traversal.context_stack.iter().rev() { if let Some((backedge_from_bcbs, _)) = &context.loop_backedges { let mut found_loop_exit = false; for &branch in branches.iter() { if backedge_from_bcbs.iter().any(|&backedge_from_bcb| { - self.bcb_is_dominated_by(backedge_from_bcb, branch) + self.bcb_is_dominated_by(backedge_from_bcb, branch.target_bcb) }) { - // The path from branch leads back to the top of the loop + if let Some(reloop_branch) = some_reloop_branch { + if reloop_branch.counter(&self.basic_coverage_blocks).is_none() + { + // we already found a candidate reloop_branch that still + // needs a counter + continue; + } + } + // The path from branch leads back to the top of the loop. Set this + // branch as the `reloop_branch`. If this branch already has a + // counter, and we find another reloop branch that doesn't have a + // counter yet, that branch will be selected as the `reloop_branch` + // instead. some_reloop_branch = Some(branch); } else { // The path from branch leads outside this loop found_loop_exit = true; } - if some_reloop_branch.is_some() && found_loop_exit { + if found_loop_exit + && some_reloop_branch.filter(branch_needs_a_counter).is_some() + { + // Found both a branch that exits the loop and a branch that returns + // to the top of the loop (`reloop_branch`), and the `reloop_branch` + // doesn't already have a counter. break; } } - debug!( - "found_loop_exit={}, some_reloop_branch={:?}", - found_loop_exit, some_reloop_branch - ); if !found_loop_exit { - // No branches exit a loop, so there is no specific recommended branch - // for an `Expression`. + debug!( + "No branches exit the loop, so any branch without an existing \ + counter can have the `Expression`." + ); break; } if some_reloop_branch.is_some() { - // A recommended branch for an `Expression` was found. + debug!( + "Found a branch that exits the loop and a branch the loops back to \ + the top of the loop (`reloop_branch`). The `reloop_branch` will \ + get the `Expression`, as long as it still needs a counter." + ); break; } // else all branches exited this loop context, so run the same checks with @@ -673,20 +864,27 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // Select a branch for the expression, either the recommended `reloop_branch`, or // if none was found, select any branch. - let expression_branch = if let Some(reloop_branch) = some_reloop_branch { - debug!("Adding expression to reloop_branch={:?}", reloop_branch); - reloop_branch + let expression_branch = if let Some(reloop_branch_without_counter) = + some_reloop_branch.filter(branch_needs_a_counter) + { + debug!( + "Selecting reloop_branch={:?} that still needs a counter, to get the \ + `Expression`", + reloop_branch_without_counter + ); + reloop_branch_without_counter } else { let &branch_without_counter = branches .iter() - .find(|&&branch| self.bcb_data(branch).counter().is_none()) + .find(|&&branch| branch.counter(&self.basic_coverage_blocks).is_none()) .expect( "needs_branch_counters was `true` so there should be at least one \ branch", ); debug!( - "No preferred expression branch. Selected the first branch without a \ - counter. That branch={:?}", + "Selecting any branch={:?} that still needs a counter, to get the \ + `Expression` because there was no `reloop_branch`, or it already had a \ + counter", branch_without_counter ); branch_without_counter @@ -697,27 +895,26 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { let mut some_sumup_counter_operand = None; for branch in branches { if branch != expression_branch { - let branch_counter_operand = if self.bcb_has_multiple_incoming_edges(branch) - { + let branch_counter_operand = if branch.is_only_path_to_target() { debug!( - "{:?} has multiple incoming edges, so adding an edge counter from \ - {:?}", + " {:?} has only one incoming edge (from {:?}), so adding a \ + counter", branch, branching_bcb ); - self.get_or_make_edge_counter_operand( - branching_bcb, - branch, + self.get_or_make_counter_operand( + branch.target_bcb, collect_intermediate_expressions, - ) + )? } else { debug!( - "{:?} has only one incoming edge (from {:?}), so adding a counter", - branch, branching_bcb + " {:?} has multiple incoming edges, so adding an edge counter", + branch ); - self.get_or_make_counter_operand( - branch, + self.get_or_make_edge_counter_operand( + branching_bcb, + branch.target_bcb, collect_intermediate_expressions, - ) + )? }; if let Some(sumup_counter_operand) = some_sumup_counter_operand.replace(branch_counter_operand) @@ -729,7 +926,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { || None, ); debug!( - " new intermediate expression: {}", + " [new intermediate expression: {}]", self.format_counter(&intermediate_expression) ); let intermediate_expression_operand = @@ -741,219 +938,148 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } let sumup_counter_operand = some_sumup_counter_operand.expect("sumup_counter_operand should have a value"); - let multiple_incoming_edges = - self.bcb_has_multiple_incoming_edges(expression_branch); debug!( - "expression_branch is {:?}, multiple_incoming_edges={}, expression_branch \ - predecessors: {:?}", + "Making an expression for the selected expression_branch: {:?} \ + (expression_branch predecessors: {:?})", expression_branch, - multiple_incoming_edges, - self.bcb_predecessors(expression_branch) + self.bcb_predecessors(expression_branch.target_bcb), ); let expression = self.make_expression( branching_counter_operand, Op::Subtract, sumup_counter_operand, - || { - Some(if multiple_incoming_edges { - format!("{:?}->{:?}", branching_bcb, expression_branch) - } else { - format!("{:?}", expression_branch) - }) - }, + || Some(format!("{:?}", expression_branch)), ); - if multiple_incoming_edges { - debug!( - "Edge {:?}->{:?} gets an expression: {}", - branching_bcb, - expression_branch, - self.format_counter(&expression) - ); - self.bcb_data_mut(expression_branch) - .set_edge_counter_from(branching_bcb, expression); + debug!( + "{:?} gets an expression: {}", + expression_branch, + self.format_counter(&expression) + ); + if expression_branch.is_only_path_to_target() { + self.bcb_data_mut(expression_branch.target_bcb).set_counter(expression)?; } else { - debug!( - "{:?} gets an expression: {}", - expression_branch, - self.format_counter(&expression) - ); - self.bcb_data_mut(expression_branch).set_counter(expression); + self.bcb_data_mut(expression_branch.target_bcb) + .set_edge_counter_from(branching_bcb, expression)?; } } } - } - - #[inline] - fn format_counter(&self, counter_kind: &CoverageKind) -> String { - self.debug_counters.format_counter(counter_kind) - } - - #[inline] - fn bcb_leader_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock { - self.bcb_data(bcb).leader_bb() - } - - #[inline] - fn bcb_last_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock { - self.bcb_data(bcb).last_bb() - } - - #[inline] - fn bcb_terminator(&self, bcb: BasicCoverageBlock) -> &Terminator<'tcx> { - self.bcb_data(bcb).terminator(self.mir_body) - } - - #[inline] - fn bcb_data(&self, bcb: BasicCoverageBlock) -> &BasicCoverageBlockData { - &self.basic_coverage_blocks[bcb] - } - #[inline] - fn bcb_data_mut(&mut self, bcb: BasicCoverageBlock) -> &mut BasicCoverageBlockData { - &mut self.basic_coverage_blocks[bcb] - } - - #[inline] - fn bcb_predecessors(&self, bcb: BasicCoverageBlock) -> &Vec { - &self.basic_coverage_blocks.predecessors[bcb] - } - - #[inline] - fn bcb_successors(&self, bcb: BasicCoverageBlock) -> &Vec { - &self.basic_coverage_blocks.successors[bcb] - } - - #[inline] - fn bcb_has_multiple_incoming_edges(&self, bcb: BasicCoverageBlock) -> bool { - self.bcb_predecessors(bcb).len() > 1 - } - - #[inline] - fn bcb_is_dominated_by(&self, node: BasicCoverageBlock, dom: BasicCoverageBlock) -> bool { - self.bcb_dominators.is_dominated_by(node, dom) - } - - fn bcb_to_string_sections( - &self, - bcb_data: &BasicCoverageBlockData, - some_coverage_spans_with_counters: Option<&Vec<(CoverageSpan, CoverageKind)>>, - some_dependency_counters: Option<&Vec>, - some_intermediate_expressions: Option<&Vec>, - ) -> Vec { - let len = bcb_data.basic_blocks.len(); - let mut sections = Vec::new(); - if let Some(collect_intermediate_expressions) = some_intermediate_expressions { - sections.push( - collect_intermediate_expressions - .iter() - .map(|expression| format!("Intermediate {}", self.format_counter(expression))) - .collect::>() - .join("\n"), - ); - } - if let Some(coverage_spans_with_counters) = some_coverage_spans_with_counters { - sections.push( - coverage_spans_with_counters - .iter() - .map(|(covspan, counter)| { - format!( - "{} at {}", - self.format_counter(counter), - covspan.format(self.tcx, self.mir_body) - ) - }) - .collect::>() - .join("\n"), - ); - } - if let Some(dependency_counters) = some_dependency_counters { - sections.push(format!( - "Non-coverage counters:\n {}", - dependency_counters - .iter() - .map(|counter| self.format_counter(counter)) - .collect::>() - .join(" \n"), - )); - } - if let Some(counter_kind) = &bcb_data.counter_kind { - sections.push(format!("{:?}", counter_kind)); - } - let non_term_blocks = bcb_data.basic_blocks[0..len - 1] - .iter() - .map(|&bb| format!("{:?}: {}", bb, term_type(&self.mir_body[bb].terminator().kind))) - .collect::>(); - if non_term_blocks.len() > 0 { - sections.push(non_term_blocks.join("\n")); + if traversal.is_complete() { + Ok(()) + } else { + Error::from_string(format!( + "`TraverseCoverageGraphWithLoops` missed some `BasicCoverageBlock`s: {:?}", + traversal.unvisited(), + )) } - sections.push(format!( - "{:?}: {}", - bcb_data.basic_blocks.last().unwrap(), - term_type(&bcb_data.terminator(self.mir_body).kind) - )); - sections } fn get_or_make_counter_operand( &mut self, bcb: BasicCoverageBlock, collect_intermediate_expressions: &mut Vec, - ) -> ExpressionOperandId { - if let Some(counter_kind) = self.basic_coverage_blocks[bcb].counter() { - debug!(" {:?} already has a counter: {}", bcb, self.format_counter(counter_kind)); - counter_kind.as_operand_id() - } else { - if self.bcb_has_multiple_incoming_edges(bcb) { - let mut predecessors = self.bcb_predecessors(bcb).clone().into_iter(); - let first_edge_counter_operand = self.get_or_make_edge_counter_operand( - predecessors.next().unwrap(), + ) -> Result { + self.recursive_get_or_make_counter_operand(bcb, collect_intermediate_expressions, 1) + } + + fn recursive_get_or_make_counter_operand( + &mut self, + bcb: BasicCoverageBlock, + collect_intermediate_expressions: &mut Vec, + debug_indent_level: usize, + ) -> Result { + Ok({ + if let Some(counter_kind) = self.basic_coverage_blocks[bcb].counter() { + debug!( + "{}{:?} already has a counter: {}", + NESTED_INDENT.repeat(debug_indent_level), bcb, - collect_intermediate_expressions, + self.format_counter(counter_kind), ); - let mut some_sumup_edge_counter_operand = None; - for predecessor in predecessors { - let edge_counter_operand = self.get_or_make_edge_counter_operand( - predecessor, - bcb, - collect_intermediate_expressions, - ); - if let Some(sumup_edge_counter_operand) = - some_sumup_edge_counter_operand.replace(edge_counter_operand) - { - let intermediate_expression = self.make_expression( - sumup_edge_counter_operand, - Op::Add, - edge_counter_operand, - || None, + counter_kind.as_operand_id() + } else { + let one_path_to_target = self.bcb_has_one_path_to_target(bcb); + if one_path_to_target || self.bcb_predecessors(bcb).contains(&bcb) { + let counter_kind = self.make_counter(|| Some(format!("{:?}", bcb))); + if one_path_to_target { + debug!( + "{}{:?} gets a new counter: {}", + NESTED_INDENT.repeat(debug_indent_level), + bcb, + self.format_counter(&counter_kind), ); + } else { debug!( - " new intermediate expression: {}", - self.format_counter(&intermediate_expression) + "{}{:?} has itself as its own predecessor. It can't be part of its own \ + Expression sum, so it will get its own new counter: {}. (Note, the \ + compiled code will generate an infinite loop.)", + NESTED_INDENT.repeat(debug_indent_level), + bcb, + self.format_counter(&counter_kind), ); - let intermediate_expression_operand = - intermediate_expression.as_operand_id(); - collect_intermediate_expressions.push(intermediate_expression); - some_sumup_edge_counter_operand.replace(intermediate_expression_operand); } + self.basic_coverage_blocks[bcb].set_counter(counter_kind)? + } else { + let mut predecessors = self.bcb_predecessors(bcb).clone().into_iter(); + debug!( + "{}{:?} has multiple incoming edges and will get an expression that sums \ + them up...", + NESTED_INDENT.repeat(debug_indent_level), + bcb, + ); + let first_edge_counter_operand = self + .recursive_get_or_make_edge_counter_operand( + predecessors.next().unwrap(), + bcb, + collect_intermediate_expressions, + debug_indent_level + 1, + )?; + let mut some_sumup_edge_counter_operand = None; + for predecessor in predecessors { + let edge_counter_operand = self + .recursive_get_or_make_edge_counter_operand( + predecessor, + bcb, + collect_intermediate_expressions, + debug_indent_level + 1, + )?; + if let Some(sumup_edge_counter_operand) = + some_sumup_edge_counter_operand.replace(edge_counter_operand) + { + let intermediate_expression = self.make_expression( + sumup_edge_counter_operand, + Op::Add, + edge_counter_operand, + || None, + ); + debug!( + "{}new intermediate expression: {}", + NESTED_INDENT.repeat(debug_indent_level), + self.format_counter(&intermediate_expression) + ); + let intermediate_expression_operand = + intermediate_expression.as_operand_id(); + collect_intermediate_expressions.push(intermediate_expression); + some_sumup_edge_counter_operand + .replace(intermediate_expression_operand); + } + } + let counter_kind = self.make_expression( + first_edge_counter_operand, + Op::Add, + some_sumup_edge_counter_operand.unwrap(), + || Some(format!("{:?}", bcb)), + ); + debug!( + "{}{:?} gets a new counter (sum of predecessor counters): {}", + NESTED_INDENT.repeat(debug_indent_level), + bcb, + self.format_counter(&counter_kind) + ); + self.basic_coverage_blocks[bcb].set_counter(counter_kind)? } - let counter_kind = self.make_expression( - first_edge_counter_operand, - Op::Add, - some_sumup_edge_counter_operand.unwrap(), - || Some(format!("{:?}", bcb)), - ); - debug!( - " {:?} gets a new counter (sum of predecessor counters): {}", - bcb, - self.format_counter(&counter_kind) - ); - self.basic_coverage_blocks[bcb].set_counter(counter_kind) - } else { - let counter_kind = self.make_counter(|| Some(format!("{:?}", bcb))); - debug!(" {:?} gets a new counter: {}", bcb, self.format_counter(&counter_kind)); - self.basic_coverage_blocks[bcb].set_counter(counter_kind) } - } + }) } fn get_or_make_edge_counter_operand( @@ -961,33 +1087,57 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock, collect_intermediate_expressions: &mut Vec, - ) -> ExpressionOperandId { - let successors = self.bcb_successors(from_bcb).iter(); - if successors.len() > 1 { - if let Some(counter_kind) = - self.basic_coverage_blocks[to_bcb].edge_counter_from(from_bcb) - { - debug!( - " Edge {:?}->{:?} already has a counter: {}", - from_bcb, - to_bcb, - self.format_counter(counter_kind) - ); - counter_kind.as_operand_id() + ) -> Result { + self.recursive_get_or_make_edge_counter_operand( + from_bcb, + to_bcb, + collect_intermediate_expressions, + 1, + ) + } + + fn recursive_get_or_make_edge_counter_operand( + &mut self, + from_bcb: BasicCoverageBlock, + to_bcb: BasicCoverageBlock, + collect_intermediate_expressions: &mut Vec, + debug_indent_level: usize, + ) -> Result { + Ok({ + let successors = self.bcb_successors(from_bcb).iter(); + if successors.len() > 1 { + if let Some(counter_kind) = + self.basic_coverage_blocks[to_bcb].edge_counter_from(from_bcb) + { + debug!( + "{}Edge {:?}->{:?} already has a counter: {}", + NESTED_INDENT.repeat(debug_indent_level), + from_bcb, + to_bcb, + self.format_counter(counter_kind) + ); + counter_kind.as_operand_id() + } else { + let counter_kind = + self.make_counter(|| Some(format!("{:?}->{:?}", from_bcb, to_bcb))); + debug!( + "{}Edge {:?}->{:?} gets a new counter: {}", + NESTED_INDENT.repeat(debug_indent_level), + from_bcb, + to_bcb, + self.format_counter(&counter_kind) + ); + self.basic_coverage_blocks[to_bcb] + .set_edge_counter_from(from_bcb, counter_kind)? + } } else { - let counter_kind = - self.make_counter(|| Some(format!("{:?}->{:?}", from_bcb, to_bcb))); - debug!( - " Edge {:?}->{:?} gets a new counter: {}", + self.recursive_get_or_make_counter_operand( from_bcb, - to_bcb, - self.format_counter(&counter_kind) - ); - self.basic_coverage_blocks[to_bcb].set_edge_counter_from(from_bcb, counter_kind) + collect_intermediate_expressions, + debug_indent_level + 1, + )? } - } else { - self.get_or_make_counter_operand(from_bcb, collect_intermediate_expressions) - } + }) } fn make_counter(&mut self, block_label_fn: F) -> CoverageKind diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.loops_and_branches.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.loops_and_branches.json new file mode 100644 index 0000000000000..cb74a1b7e2716 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.loops_and_branches.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../coverage/loops_and_branches.rs", + "summary": { + "functions": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "instantiations": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "lines": { + "count": 11, + "covered": 11, + "percent": 100 + }, + "regions": { + "count": 9, + "covered": 8, + "notcovered": 1, + "percent": 88.88888888888889 + } + } + } + ], + "totals": { + "functions": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "instantiations": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "lines": { + "count": 11, + "covered": 11, + "percent": 100 + }, + "regions": { + "count": 9, + "covered": 8, + "notcovered": 1, + "percent": 88.88888888888889 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.nested_loops.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.nested_loops.json new file mode 100644 index 0000000000000..68163eb763619 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.nested_loops.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../coverage/nested_loops.rs", + "summary": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 21, + "covered": 16, + "percent": 76.19047619047619 + }, + "regions": { + "count": 18, + "covered": 14, + "notcovered": 4, + "percent": 77.77777777777779 + } + } + } + ], + "totals": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 21, + "covered": 16, + "percent": 76.19047619047619 + }, + "regions": { + "count": 18, + "covered": 14, + "notcovered": 4, + "percent": 77.77777777777779 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.partial_eq_counter_without_region.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.partial_eq_counter_without_region.json new file mode 100644 index 0000000000000..8081fba9e22ee --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.partial_eq_counter_without_region.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../coverage/partial_eq_counter_without_region.rs", + "summary": { + "functions": { + "count": 5, + "covered": 5, + "percent": 100 + }, + "instantiations": { + "count": 5, + "covered": 5, + "percent": 100 + }, + "lines": { + "count": 15, + "covered": 15, + "percent": 100 + }, + "regions": { + "count": 6, + "covered": 6, + "notcovered": 0, + "percent": 100 + } + } + } + ], + "totals": { + "functions": { + "count": 5, + "covered": 5, + "percent": 100 + }, + "instantiations": { + "count": 5, + "covered": 5, + "percent": 100 + }, + "lines": { + "count": 15, + "covered": 15, + "percent": 100 + }, + "regions": { + "count": 6, + "covered": 6, + "notcovered": 0, + "percent": 100 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.tight_infinite_loop.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.tight_infinite_loop.json new file mode 100644 index 0000000000000..3fa6821cd1df9 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.tight_infinite_loop.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../coverage/tight_infinite_loop.rs", + "summary": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "regions": { + "count": 2, + "covered": 2, + "notcovered": 0, + "percent": 100 + } + } + } + ], + "totals": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "regions": { + "count": 2, + "covered": 2, + "notcovered": 0, + "percent": 100 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while.json new file mode 100644 index 0000000000000..339c65556682a --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../coverage/while.rs", + "summary": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 4, + "covered": 3, + "percent": 75 + }, + "regions": { + "count": 4, + "covered": 3, + "notcovered": 1, + "percent": 75 + } + } + } + ], + "totals": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 4, + "covered": 3, + "percent": 75 + }, + "regions": { + "count": 4, + "covered": 3, + "notcovered": 1, + "percent": 75 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.loops_and_branches.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.loops_and_branches.txt new file mode 100644 index 0000000000000..3a969a6b89869 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.loops_and_branches.txt @@ -0,0 +1,38 @@ + 1| |#![allow(unused_assignments)] + 2| | + 3| |// This test confirms an earlier problem was resolved, supporting the MIR graph generated by the + 4| |// structure of this `fmt` function. + 5| | + 6| |struct DebugTest; + 7| | + 8| |impl std::fmt::Debug for DebugTest { + 9| | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + 10| 1| if true { + 11| 1| if false { + 12| | while true { + 13| | } + 14| 1| } + 15| 1| write!(f, "error")?; + ^0 + 16| | } else { + 17| 1| } + 18| 1| Ok(()) + 19| 1| } + 20| |} + 21| | + 22| 1|fn main() { + 23| 1| let debug_test = DebugTest; + 24| 1| println!("{:?}", debug_test); + 25| 1|} + 26| | + 27| |/* + 28| | + 29| |This is the error message generated, before the issue was fixed: + 30| | + 31| |error: internal compiler error: compiler/rustc_mir/src/transform/coverage/mod.rs:374:42: + 32| |Error processing: DefId(0:6 ~ bug_incomplete_cov_graph_traversal_simplified[317d]::{impl#0}::fmt): + 33| |Error { message: "`TraverseCoverageGraphWithLoops` missed some `BasicCoverageBlock`s: + 34| |[bcb6, bcb7, bcb9]" } + 35| | + 36| |*/ + diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.nested_loops.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.nested_loops.txt new file mode 100644 index 0000000000000..c9f373bf6a7c0 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.nested_loops.txt @@ -0,0 +1,26 @@ + 1| |fn main() { + 2| 1| let is_true = std::env::args().len() == 1; + 3| 1| let mut countdown = 10; + 4| | + 5| 1| 'outer: while countdown > 0 { + 6| 1| let mut a = 100; + 7| 1| let mut b = 100; + 8| 3| for _ in 0..50 { + 9| 3| if a < 30 { + 10| 0| break; + 11| | } + 12| 3| a -= 5; + 13| 3| b -= 5; + 14| 3| if b < 90 { + 15| 1| a -= 10; + 16| 1| if is_true { + 17| 1| break 'outer; + 18| | } else { + 19| 0| a -= 2; + 20| 0| } + 21| 2| } + 22| 2| } + 23| 0| countdown -= 1; + 24| 0| } + 25| 1|} + diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.partial_eq_counter_without_region.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.partial_eq_counter_without_region.txt new file mode 100644 index 0000000000000..310bf13a695af --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.partial_eq_counter_without_region.txt @@ -0,0 +1,101 @@ + 1| |// This test confirms an earlier problem was resolved, supporting the MIR graph generated by the + 2| |// structure of this test. + 3| | + 4| 2|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + ^1 ^1 + 5| |pub struct Version { + 6| | major: usize, + 7| 1| minor: usize, + 8| | patch: usize, + 9| |} + 10| | + 11| |impl Version { + 12| | pub fn new(major: usize, minor: usize, patch: usize) -> Self { + 13| 2| Self { + 14| 2| major, + 15| 2| minor, + 16| 2| patch, + 17| 2| } + 18| 2| } + 19| |} + 20| | + 21| 1|fn main() { + 22| 1| let version_3_2_1 = Version::new(3, 2, 1); + 23| 1| let version_3_3_0 = Version::new(3, 3, 0); + 24| 1| + 25| 1| println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version_3_3_0); + 26| 1|} + 27| | + 28| |/* + 29| | + 30| |This test verifies a bug was fixed that otherwise generated this error: + 31| | + 32| |thread 'rustc' panicked at 'No counters provided the source_hash for function: + 33| | Instance { + 34| | def: Item(WithOptConstParam { + 35| | did: DefId(0:101 ~ autocfg[c44a]::version::{impl#2}::partial_cmp), + 36| | const_param_did: None + 37| | }), + 38| | substs: [] + 39| | }' + 40| |The `PartialOrd` derived by `Version` happened to generate a MIR that generated coverage + 41| |without a code region associated with any `Counter`. Code regions were associated with at least + 42| |one expression, which is allowed, but the `function_source_hash` was only passed to the codegen + 43| |(coverage mapgen) phase from a `Counter`s code region. A new method was added to pass the + 44| |`function_source_hash` without a code region, if necessary. + 45| | + 46| |*/ + 47| | + 48| |// FIXME(richkadel): It may be worth investigating why the coverage report for this test produces + 49| |// the following results: + 50| | + 51| |/* + 52| | + 53| |1. Why are their two counts below different characters (first and last) of `PartialOrd`, on line 17? + 54| | + 55| |2. Line 17 is counted twice, but the `::lt` instance shows a line count of 1? Is there a missing + 56| | line count with a different instance? Or was it really only called once? + 57| | + 58| |3. Line 20 shows another line count (of 1) for a line within a `struct` declaration (on only one of + 59| | its 3 fields). I doubt the specific field (`minor`) is relevant, but rather I suspect there's a + 60| | problem computing the file position here, for some reason. + 61| | + 62| | + 63| | 16| | + 64| | 17| 2|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + 65| | ^1 ^1 + 66| |------------------ + 67| ||Unexecuted instantiation: ::gt + 68| |------------------ + 69| ||Unexecuted instantiation: ::le + 70| |------------------ + 71| ||Unexecuted instantiation: ::ge + 72| |------------------ + 73| ||::lt: + 74| || 17| 1|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + 75| |------------------ + 76| | 18| |pub struct Version { + 77| | 19| | major: usize, + 78| | 20| 1| minor: usize, + 79| | 21| | patch: usize, + 80| | 22| |} + 81| | 23| | + 82| | 24| |impl Version { + 83| | 25| | pub fn new(major: usize, minor: usize, patch: usize) -> Self { + 84| | 26| 2| Version { + 85| | 27| 2| major, + 86| | 28| 2| minor, + 87| | 29| 2| patch, + 88| | 30| 2| } + 89| | 31| 2| } + 90| | 32| |} + 91| | 33| | + 92| | 34| 1|fn main() { + 93| | 35| 1| let version_3_2_1 = Version::new(3, 2, 1); + 94| | 36| 1| let version_3_3_0 = Version::new(3, 3, 0); + 95| | 37| 1| + 96| | 38| 1| println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version + 97| |_3_3_0); + 98| | 39| 1|} + 99| |*/ + diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.tight_infinite_loop.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.tight_infinite_loop.txt new file mode 100644 index 0000000000000..e02eac03a6b15 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.tight_infinite_loop.txt @@ -0,0 +1,6 @@ + 1| |fn main() { + 2| 1| if false { + 3| | loop {} + 4| | } + 5| 1|} + diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while.txt new file mode 100644 index 0000000000000..194325b6b9eca --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while.txt @@ -0,0 +1,6 @@ + 1| |fn main() { + 2| 1| let num = 9; + 3| 1| while num >= 10 { + 4| 0| } + 5| 1|} + diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.loops_and_branches.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.loops_and_branches.txt new file mode 100644 index 0000000000000..171b77c9cc749 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.loops_and_branches.txt @@ -0,0 +1,38 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/loops_and_branches.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/loops_and_branches +Counter in file 0 10:12 -> 10:16, #1 +Counter in file 0 11:16 -> 11:21, #2 +Counter in file 0 14:14 -> 14:15, (#2 - #5) +Counter in file 0 15:13 -> 15:31, (0 + (#2 - #5)) +Counter in file 0 15:31 -> 15:32, #4 +Counter in file 0 17:10 -> 17:11, #3 +Counter in file 0 18:9 -> 18:15, (#3 + 0) +Counter in file 0 19:5 -> 19:6, (#4 + (#3 + 0)) +Counter in file 0 22:11 -> 25:2, #1 +Emitting segments for file: ../coverage/loops_and_branches.rs +Combined regions: + 10:12 -> 10:16 (count=1) + 11:16 -> 11:21 (count=1) + 14:14 -> 14:15 (count=1) + 15:13 -> 15:31 (count=1) + 15:31 -> 15:32 (count=0) + 17:10 -> 17:11 (count=1) + 18:9 -> 18:15 (count=1) + 19:5 -> 19:6 (count=1) + 22:11 -> 25:2 (count=1) +Segment at 10:12 (count = 1), RegionEntry +Segment at 10:16 (count = 0), Skipped +Segment at 11:16 (count = 1), RegionEntry +Segment at 11:21 (count = 0), Skipped +Segment at 14:14 (count = 1), RegionEntry +Segment at 14:15 (count = 0), Skipped +Segment at 15:13 (count = 1), RegionEntry +Segment at 15:31 (count = 0), RegionEntry +Segment at 15:32 (count = 0), Skipped +Segment at 17:10 (count = 1), RegionEntry +Segment at 17:11 (count = 0), Skipped +Segment at 18:9 (count = 1), RegionEntry +Segment at 18:15 (count = 0), Skipped +Segment at 19:5 (count = 1), RegionEntry +Segment at 19:6 (count = 0), Skipped +Segment at 22:11 (count = 1), RegionEntry +Segment at 25:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.nested_loops.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.nested_loops.txt new file mode 100644 index 0000000000000..ac376c4f36a40 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.nested_loops.txt @@ -0,0 +1,74 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/nested_loops.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/nested_loops +Counter in file 0 2:9 -> 3:27, #1 +Counter in file 0 5:19 -> 5:32, (#1 + #2) +Counter in file 0 6:13 -> 7:24, ((#1 + #2) - #3) +Counter in file 0 8:13 -> 8:14, ((((#1 + #2) - #3) + (#5 + #6)) - #7) +Counter in file 0 8:18 -> 8:23, (((#1 + #2) - #3) + (#5 + #6)) +Counter in file 0 9:16 -> 9:22, (((((#1 + #2) - #3) + (#5 + #6)) - #7) + 0) +Counter in file 0 10:17 -> 10:22, #8 +Counter in file 0 12:13 -> 12:19, #9 +Counter in file 0 13:13 -> 13:19, #10 +Counter in file 0 14:16 -> 14:22, (#10 + 0) +Counter in file 0 15:17 -> 16:27, #11 +Counter in file 0 17:21 -> 17:33, #4 +Counter in file 0 19:21 -> 21:14, #5 +Counter in file 0 21:14 -> 21:15, #6 +Counter in file 0 22:10 -> 22:11, (#5 + #6) +Counter in file 0 23:9 -> 23:23, #2 +Counter in file 0 24:6 -> 24:7, #3 +Counter in file 0 25:1 -> 25:2, (#4 + #3) +Emitting segments for file: ../coverage/nested_loops.rs +Combined regions: + 2:9 -> 3:27 (count=1) + 5:19 -> 5:32 (count=1) + 6:13 -> 7:24 (count=1) + 8:13 -> 8:14 (count=3) + 8:18 -> 8:23 (count=3) + 9:16 -> 9:22 (count=3) + 10:17 -> 10:22 (count=0) + 12:13 -> 12:19 (count=3) + 13:13 -> 13:19 (count=3) + 14:16 -> 14:22 (count=3) + 15:17 -> 16:27 (count=1) + 17:21 -> 17:33 (count=1) + 19:21 -> 21:14 (count=0) + 21:14 -> 21:15 (count=2) + 22:10 -> 22:11 (count=2) + 23:9 -> 23:23 (count=0) + 24:6 -> 24:7 (count=0) + 25:1 -> 25:2 (count=1) +Segment at 2:9 (count = 1), RegionEntry +Segment at 3:27 (count = 0), Skipped +Segment at 5:19 (count = 1), RegionEntry +Segment at 5:32 (count = 0), Skipped +Segment at 6:13 (count = 1), RegionEntry +Segment at 7:24 (count = 0), Skipped +Segment at 8:13 (count = 3), RegionEntry +Segment at 8:14 (count = 0), Skipped +Segment at 8:18 (count = 3), RegionEntry +Segment at 8:23 (count = 0), Skipped +Segment at 9:16 (count = 3), RegionEntry +Segment at 9:22 (count = 0), Skipped +Segment at 10:17 (count = 0), RegionEntry +Segment at 10:22 (count = 0), Skipped +Segment at 12:13 (count = 3), RegionEntry +Segment at 12:19 (count = 0), Skipped +Segment at 13:13 (count = 3), RegionEntry +Segment at 13:19 (count = 0), Skipped +Segment at 14:16 (count = 3), RegionEntry +Segment at 14:22 (count = 0), Skipped +Segment at 15:17 (count = 1), RegionEntry +Segment at 16:27 (count = 0), Skipped +Segment at 17:21 (count = 1), RegionEntry +Segment at 17:33 (count = 0), Skipped +Segment at 19:21 (count = 0), RegionEntry +Segment at 21:14 (count = 2), RegionEntry +Segment at 21:15 (count = 0), Skipped +Segment at 22:10 (count = 2), RegionEntry +Segment at 22:11 (count = 0), Skipped +Segment at 23:9 (count = 0), RegionEntry +Segment at 23:23 (count = 0), Skipped +Segment at 24:6 (count = 0), RegionEntry +Segment at 24:7 (count = 0), Skipped +Segment at 25:1 (count = 1), RegionEntry +Segment at 25:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.partial_eq_counter_without_region.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.partial_eq_counter_without_region.txt new file mode 100644 index 0000000000000..d2a68ab32022d --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.partial_eq_counter_without_region.txt @@ -0,0 +1,28 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/partial_eq_counter_without_region.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/partial_eq_counter_without_region +Counter in file 0 7:5 -> 7:6, #1 +Counter in file 0 21:11 -> 26:2, #1 +Counter in file 0 4:17 -> 4:22, #1 +Counter in file 0 13:9 -> 18:6, #1 +Counter in file 0 4:39 -> 4:40, #1 +Counter in file 0 4:48 -> 4:49, (#1 + 0) +Counter in file 0 8:5 -> 8:17, #1 +Emitting segments for file: ../coverage/partial_eq_counter_without_region.rs +Combined regions: + 4:17 -> 4:22 (count=2) + 4:39 -> 4:40 (count=1) + 4:48 -> 4:49 (count=1) + 7:5 -> 7:6 (count=1) + 13:9 -> 18:6 (count=2) + 21:11 -> 26:2 (count=1) +Segment at 4:17 (count = 2), RegionEntry +Segment at 4:22 (count = 0), Skipped +Segment at 4:39 (count = 1), RegionEntry +Segment at 4:40 (count = 0), Skipped +Segment at 4:48 (count = 1), RegionEntry +Segment at 4:49 (count = 0), Skipped +Segment at 7:5 (count = 1), RegionEntry +Segment at 7:6 (count = 0), Skipped +Segment at 13:9 (count = 2), RegionEntry +Segment at 18:6 (count = 0), Skipped +Segment at 21:11 (count = 1), RegionEntry +Segment at 26:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.tight_infinite_loop.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.tight_infinite_loop.txt new file mode 100644 index 0000000000000..5180ddd7de669 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.tight_infinite_loop.txt @@ -0,0 +1,11 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/tight_infinite_loop.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/tight_infinite_loop +Counter in file 0 2:8 -> 2:13, #1 +Counter in file 0 5:1 -> 5:2, (#1 - #2) +Emitting segments for file: ../coverage/tight_infinite_loop.rs +Combined regions: + 2:8 -> 2:13 (count=1) + 5:1 -> 5:2 (count=1) +Segment at 2:8 (count = 1), RegionEntry +Segment at 2:13 (count = 0), Skipped +Segment at 5:1 (count = 1), RegionEntry +Segment at 5:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while.txt new file mode 100644 index 0000000000000..d496805955f78 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while.txt @@ -0,0 +1,19 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/while.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-base/coverage-reports-base/while +Counter in file 0 2:9 -> 2:16, #1 +Counter in file 0 3:11 -> 3:20, (#1 + #2) +Counter in file 0 3:21 -> 4:6, #2 +Counter in file 0 5:1 -> 5:2, ((#1 + #2) - #2) +Emitting segments for file: ../coverage/while.rs +Combined regions: + 2:9 -> 2:16 (count=1) + 3:11 -> 3:20 (count=1) + 3:21 -> 4:6 (count=0) + 5:1 -> 5:2 (count=1) +Segment at 2:9 (count = 1), RegionEntry +Segment at 2:16 (count = 0), Skipped +Segment at 3:11 (count = 1), RegionEntry +Segment at 3:20 (count = 0), Skipped +Segment at 3:21 (count = 0), RegionEntry +Segment at 4:6 (count = 0), Skipped +Segment at 5:1 (count = 1), RegionEntry +Segment at 5:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.loops_and_branches.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.loops_and_branches.json new file mode 100644 index 0000000000000..cb74a1b7e2716 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.loops_and_branches.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../coverage/loops_and_branches.rs", + "summary": { + "functions": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "instantiations": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "lines": { + "count": 11, + "covered": 11, + "percent": 100 + }, + "regions": { + "count": 9, + "covered": 8, + "notcovered": 1, + "percent": 88.88888888888889 + } + } + } + ], + "totals": { + "functions": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "instantiations": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "lines": { + "count": 11, + "covered": 11, + "percent": 100 + }, + "regions": { + "count": 9, + "covered": 8, + "notcovered": 1, + "percent": 88.88888888888889 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.nested_loops.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.nested_loops.json new file mode 100644 index 0000000000000..68163eb763619 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.nested_loops.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../coverage/nested_loops.rs", + "summary": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 21, + "covered": 16, + "percent": 76.19047619047619 + }, + "regions": { + "count": 18, + "covered": 14, + "notcovered": 4, + "percent": 77.77777777777779 + } + } + } + ], + "totals": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 21, + "covered": 16, + "percent": 76.19047619047619 + }, + "regions": { + "count": 18, + "covered": 14, + "notcovered": 4, + "percent": 77.77777777777779 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.partial_eq_counter_without_region.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.partial_eq_counter_without_region.json new file mode 100644 index 0000000000000..779bfe9904ced --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.partial_eq_counter_without_region.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../coverage/partial_eq_counter_without_region.rs", + "summary": { + "functions": { + "count": 5, + "covered": 5, + "percent": 100 + }, + "instantiations": { + "count": 8, + "covered": 5, + "percent": 62.5 + }, + "lines": { + "count": 15, + "covered": 15, + "percent": 100 + }, + "regions": { + "count": 6, + "covered": 6, + "notcovered": 0, + "percent": 100 + } + } + } + ], + "totals": { + "functions": { + "count": 5, + "covered": 5, + "percent": 100 + }, + "instantiations": { + "count": 8, + "covered": 5, + "percent": 62.5 + }, + "lines": { + "count": 15, + "covered": 15, + "percent": 100 + }, + "regions": { + "count": 6, + "covered": 6, + "notcovered": 0, + "percent": 100 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.tight_infinite_loop.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.tight_infinite_loop.json new file mode 100644 index 0000000000000..3fa6821cd1df9 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.tight_infinite_loop.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../coverage/tight_infinite_loop.rs", + "summary": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "regions": { + "count": 2, + "covered": 2, + "notcovered": 0, + "percent": 100 + } + } + } + ], + "totals": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "regions": { + "count": 2, + "covered": 2, + "notcovered": 0, + "percent": 100 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while.json new file mode 100644 index 0000000000000..339c65556682a --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../coverage/while.rs", + "summary": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 4, + "covered": 3, + "percent": 75 + }, + "regions": { + "count": 4, + "covered": 3, + "notcovered": 1, + "percent": 75 + } + } + } + ], + "totals": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 4, + "covered": 3, + "percent": 75 + }, + "regions": { + "count": 4, + "covered": 3, + "notcovered": 1, + "percent": 75 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.loops_and_branches.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.loops_and_branches.txt new file mode 100644 index 0000000000000..3a969a6b89869 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.loops_and_branches.txt @@ -0,0 +1,38 @@ + 1| |#![allow(unused_assignments)] + 2| | + 3| |// This test confirms an earlier problem was resolved, supporting the MIR graph generated by the + 4| |// structure of this `fmt` function. + 5| | + 6| |struct DebugTest; + 7| | + 8| |impl std::fmt::Debug for DebugTest { + 9| | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + 10| 1| if true { + 11| 1| if false { + 12| | while true { + 13| | } + 14| 1| } + 15| 1| write!(f, "error")?; + ^0 + 16| | } else { + 17| 1| } + 18| 1| Ok(()) + 19| 1| } + 20| |} + 21| | + 22| 1|fn main() { + 23| 1| let debug_test = DebugTest; + 24| 1| println!("{:?}", debug_test); + 25| 1|} + 26| | + 27| |/* + 28| | + 29| |This is the error message generated, before the issue was fixed: + 30| | + 31| |error: internal compiler error: compiler/rustc_mir/src/transform/coverage/mod.rs:374:42: + 32| |Error processing: DefId(0:6 ~ bug_incomplete_cov_graph_traversal_simplified[317d]::{impl#0}::fmt): + 33| |Error { message: "`TraverseCoverageGraphWithLoops` missed some `BasicCoverageBlock`s: + 34| |[bcb6, bcb7, bcb9]" } + 35| | + 36| |*/ + diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.nested_loops.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.nested_loops.txt new file mode 100644 index 0000000000000..c9f373bf6a7c0 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.nested_loops.txt @@ -0,0 +1,26 @@ + 1| |fn main() { + 2| 1| let is_true = std::env::args().len() == 1; + 3| 1| let mut countdown = 10; + 4| | + 5| 1| 'outer: while countdown > 0 { + 6| 1| let mut a = 100; + 7| 1| let mut b = 100; + 8| 3| for _ in 0..50 { + 9| 3| if a < 30 { + 10| 0| break; + 11| | } + 12| 3| a -= 5; + 13| 3| b -= 5; + 14| 3| if b < 90 { + 15| 1| a -= 10; + 16| 1| if is_true { + 17| 1| break 'outer; + 18| | } else { + 19| 0| a -= 2; + 20| 0| } + 21| 2| } + 22| 2| } + 23| 0| countdown -= 1; + 24| 0| } + 25| 1|} + diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.partial_eq_counter_without_region.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.partial_eq_counter_without_region.txt new file mode 100644 index 0000000000000..e7e4dfb5a86c4 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.partial_eq_counter_without_region.txt @@ -0,0 +1,111 @@ + 1| |// This test confirms an earlier problem was resolved, supporting the MIR graph generated by the + 2| |// structure of this test. + 3| | + 4| 2|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + ^1 ^1 + ------------------ + | Unexecuted instantiation: ::gt + ------------------ + | Unexecuted instantiation: ::le + ------------------ + | Unexecuted instantiation: ::ge + ------------------ + | ::lt: + | 4| 1|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + ------------------ + 5| |pub struct Version { + 6| | major: usize, + 7| 1| minor: usize, + 8| | patch: usize, + 9| |} + 10| | + 11| |impl Version { + 12| | pub fn new(major: usize, minor: usize, patch: usize) -> Self { + 13| 2| Self { + 14| 2| major, + 15| 2| minor, + 16| 2| patch, + 17| 2| } + 18| 2| } + 19| |} + 20| | + 21| 1|fn main() { + 22| 1| let version_3_2_1 = Version::new(3, 2, 1); + 23| 1| let version_3_3_0 = Version::new(3, 3, 0); + 24| 1| + 25| 1| println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version_3_3_0); + 26| 1|} + 27| | + 28| |/* + 29| | + 30| |This test verifies a bug was fixed that otherwise generated this error: + 31| | + 32| |thread 'rustc' panicked at 'No counters provided the source_hash for function: + 33| | Instance { + 34| | def: Item(WithOptConstParam { + 35| | did: DefId(0:101 ~ autocfg[c44a]::version::{impl#2}::partial_cmp), + 36| | const_param_did: None + 37| | }), + 38| | substs: [] + 39| | }' + 40| |The `PartialOrd` derived by `Version` happened to generate a MIR that generated coverage + 41| |without a code region associated with any `Counter`. Code regions were associated with at least + 42| |one expression, which is allowed, but the `function_source_hash` was only passed to the codegen + 43| |(coverage mapgen) phase from a `Counter`s code region. A new method was added to pass the + 44| |`function_source_hash` without a code region, if necessary. + 45| | + 46| |*/ + 47| | + 48| |// FIXME(richkadel): It may be worth investigating why the coverage report for this test produces + 49| |// the following results: + 50| | + 51| |/* + 52| | + 53| |1. Why are their two counts below different characters (first and last) of `PartialOrd`, on line 17? + 54| | + 55| |2. Line 17 is counted twice, but the `::lt` instance shows a line count of 1? Is there a missing + 56| | line count with a different instance? Or was it really only called once? + 57| | + 58| |3. Line 20 shows another line count (of 1) for a line within a `struct` declaration (on only one of + 59| | its 3 fields). I doubt the specific field (`minor`) is relevant, but rather I suspect there's a + 60| | problem computing the file position here, for some reason. + 61| | + 62| | + 63| | 16| | + 64| | 17| 2|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + 65| | ^1 ^1 + 66| |------------------ + 67| ||Unexecuted instantiation: ::gt + 68| |------------------ + 69| ||Unexecuted instantiation: ::le + 70| |------------------ + 71| ||Unexecuted instantiation: ::ge + 72| |------------------ + 73| ||::lt: + 74| || 17| 1|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + 75| |------------------ + 76| | 18| |pub struct Version { + 77| | 19| | major: usize, + 78| | 20| 1| minor: usize, + 79| | 21| | patch: usize, + 80| | 22| |} + 81| | 23| | + 82| | 24| |impl Version { + 83| | 25| | pub fn new(major: usize, minor: usize, patch: usize) -> Self { + 84| | 26| 2| Version { + 85| | 27| 2| major, + 86| | 28| 2| minor, + 87| | 29| 2| patch, + 88| | 30| 2| } + 89| | 31| 2| } + 90| | 32| |} + 91| | 33| | + 92| | 34| 1|fn main() { + 93| | 35| 1| let version_3_2_1 = Version::new(3, 2, 1); + 94| | 36| 1| let version_3_3_0 = Version::new(3, 3, 0); + 95| | 37| 1| + 96| | 38| 1| println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version + 97| |_3_3_0); + 98| | 39| 1|} + 99| |*/ + diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.tight_infinite_loop.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.tight_infinite_loop.txt new file mode 100644 index 0000000000000..e02eac03a6b15 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.tight_infinite_loop.txt @@ -0,0 +1,6 @@ + 1| |fn main() { + 2| 1| if false { + 3| | loop {} + 4| | } + 5| 1|} + diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while.txt new file mode 100644 index 0000000000000..194325b6b9eca --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while.txt @@ -0,0 +1,6 @@ + 1| |fn main() { + 2| 1| let num = 9; + 3| 1| while num >= 10 { + 4| 0| } + 5| 1|} + diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.loops_and_branches.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.loops_and_branches.txt new file mode 100644 index 0000000000000..235557ba3495c --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.loops_and_branches.txt @@ -0,0 +1,38 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/loops_and_branches.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/loops_and_branches +Counter in file 0 10:12 -> 10:16, #1 +Counter in file 0 11:16 -> 11:21, #2 +Counter in file 0 14:14 -> 14:15, (#2 - #5) +Counter in file 0 15:13 -> 15:31, (0 + (#2 - #5)) +Counter in file 0 15:31 -> 15:32, #4 +Counter in file 0 17:10 -> 17:11, #3 +Counter in file 0 18:9 -> 18:15, (#3 + 0) +Counter in file 0 19:5 -> 19:6, (#4 + (#3 + 0)) +Counter in file 0 22:11 -> 25:2, #1 +Emitting segments for file: ../coverage/loops_and_branches.rs +Combined regions: + 10:12 -> 10:16 (count=1) + 11:16 -> 11:21 (count=1) + 14:14 -> 14:15 (count=1) + 15:13 -> 15:31 (count=1) + 15:31 -> 15:32 (count=0) + 17:10 -> 17:11 (count=1) + 18:9 -> 18:15 (count=1) + 19:5 -> 19:6 (count=1) + 22:11 -> 25:2 (count=1) +Segment at 10:12 (count = 1), RegionEntry +Segment at 10:16 (count = 0), Skipped +Segment at 11:16 (count = 1), RegionEntry +Segment at 11:21 (count = 0), Skipped +Segment at 14:14 (count = 1), RegionEntry +Segment at 14:15 (count = 0), Skipped +Segment at 15:13 (count = 1), RegionEntry +Segment at 15:31 (count = 0), RegionEntry +Segment at 15:32 (count = 0), Skipped +Segment at 17:10 (count = 1), RegionEntry +Segment at 17:11 (count = 0), Skipped +Segment at 18:9 (count = 1), RegionEntry +Segment at 18:15 (count = 0), Skipped +Segment at 19:5 (count = 1), RegionEntry +Segment at 19:6 (count = 0), Skipped +Segment at 22:11 (count = 1), RegionEntry +Segment at 25:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.nested_loops.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.nested_loops.txt new file mode 100644 index 0000000000000..6bc26f94ac7c0 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.nested_loops.txt @@ -0,0 +1,74 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/nested_loops.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/nested_loops +Counter in file 0 2:9 -> 3:27, #1 +Counter in file 0 5:19 -> 5:32, (#1 + #2) +Counter in file 0 6:13 -> 7:24, ((#1 + #2) - #3) +Counter in file 0 8:13 -> 8:14, ((((#1 + #2) - #3) + (#5 + #6)) - #7) +Counter in file 0 8:18 -> 8:23, (((#1 + #2) - #3) + (#5 + #6)) +Counter in file 0 9:16 -> 9:22, (((((#1 + #2) - #3) + (#5 + #6)) - #7) + 0) +Counter in file 0 10:17 -> 10:22, #8 +Counter in file 0 12:13 -> 12:19, #9 +Counter in file 0 13:13 -> 13:19, #10 +Counter in file 0 14:16 -> 14:22, (#10 + 0) +Counter in file 0 15:17 -> 16:27, #11 +Counter in file 0 17:21 -> 17:33, #4 +Counter in file 0 19:21 -> 21:14, #5 +Counter in file 0 21:14 -> 21:15, #6 +Counter in file 0 22:10 -> 22:11, (#5 + #6) +Counter in file 0 23:9 -> 23:23, #2 +Counter in file 0 24:6 -> 24:7, #3 +Counter in file 0 25:1 -> 25:2, (#4 + #3) +Emitting segments for file: ../coverage/nested_loops.rs +Combined regions: + 2:9 -> 3:27 (count=1) + 5:19 -> 5:32 (count=1) + 6:13 -> 7:24 (count=1) + 8:13 -> 8:14 (count=3) + 8:18 -> 8:23 (count=3) + 9:16 -> 9:22 (count=3) + 10:17 -> 10:22 (count=0) + 12:13 -> 12:19 (count=3) + 13:13 -> 13:19 (count=3) + 14:16 -> 14:22 (count=3) + 15:17 -> 16:27 (count=1) + 17:21 -> 17:33 (count=1) + 19:21 -> 21:14 (count=0) + 21:14 -> 21:15 (count=2) + 22:10 -> 22:11 (count=2) + 23:9 -> 23:23 (count=0) + 24:6 -> 24:7 (count=0) + 25:1 -> 25:2 (count=1) +Segment at 2:9 (count = 1), RegionEntry +Segment at 3:27 (count = 0), Skipped +Segment at 5:19 (count = 1), RegionEntry +Segment at 5:32 (count = 0), Skipped +Segment at 6:13 (count = 1), RegionEntry +Segment at 7:24 (count = 0), Skipped +Segment at 8:13 (count = 3), RegionEntry +Segment at 8:14 (count = 0), Skipped +Segment at 8:18 (count = 3), RegionEntry +Segment at 8:23 (count = 0), Skipped +Segment at 9:16 (count = 3), RegionEntry +Segment at 9:22 (count = 0), Skipped +Segment at 10:17 (count = 0), RegionEntry +Segment at 10:22 (count = 0), Skipped +Segment at 12:13 (count = 3), RegionEntry +Segment at 12:19 (count = 0), Skipped +Segment at 13:13 (count = 3), RegionEntry +Segment at 13:19 (count = 0), Skipped +Segment at 14:16 (count = 3), RegionEntry +Segment at 14:22 (count = 0), Skipped +Segment at 15:17 (count = 1), RegionEntry +Segment at 16:27 (count = 0), Skipped +Segment at 17:21 (count = 1), RegionEntry +Segment at 17:33 (count = 0), Skipped +Segment at 19:21 (count = 0), RegionEntry +Segment at 21:14 (count = 2), RegionEntry +Segment at 21:15 (count = 0), Skipped +Segment at 22:10 (count = 2), RegionEntry +Segment at 22:11 (count = 0), Skipped +Segment at 23:9 (count = 0), RegionEntry +Segment at 23:23 (count = 0), Skipped +Segment at 24:6 (count = 0), RegionEntry +Segment at 24:7 (count = 0), Skipped +Segment at 25:1 (count = 1), RegionEntry +Segment at 25:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.partial_eq_counter_without_region.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.partial_eq_counter_without_region.txt new file mode 100644 index 0000000000000..5ab9a99d6868f --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.partial_eq_counter_without_region.txt @@ -0,0 +1,54 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/partial_eq_counter_without_region.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/partial_eq_counter_without_region +Counter in file 0 4:48 -> 4:49, ((#1 + #2) + ((#3 + #4) + ((#5 + #6) + #7))) +Counter in file 0 8:5 -> 8:17, #1 +Counter in file 0 21:11 -> 26:2, #1 +Counter in file 0 4:39 -> 4:40, #1 +Counter in file 0 4:48 -> 4:49, (#1 + 0) +Counter in file 0 7:5 -> 7:6, #1 +Counter in file 0 8:5 -> 8:17, #1 +Counter in file 0 4:39 -> 4:40, #1 +Counter in file 0 4:48 -> 4:49, (#1 + 0) +Counter in file 0 4:32 -> 4:33, ((#4 + #5) + #6) +Counter in file 0 4:53 -> 4:54, (#1 + (#2 + (#3 + #4))) +Counter in file 0 13:9 -> 18:6, #1 +Counter in file 0 7:5 -> 7:6, #1 +Counter in file 0 4:39 -> 4:40, #1 +Counter in file 0 4:48 -> 4:49, (#1 + 0) +Counter in file 0 7:5 -> 7:6, #1 +Counter in file 0 4:10 -> 4:15, #1 +Counter in file 0 4:35 -> 4:37, #1 +Counter in file 0 7:5 -> 7:6, #1 +Counter in file 0 8:5 -> 8:17, #1 +Counter in file 0 4:17 -> 4:22, #1 +Counter in file 0 8:5 -> 8:17, #1 +Counter in file 0 4:39 -> 4:40, #1 +Counter in file 0 4:48 -> 4:49, (#1 + 0) +Counter in file 0 4:32 -> 4:33, (#3 + (#1 + #2)) +Emitting segments for file: ../coverage/partial_eq_counter_without_region.rs +Combined regions: + 4:17 -> 4:22 (count=2) + 4:39 -> 4:40 (count=1) + 4:48 -> 4:49 (count=1) + 7:5 -> 7:6 (count=1) + 13:9 -> 18:6 (count=2) + 21:11 -> 26:2 (count=1) +Segment at 4:17 (count = 2), RegionEntry +Segment at 4:22 (count = 0), Skipped +Segment at 4:39 (count = 1), RegionEntry +Segment at 4:40 (count = 0), Skipped +Segment at 4:48 (count = 1), RegionEntry +Segment at 4:49 (count = 0), Skipped +Segment at 7:5 (count = 1), RegionEntry +Segment at 7:6 (count = 0), Skipped +Segment at 13:9 (count = 2), RegionEntry +Segment at 18:6 (count = 0), Skipped +Segment at 21:11 (count = 1), RegionEntry +Segment at 26:2 (count = 0), Skipped +Emitting segments for function: _RNvXs0_Cs4fqI2P2rA04_33partial_eq_counter_without_regionNtB5_7VersionNtNtCs7f2nZg1zwMz_4core3cmp10PartialOrd2ltB5_ +Combined regions: + 4:39 -> 4:40 (count=1) + 4:48 -> 4:49 (count=1) +Segment at 4:39 (count = 1), RegionEntry +Segment at 4:40 (count = 0), Skipped +Segment at 4:48 (count = 1), RegionEntry +Segment at 4:49 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.tight_infinite_loop.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.tight_infinite_loop.txt new file mode 100644 index 0000000000000..39a3cf5458c64 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.tight_infinite_loop.txt @@ -0,0 +1,11 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/tight_infinite_loop.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/tight_infinite_loop +Counter in file 0 2:8 -> 2:13, #1 +Counter in file 0 5:1 -> 5:2, (#1 - #2) +Emitting segments for file: ../coverage/tight_infinite_loop.rs +Combined regions: + 2:8 -> 2:13 (count=1) + 5:1 -> 5:2 (count=1) +Segment at 2:8 (count = 1), RegionEntry +Segment at 2:13 (count = 0), Skipped +Segment at 5:1 (count = 1), RegionEntry +Segment at 5:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while.txt new file mode 100644 index 0000000000000..76cf1e92ecd3b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while.txt @@ -0,0 +1,19 @@ +Args: /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-cov show --debug --Xdemangler=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/stage0-tools-bin/rust-demangler --show-line-counts-or-regions --instr-profile=/usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/while.profdata /usr/local/google/home/richkadel/rust/build/x86_64-unknown-linux-gnu/test/run-make-fulldeps/coverage-reports-deadcode/coverage-reports-deadcode/while +Counter in file 0 2:9 -> 2:16, #1 +Counter in file 0 3:11 -> 3:20, (#1 + #2) +Counter in file 0 3:21 -> 4:6, #2 +Counter in file 0 5:1 -> 5:2, ((#1 + #2) - #2) +Emitting segments for file: ../coverage/while.rs +Combined regions: + 2:9 -> 2:16 (count=1) + 3:11 -> 3:20 (count=1) + 3:21 -> 4:6 (count=0) + 5:1 -> 5:2 (count=1) +Segment at 2:9 (count = 1), RegionEntry +Segment at 2:16 (count = 0), Skipped +Segment at 3:11 (count = 1), RegionEntry +Segment at 3:20 (count = 0), Skipped +Segment at 3:21 (count = 0), RegionEntry +Segment at 4:6 (count = 0), Skipped +Segment at 5:1 (count = 1), RegionEntry +Segment at 5:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loops_and_branches/loops_and_branches.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loops_and_branches/loops_and_branches.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..a876c85822f32 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loops_and_branches/loops_and_branches.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,151 @@ + + + +loops_and_branches.main - Coverage Spans + + + +
fn main() @0,1,2,3⦊{ + let debug_test = DebugTest; + println!("{:?}", debug_test); +}⦉@0,1,2,3
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loops_and_branches/loops_and_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loops_and_branches/loops_and_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..2164a565e22b7 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loops_and_branches/loops_and_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html @@ -0,0 +1,94 @@ + + + +loops_and_branches.{impl#0}-fmt - Coverage Spans + + + +
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if @0⦊true⦉@0 { + if @1,3⦊false⦉@1,3 { + while @6,7⦊true⦉@6,7 @8,10⦊{ + }⦉@8,10 + }@9⦊‸⦉@9@5⦊‸⦉@5 + @11,12,13,14⦊write!(f, "error")⦉@11,12,13,14@16,18,19,20⦊?⦉@16,18,19,20; + } else @2⦊{ + }⦉@2@15⦊‸⦉@15 + @21⦊Ok(())⦉@21 + }@22⦊‸⦉@22
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..b30a5a9f81e1f --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,125 @@ + + + +nested_loops.main - Coverage Spans + + + +
fn main() { + let @0,1,2,3⦊is_true = std::env::args().len() == 1; + let mut countdown = 10⦉@0,1,2,3; + + 'outer: while @4,5⦊countdown > 0⦉@4,5 { + let @6,8,9⦊mut a = 100; + let mut b = 100⦉@6,8,9; + for @14,16⦊_⦉@14,16 in @10,11,12⦊0..50⦉@10,11,12 { + if @14,16⦊a < 30⦉@14,16 { + @17,19⦊break⦉@17,19; + } + @20⦊a -= 5⦉@20; + @21⦊b -= 5⦉@21; + if @21⦊b < 90⦉@21 { + @25⦊a -= 10; + if is_true⦉@25 { + @26,28⦊break 'outer⦉@26,28; + } else { + @29⦊a -= 2; + } + }⦉@29@23⦊‸⦉@23 + }@30⦊‸⦉@30 + @32⦊countdown -= 1⦉@32; + }@7⦊‸⦉@7 +}@33⦊‸⦉@33
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..3ebe51bc7ec36 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,285 @@ + + + +partial_eq_counter_without_region.main - Coverage Spans + + + +
fn main() @0,1,2,3,4,5,6,7,8⦊{ + let version_3_2_1 = Version::new(3, 2, 1); + let version_3_3_0 = Version::new(3, 3, 0); + + println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version_3_3_0); +}⦉@0,1,2,3,4,5,6,7,8
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#0}-new.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#0}-new.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..ee6a5489f267b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#0}-new.-------.InstrumentCoverage.0.html @@ -0,0 +1,94 @@ + + + +partial_eq_counter_without_region.{impl#0}-new - Coverage Spans + + + +
pub fn new(major: usize, minor: usize, patch: usize) -> Self { + @0⦊Self { + major, + minor, + patch, + } + }⦉@0
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#1}-cmp.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#1}-cmp.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..469a00f62060e --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#1}-cmp.-------.InstrumentCoverage.0.html @@ -0,0 +1,66 @@ + + + +partial_eq_counter_without_region.{impl#1}-cmp - Coverage Spans + + + +
@14⦊@11,12⦊@13⦊Ord⦉@13⦉@11,12⦉@14@15⦊‸⦉@15
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-ge-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-ge-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..671cbea0ff305 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-ge-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,72 @@ + + + +partial_eq_counter_without_region.{impl#2}-ge-{closure#0}-{closure#0} - Coverage Spans + + + +
minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-ge-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-ge-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..6da79c4b130cb --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-ge-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,73 @@ + + + +partial_eq_counter_without_region.{impl#2}-ge-{closure#0} - Coverage Spans + + + +
major: usize, + @0,1,2,3⦊‸⦉@0,1,2,3minor: usize
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-ge.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-ge.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..5f5c31ce77591 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-ge.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + +partial_eq_counter_without_region.{impl#2}-ge - Coverage Spans + + + +
@0,1,2,3,4⦊‸⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊‸⦉@0,1,2,3,4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-gt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-gt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..fbdd0f7db42b4 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-gt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,72 @@ + + + +partial_eq_counter_without_region.{impl#2}-gt-{closure#0}-{closure#0} - Coverage Spans + + + +
minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-gt-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-gt-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..736f203433333 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-gt-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,73 @@ + + + +partial_eq_counter_without_region.{impl#2}-gt-{closure#0} - Coverage Spans + + + +
major: usize, + @0,1,2,3⦊‸⦉@0,1,2,3minor: usize
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-gt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-gt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..0fec7c9932f22 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-gt.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + +partial_eq_counter_without_region.{impl#2}-gt - Coverage Spans + + + +
@0,1,2,3,4⦊‸⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊‸⦉@0,1,2,3,4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-le-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-le-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..ff4eba107892b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-le-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,72 @@ + + + +partial_eq_counter_without_region.{impl#2}-le-{closure#0}-{closure#0} - Coverage Spans + + + +
minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-le-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-le-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..ccc86a7f92fea --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-le-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,73 @@ + + + +partial_eq_counter_without_region.{impl#2}-le-{closure#0} - Coverage Spans + + + +
major: usize, + @0,1,2,3⦊‸⦉@0,1,2,3minor: usize
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-le.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-le.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..682b9112c4cf8 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-le.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + +partial_eq_counter_without_region.{impl#2}-le - Coverage Spans + + + +
@0,1,2,3,4⦊‸⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊‸⦉@0,1,2,3,4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-lt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-lt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..e018c96c24f22 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-lt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,72 @@ + + + +partial_eq_counter_without_region.{impl#2}-lt-{closure#0}-{closure#0} - Coverage Spans + + + +
minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-lt-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-lt-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..a10032059b59e --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-lt-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,73 @@ + + + +partial_eq_counter_without_region.{impl#2}-lt-{closure#0} - Coverage Spans + + + +
major: usize, + @0,1,2,3⦊‸⦉@0,1,2,3minor: usize
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-lt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-lt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..89dad0f90698c --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-lt.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + +partial_eq_counter_without_region.{impl#2}-lt - Coverage Spans + + + +
@0,1,2,3,4⦊‸⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊‸⦉@0,1,2,3,4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..feae343bbbb8a --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html @@ -0,0 +1,68 @@ + + + +partial_eq_counter_without_region.{impl#2}-partial_cmp - Coverage Spans + + + +
@17⦊@14,15⦊@16⦊PartialOrd⦉@16⦉@14,15⦉@17@18⦊‸⦉@18
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..49d73b3457b7c --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html @@ -0,0 +1,65 @@ + + + +partial_eq_counter_without_region.{impl#4}-assert_receiver_is_total_eq - Coverage Spans + + + +
@0⦊Eq⦉@0
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#6}-eq.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#6}-eq.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..bc34080f10974 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#6}-eq.-------.InstrumentCoverage.0.html @@ -0,0 +1,67 @@ + + + +partial_eq_counter_without_region.{impl#6}-eq - Coverage Spans + + + +
@2⦊@1⦊PartialEq⦉@1⦉@2@4⦊‸⦉@4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#6}-ne.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#6}-ne.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..376c8dd80d096 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#6}-ne.-------.InstrumentCoverage.0.html @@ -0,0 +1,71 @@ + + + +partial_eq_counter_without_region.{impl#6}-ne - Coverage Spans + + + +
@2⦊@5⦊@6⦊@1⦊PartialEq⦉@1⦉@6⦉@5⦉@2@4⦊‸⦉@4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#7}-fmt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#7}-fmt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..c3fed16a3b4ee --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#7}-fmt.-------.InstrumentCoverage.0.html @@ -0,0 +1,99 @@ + + + +partial_eq_counter_without_region.{impl#7}-fmt - Coverage Spans + + + +
@0,1,2,3,4,5⦊Debug⦉@0,1,2,3,4,5
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#8}-clone.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#8}-clone.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..6a4f11e075446 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#8}-clone.-------.InstrumentCoverage.0.html @@ -0,0 +1,78 @@ + + + +partial_eq_counter_without_region.{impl#8}-clone - Coverage Spans + + + +
@0,1,2,3⦊Clone⦉@0,1,2,3
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.tight_infinite_loop/tight_infinite_loop.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.tight_infinite_loop/tight_infinite_loop.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..cc5e54f87ce3a --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.tight_infinite_loop/tight_infinite_loop.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,70 @@ + + + +tight_infinite_loop.main - Coverage Spans + + + +
fn main() { + if @0⦊false⦉@0 { + @4,5⦊loop {}⦉@4,5@1,3⦊‸⦉@1,3 + } +}@2⦊‸⦉@2
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..49b8bb5520d86 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,71 @@ + + + +while.main - Coverage Spans + + + +
fn main() { + let @0⦊num = 9⦉@0; + while @1,2⦊num >= 10⦉@1,2 @3,5⦊{ + }⦉@3,5 +}@4⦊‸⦉@4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loops_and_branches/loops_and_branches.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loops_and_branches/loops_and_branches.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..a876c85822f32 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loops_and_branches/loops_and_branches.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,151 @@ + + + +loops_and_branches.main - Coverage Spans + + + +
fn main() @0,1,2,3⦊{ + let debug_test = DebugTest; + println!("{:?}", debug_test); +}⦉@0,1,2,3
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loops_and_branches/loops_and_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loops_and_branches/loops_and_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..2164a565e22b7 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loops_and_branches/loops_and_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html @@ -0,0 +1,94 @@ + + + +loops_and_branches.{impl#0}-fmt - Coverage Spans + + + +
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if @0⦊true⦉@0 { + if @1,3⦊false⦉@1,3 { + while @6,7⦊true⦉@6,7 @8,10⦊{ + }⦉@8,10 + }@9⦊‸⦉@9@5⦊‸⦉@5 + @11,12,13,14⦊write!(f, "error")⦉@11,12,13,14@16,18,19,20⦊?⦉@16,18,19,20; + } else @2⦊{ + }⦉@2@15⦊‸⦉@15 + @21⦊Ok(())⦉@21 + }@22⦊‸⦉@22
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..b30a5a9f81e1f --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,125 @@ + + + +nested_loops.main - Coverage Spans + + + +
fn main() { + let @0,1,2,3⦊is_true = std::env::args().len() == 1; + let mut countdown = 10⦉@0,1,2,3; + + 'outer: while @4,5⦊countdown > 0⦉@4,5 { + let @6,8,9⦊mut a = 100; + let mut b = 100⦉@6,8,9; + for @14,16⦊_⦉@14,16 in @10,11,12⦊0..50⦉@10,11,12 { + if @14,16⦊a < 30⦉@14,16 { + @17,19⦊break⦉@17,19; + } + @20⦊a -= 5⦉@20; + @21⦊b -= 5⦉@21; + if @21⦊b < 90⦉@21 { + @25⦊a -= 10; + if is_true⦉@25 { + @26,28⦊break 'outer⦉@26,28; + } else { + @29⦊a -= 2; + } + }⦉@29@23⦊‸⦉@23 + }@30⦊‸⦉@30 + @32⦊countdown -= 1⦉@32; + }@7⦊‸⦉@7 +}@33⦊‸⦉@33
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..3ebe51bc7ec36 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,285 @@ + + + +partial_eq_counter_without_region.main - Coverage Spans + + + +
fn main() @0,1,2,3,4,5,6,7,8⦊{ + let version_3_2_1 = Version::new(3, 2, 1); + let version_3_3_0 = Version::new(3, 3, 0); + + println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version_3_3_0); +}⦉@0,1,2,3,4,5,6,7,8
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#0}-new.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#0}-new.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..ee6a5489f267b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#0}-new.-------.InstrumentCoverage.0.html @@ -0,0 +1,94 @@ + + + +partial_eq_counter_without_region.{impl#0}-new - Coverage Spans + + + +
pub fn new(major: usize, minor: usize, patch: usize) -> Self { + @0⦊Self { + major, + minor, + patch, + } + }⦉@0
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#1}-cmp.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#1}-cmp.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..469a00f62060e --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#1}-cmp.-------.InstrumentCoverage.0.html @@ -0,0 +1,66 @@ + + + +partial_eq_counter_without_region.{impl#1}-cmp - Coverage Spans + + + +
@14⦊@11,12⦊@13⦊Ord⦉@13⦉@11,12⦉@14@15⦊‸⦉@15
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-ge-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-ge-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..671cbea0ff305 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-ge-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,72 @@ + + + +partial_eq_counter_without_region.{impl#2}-ge-{closure#0}-{closure#0} - Coverage Spans + + + +
minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-ge-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-ge-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..6da79c4b130cb --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-ge-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,73 @@ + + + +partial_eq_counter_without_region.{impl#2}-ge-{closure#0} - Coverage Spans + + + +
major: usize, + @0,1,2,3⦊‸⦉@0,1,2,3minor: usize
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-ge.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-ge.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..5f5c31ce77591 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-ge.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + +partial_eq_counter_without_region.{impl#2}-ge - Coverage Spans + + + +
@0,1,2,3,4⦊‸⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊‸⦉@0,1,2,3,4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-gt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-gt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..fbdd0f7db42b4 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-gt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,72 @@ + + + +partial_eq_counter_without_region.{impl#2}-gt-{closure#0}-{closure#0} - Coverage Spans + + + +
minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-gt-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-gt-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..736f203433333 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-gt-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,73 @@ + + + +partial_eq_counter_without_region.{impl#2}-gt-{closure#0} - Coverage Spans + + + +
major: usize, + @0,1,2,3⦊‸⦉@0,1,2,3minor: usize
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-gt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-gt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..0fec7c9932f22 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-gt.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + +partial_eq_counter_without_region.{impl#2}-gt - Coverage Spans + + + +
@0,1,2,3,4⦊‸⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊‸⦉@0,1,2,3,4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-le-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-le-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..ff4eba107892b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-le-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,72 @@ + + + +partial_eq_counter_without_region.{impl#2}-le-{closure#0}-{closure#0} - Coverage Spans + + + +
minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-le-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-le-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..ccc86a7f92fea --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-le-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,73 @@ + + + +partial_eq_counter_without_region.{impl#2}-le-{closure#0} - Coverage Spans + + + +
major: usize, + @0,1,2,3⦊‸⦉@0,1,2,3minor: usize
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-le.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-le.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..682b9112c4cf8 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-le.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + +partial_eq_counter_without_region.{impl#2}-le - Coverage Spans + + + +
@0,1,2,3,4⦊‸⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊‸⦉@0,1,2,3,4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-lt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-lt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..e018c96c24f22 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-lt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,72 @@ + + + +partial_eq_counter_without_region.{impl#2}-lt-{closure#0}-{closure#0} - Coverage Spans + + + +
minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-lt-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-lt-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..a10032059b59e --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-lt-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,73 @@ + + + +partial_eq_counter_without_region.{impl#2}-lt-{closure#0} - Coverage Spans + + + +
major: usize, + @0,1,2,3⦊‸⦉@0,1,2,3minor: usize
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-lt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-lt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..89dad0f90698c --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-lt.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + +partial_eq_counter_without_region.{impl#2}-lt - Coverage Spans + + + +
@0,1,2,3,4⦊‸⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊‸⦉@0,1,2,3,4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..feae343bbbb8a --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html @@ -0,0 +1,68 @@ + + + +partial_eq_counter_without_region.{impl#2}-partial_cmp - Coverage Spans + + + +
@17⦊@14,15⦊@16⦊PartialOrd⦉@16⦉@14,15⦉@17@18⦊‸⦉@18
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..49d73b3457b7c --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html @@ -0,0 +1,65 @@ + + + +partial_eq_counter_without_region.{impl#4}-assert_receiver_is_total_eq - Coverage Spans + + + +
@0⦊Eq⦉@0
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#6}-eq.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#6}-eq.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..bc34080f10974 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#6}-eq.-------.InstrumentCoverage.0.html @@ -0,0 +1,67 @@ + + + +partial_eq_counter_without_region.{impl#6}-eq - Coverage Spans + + + +
@2⦊@1⦊PartialEq⦉@1⦉@2@4⦊‸⦉@4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#6}-ne.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#6}-ne.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..376c8dd80d096 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#6}-ne.-------.InstrumentCoverage.0.html @@ -0,0 +1,71 @@ + + + +partial_eq_counter_without_region.{impl#6}-ne - Coverage Spans + + + +
@2⦊@5⦊@6⦊@1⦊PartialEq⦉@1⦉@6⦉@5⦉@2@4⦊‸⦉@4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#7}-fmt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#7}-fmt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..c3fed16a3b4ee --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#7}-fmt.-------.InstrumentCoverage.0.html @@ -0,0 +1,99 @@ + + + +partial_eq_counter_without_region.{impl#7}-fmt - Coverage Spans + + + +
@0,1,2,3,4,5⦊Debug⦉@0,1,2,3,4,5
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#8}-clone.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#8}-clone.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..6a4f11e075446 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq_counter_without_region/partial_eq_counter_without_region.{impl#8}-clone.-------.InstrumentCoverage.0.html @@ -0,0 +1,78 @@ + + + +partial_eq_counter_without_region.{impl#8}-clone - Coverage Spans + + + +
@0,1,2,3⦊Clone⦉@0,1,2,3
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.tight_infinite_loop/tight_infinite_loop.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.tight_infinite_loop/tight_infinite_loop.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..cc5e54f87ce3a --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.tight_infinite_loop/tight_infinite_loop.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,70 @@ + + + +tight_infinite_loop.main - Coverage Spans + + + +
fn main() { + if @0⦊false⦉@0 { + @4,5⦊loop {}⦉@4,5@1,3⦊‸⦉@1,3 + } +}@2⦊‸⦉@2
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..49b8bb5520d86 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,71 @@ + + + +while.main - Coverage Spans + + + +
fn main() { + let @0⦊num = 9⦉@0; + while @1,2⦊num >= 10⦉@1,2 @3,5⦊{ + }⦉@3,5 +}@4⦊‸⦉@4
+ + diff --git a/src/test/run-make-fulldeps/coverage/loops_and_branches.rs b/src/test/run-make-fulldeps/coverage/loops_and_branches.rs new file mode 100644 index 0000000000000..a9df7e0fab75f --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/loops_and_branches.rs @@ -0,0 +1,36 @@ +#![allow(unused_assignments)] + +// This test confirms an earlier problem was resolved, supporting the MIR graph generated by the +// structure of this `fmt` function. + +struct DebugTest; + +impl std::fmt::Debug for DebugTest { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if true { + if false { + while true { + } + } + write!(f, "error")?; + } else { + } + Ok(()) + } +} + +fn main() { + let debug_test = DebugTest; + println!("{:?}", debug_test); +} + +/* + +This is the error message generated, before the issue was fixed: + +error: internal compiler error: compiler/rustc_mir/src/transform/coverage/mod.rs:374:42: +Error processing: DefId(0:6 ~ bug_incomplete_cov_graph_traversal_simplified[317d]::{impl#0}::fmt): +Error { message: "`TraverseCoverageGraphWithLoops` missed some `BasicCoverageBlock`s: +[bcb6, bcb7, bcb9]" } + +*/ diff --git a/src/test/run-make-fulldeps/coverage/nested_loops.rs b/src/test/run-make-fulldeps/coverage/nested_loops.rs new file mode 100644 index 0000000000000..4c7c784279650 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/nested_loops.rs @@ -0,0 +1,25 @@ +fn main() { + let is_true = std::env::args().len() == 1; + let mut countdown = 10; + + 'outer: while countdown > 0 { + let mut a = 100; + let mut b = 100; + for _ in 0..50 { + if a < 30 { + break; + } + a -= 5; + b -= 5; + if b < 90 { + a -= 10; + if is_true { + break 'outer; + } else { + a -= 2; + } + } + } + countdown -= 1; + } +} diff --git a/src/test/run-make-fulldeps/coverage/partial_eq_counter_without_region.rs b/src/test/run-make-fulldeps/coverage/partial_eq_counter_without_region.rs new file mode 100644 index 0000000000000..334fb3364ccc4 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/partial_eq_counter_without_region.rs @@ -0,0 +1,99 @@ +// This test confirms an earlier problem was resolved, supporting the MIR graph generated by the +// structure of this test. + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Version { + major: usize, + minor: usize, + patch: usize, +} + +impl Version { + pub fn new(major: usize, minor: usize, patch: usize) -> Self { + Self { + major, + minor, + patch, + } + } +} + +fn main() { + let version_3_2_1 = Version::new(3, 2, 1); + let version_3_3_0 = Version::new(3, 3, 0); + + println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version_3_3_0); +} + +/* + +This test verifies a bug was fixed that otherwise generated this error: + +thread 'rustc' panicked at 'No counters provided the source_hash for function: + Instance { + def: Item(WithOptConstParam { + did: DefId(0:101 ~ autocfg[c44a]::version::{impl#2}::partial_cmp), + const_param_did: None + }), + substs: [] + }' +The `PartialOrd` derived by `Version` happened to generate a MIR that generated coverage +without a code region associated with any `Counter`. Code regions were associated with at least +one expression, which is allowed, but the `function_source_hash` was only passed to the codegen +(coverage mapgen) phase from a `Counter`s code region. A new method was added to pass the +`function_source_hash` without a code region, if necessary. + +*/ + +// FIXME(richkadel): It may be worth investigating why the coverage report for this test produces +// the following results: + +/* + +1. Why are their two counts below different characters (first and last) of `PartialOrd`, on line 17? + +2. Line 17 is counted twice, but the `::lt` instance shows a line count of 1? Is there a missing + line count with a different instance? Or was it really only called once? + +3. Line 20 shows another line count (of 1) for a line within a `struct` declaration (on only one of + its 3 fields). I doubt the specific field (`minor`) is relevant, but rather I suspect there's a + problem computing the file position here, for some reason. + + + 16| | + 17| 2|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + ^1 ^1 +------------------ +|Unexecuted instantiation: ::gt +------------------ +|Unexecuted instantiation: ::le +------------------ +|Unexecuted instantiation: ::ge +------------------ +|::lt: +| 17| 1|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +------------------ + 18| |pub struct Version { + 19| | major: usize, + 20| 1| minor: usize, + 21| | patch: usize, + 22| |} + 23| | + 24| |impl Version { + 25| | pub fn new(major: usize, minor: usize, patch: usize) -> Self { + 26| 2| Version { + 27| 2| major, + 28| 2| minor, + 29| 2| patch, + 30| 2| } + 31| 2| } + 32| |} + 33| | + 34| 1|fn main() { + 35| 1| let version_3_2_1 = Version::new(3, 2, 1); + 36| 1| let version_3_3_0 = Version::new(3, 3, 0); + 37| 1| + 38| 1| println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version +_3_3_0); + 39| 1|} +*/ diff --git a/src/test/run-make-fulldeps/coverage/tight_infinite_loop.rs b/src/test/run-make-fulldeps/coverage/tight_infinite_loop.rs new file mode 100644 index 0000000000000..cef99027aaa4f --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/tight_infinite_loop.rs @@ -0,0 +1,5 @@ +fn main() { + if false { + loop {} + } +} diff --git a/src/test/run-make-fulldeps/coverage/while.rs b/src/test/run-make-fulldeps/coverage/while.rs new file mode 100644 index 0000000000000..781b90b35663e --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/while.rs @@ -0,0 +1,5 @@ +fn main() { + let num = 9; + while num >= 10 { + } +} From 4ee5cec3b0b4ba553573d6b44d6fe4daae6c65d6 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Wed, 21 Oct 2020 14:10:54 -0700 Subject: [PATCH 16/18] Refactored debug features from mod.rs to debug.rs Also removed the SIMPLIFY_EXPRESSIONS option, which added complexity without tangible benefits. --- .../rustc_mir/src/transform/coverage/debug.rs | 252 ++++++++++- .../rustc_mir/src/transform/coverage/mod.rs | 393 +++--------------- 2 files changed, 296 insertions(+), 349 deletions(-) diff --git a/compiler/rustc_mir/src/transform/coverage/debug.rs b/compiler/rustc_mir/src/transform/coverage/debug.rs index 5b8d50bf99ffe..835636946bab9 100644 --- a/compiler/rustc_mir/src/transform/coverage/debug.rs +++ b/compiler/rustc_mir/src/transform/coverage/debug.rs @@ -1,9 +1,15 @@ -use super::graph::BasicCoverageBlock; +use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; use super::spans::CoverageSpan; +use crate::util::generic_graphviz::GraphvizWriter; +use crate::util::pretty; +use crate::util::spanview::{self, SpanViewable}; + use rustc_data_structures::fx::FxHashMap; +use rustc_index::vec::Idx; use rustc_middle::mir::coverage::*; -use rustc_middle::mir::{BasicBlock, TerminatorKind}; +use rustc_middle::mir::{self, BasicBlock, TerminatorKind}; +use rustc_middle::ty::TyCtxt; use std::lazy::SyncOnceCell; @@ -20,14 +26,12 @@ pub(crate) fn debug_options<'a>() -> &'a DebugOptions { #[derive(Debug, Clone)] pub(crate) struct DebugOptions { pub allow_unused_expressions: bool, - pub simplify_expressions: bool, counter_format: ExpressionFormat, } impl DebugOptions { fn new() -> Self { let mut allow_unused_expressions = true; - let mut simplify_expressions = false; let mut counter_format = ExpressionFormat::default(); if let Ok(env_debug_options) = std::env::var(RUSTC_COVERAGE_DEBUG_OPTIONS) { @@ -41,13 +45,6 @@ impl DebugOptions { RUSTC_COVERAGE_DEBUG_OPTIONS, allow_unused_expressions ); } - Some(option) if option == "simplify_expressions" => { - simplify_expressions = bool_option_val(option, setting.next()); - debug!( - "{} env option `simplify_expressions` is set to {}", - RUSTC_COVERAGE_DEBUG_OPTIONS, simplify_expressions - ); - } Some(option) if option == "counter_format" => { if let Some(strval) = setting.next() { counter_format = counter_format_option_val(strval); @@ -76,7 +73,7 @@ impl DebugOptions { } } - Self { allow_unused_expressions, simplify_expressions, counter_format } + Self { allow_unused_expressions, counter_format } } } @@ -405,7 +402,7 @@ impl UsedExpressions { } } - pub fn validate_expression_is_used( + pub fn add_unused_expression_if_not_found( &mut self, expression: &CoverageKind, edge_from_bcb: Option, @@ -434,7 +431,38 @@ impl UsedExpressions { } } - pub fn check_no_unused(&self, debug_counters: &DebugCounters) { + /// If enabled, validate that every BCB or edge counter not directly associated with a coverage + /// span is at least indirectly associated (it is a dependency of a BCB counter that _is_ + /// associated with a coverage span). + pub fn validate( + &mut self, + bcb_counters_without_direct_coverage_spans: &Vec<( + Option, + BasicCoverageBlock, + CoverageKind, + )>, + ) { + if self.is_enabled() { + let mut not_validated = bcb_counters_without_direct_coverage_spans + .iter() + .map(|(_, _, counter_kind)| counter_kind) + .collect::>(); + let mut validating_count = 0; + while not_validated.len() != validating_count { + let to_validate = not_validated.split_off(0); + validating_count = to_validate.len(); + for counter_kind in to_validate { + if self.expression_is_used(counter_kind) { + self.add_expression_operands(counter_kind); + } else { + not_validated.push(counter_kind); + } + } + } + } + } + + pub fn alert_on_unused_expressions(&self, debug_counters: &DebugCounters) { if let Some(unused_expressions) = self.some_unused_expressions.as_ref() { for (counter_kind, edge_from_bcb, target_bcb) in unused_expressions { let unused_counter_message = if let Some(from_bcb) = edge_from_bcb.as_ref() { @@ -454,15 +482,7 @@ impl UsedExpressions { ) }; - if debug_options().simplify_expressions || debug_options().allow_unused_expressions - { - // Note, the debugging option `simplify_expressions`, which initializes the - // `debug_expressions_cache` can cause some counters to become unused, and - // is not a bug. - // - // For example, converting `x + (y - x)` to just `y` removes a dependency - // on `y - x`. If that expression is not a dependency elsewhere, and if it is - // not associated with a `CoverageSpan`, it is now considered `unused`. + if debug_options().allow_unused_expressions { debug!("WARNING: {}", unused_counter_message); } else { bug!("{}", unused_counter_message); @@ -472,6 +492,192 @@ impl UsedExpressions { } } +pub(crate) fn dump_coverage_spanview( + tcx: TyCtxt<'tcx>, + mir_body: &mir::Body<'tcx>, + basic_coverage_blocks: &CoverageGraph, + pass_name: &str, + coverage_spans: &Vec, +) { + let mir_source = mir_body.source; + let def_id = mir_source.def_id(); + + let span_viewables = span_viewables(tcx, mir_body, basic_coverage_blocks, &coverage_spans); + let mut file = pretty::create_dump_file(tcx, "html", None, pass_name, &0, mir_source) + .expect("Unexpected error creating MIR spanview HTML file"); + let crate_name = tcx.crate_name(def_id.krate); + let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate(); + let title = format!("{}.{} - Coverage Spans", crate_name, item_name); + spanview::write_document(tcx, def_id, span_viewables, &title, &mut file) + .expect("Unexpected IO error dumping coverage spans as HTML"); +} + +/// Converts the computed `BasicCoverageBlockData`s into `SpanViewable`s. +fn span_viewables( + tcx: TyCtxt<'tcx>, + mir_body: &mir::Body<'tcx>, + basic_coverage_blocks: &CoverageGraph, + coverage_spans: &Vec, +) -> Vec { + let mut span_viewables = Vec::new(); + for coverage_span in coverage_spans { + let tooltip = coverage_span.format_coverage_statements(tcx, mir_body); + let CoverageSpan { span, bcb, .. } = coverage_span; + let bcb_data = &basic_coverage_blocks[*bcb]; + let id = bcb_data.id(); + let leader_bb = bcb_data.leader_bb(); + span_viewables.push(SpanViewable { bb: leader_bb, span: *span, id, tooltip }); + } + span_viewables +} + +pub(crate) fn dump_coverage_graphviz( + tcx: TyCtxt<'tcx>, + mir_body: &mir::Body<'tcx>, + pass_name: &str, + basic_coverage_blocks: &CoverageGraph, + debug_counters: &DebugCounters, + graphviz_data: &GraphvizData, + intermediate_expressions: &Vec, + debug_used_expressions: &UsedExpressions, +) { + let mir_source = mir_body.source; + let def_id = mir_source.def_id(); + let node_content = |bcb| { + bcb_to_string_sections( + tcx, + mir_body, + debug_counters, + &basic_coverage_blocks[bcb], + graphviz_data.get_bcb_coverage_spans_with_counters(bcb), + graphviz_data.get_bcb_dependency_counters(bcb), + // intermediate_expressions are injected into the mir::START_BLOCK, so + // include them in the first BCB. + if bcb.index() == 0 { Some(&intermediate_expressions) } else { None }, + ) + }; + let edge_labels = |from_bcb| { + let from_bcb_data = &basic_coverage_blocks[from_bcb]; + let from_terminator = from_bcb_data.terminator(mir_body); + let mut edge_labels = from_terminator.kind.fmt_successor_labels(); + edge_labels.retain(|label| label.to_string() != "unreachable"); + let edge_counters = from_terminator + .successors() + .map(|&successor_bb| graphviz_data.get_edge_counter(from_bcb, successor_bb)); + edge_labels + .iter() + .zip(edge_counters) + .map(|(label, some_counter)| { + if let Some(counter) = some_counter { + format!("{}\n{}", label, debug_counters.format_counter(counter)) + } else { + label.to_string() + } + }) + .collect::>() + }; + let graphviz_name = format!("Cov_{}_{}", def_id.krate.index(), def_id.index.index()); + let mut graphviz_writer = + GraphvizWriter::new(basic_coverage_blocks, &graphviz_name, node_content, edge_labels); + let unused_expressions = debug_used_expressions.get_unused_expressions(); + if unused_expressions.len() > 0 { + graphviz_writer.set_graph_label(&format!( + "Unused expressions:\n {}", + unused_expressions + .as_slice() + .iter() + .map(|(counter_kind, edge_from_bcb, target_bcb)| { + if let Some(from_bcb) = edge_from_bcb.as_ref() { + format!( + "{:?}->{:?}: {}", + from_bcb, + target_bcb, + debug_counters.format_counter(&counter_kind), + ) + } else { + format!( + "{:?}: {}", + target_bcb, + debug_counters.format_counter(&counter_kind), + ) + } + }) + .collect::>() + .join("\n ") + )); + } + let mut file = pretty::create_dump_file(tcx, "dot", None, pass_name, &0, mir_source) + .expect("Unexpected error creating BasicCoverageBlock graphviz DOT file"); + graphviz_writer + .write_graphviz(tcx, &mut file) + .expect("Unexpected error writing BasicCoverageBlock graphviz DOT file"); +} + +fn bcb_to_string_sections( + tcx: TyCtxt<'tcx>, + mir_body: &mir::Body<'tcx>, + debug_counters: &DebugCounters, + bcb_data: &BasicCoverageBlockData, + some_coverage_spans_with_counters: Option<&Vec<(CoverageSpan, CoverageKind)>>, + some_dependency_counters: Option<&Vec>, + some_intermediate_expressions: Option<&Vec>, +) -> Vec { + let len = bcb_data.basic_blocks.len(); + let mut sections = Vec::new(); + if let Some(collect_intermediate_expressions) = some_intermediate_expressions { + sections.push( + collect_intermediate_expressions + .iter() + .map(|expression| { + format!("Intermediate {}", debug_counters.format_counter(expression)) + }) + .collect::>() + .join("\n"), + ); + } + if let Some(coverage_spans_with_counters) = some_coverage_spans_with_counters { + sections.push( + coverage_spans_with_counters + .iter() + .map(|(covspan, counter)| { + format!( + "{} at {}", + debug_counters.format_counter(counter), + covspan.format(tcx, mir_body) + ) + }) + .collect::>() + .join("\n"), + ); + } + if let Some(dependency_counters) = some_dependency_counters { + sections.push(format!( + "Non-coverage counters:\n {}", + dependency_counters + .iter() + .map(|counter| debug_counters.format_counter(counter)) + .collect::>() + .join(" \n"), + )); + } + if let Some(counter_kind) = &bcb_data.counter_kind { + sections.push(format!("{:?}", counter_kind)); + } + let non_term_blocks = bcb_data.basic_blocks[0..len - 1] + .iter() + .map(|&bb| format!("{:?}: {}", bb, term_type(&mir_body[bb].terminator().kind))) + .collect::>(); + if non_term_blocks.len() > 0 { + sections.push(non_term_blocks.join("\n")); + } + sections.push(format!( + "{:?}: {}", + bcb_data.basic_blocks.last().unwrap(), + term_type(&bcb_data.terminator(mir_body).kind) + )); + sections +} + pub(crate) fn term_type(kind: &TerminatorKind<'tcx>) -> &'static str { match kind { TerminatorKind::Goto { .. } => "Goto", diff --git a/compiler/rustc_mir/src/transform/coverage/mod.rs b/compiler/rustc_mir/src/transform/coverage/mod.rs index 6765f22c36c2a..e81e5de1d3805 100644 --- a/compiler/rustc_mir/src/transform/coverage/mod.rs +++ b/compiler/rustc_mir/src/transform/coverage/mod.rs @@ -4,7 +4,7 @@ mod debug; mod graph; mod spans; -use debug::{debug_options, term_type, NESTED_INDENT}; +use debug::NESTED_INDENT; use graph::{ BasicCoverageBlock, BasicCoverageBlockData, BcbBranch, CoverageGraph, TraverseCoverageGraphWithLoops, @@ -12,18 +12,15 @@ use graph::{ use spans::{CoverageSpan, CoverageSpans}; use crate::transform::MirPass; -use crate::util::generic_graphviz::GraphvizWriter; use crate::util::pretty; -use crate::util::spanview::{self, SpanViewable}; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::graph::dominators::Dominators; use rustc_data_structures::graph::WithNumNodes; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; use rustc_index::bit_set::BitSet; -use rustc_index::vec::{Idx, IndexVec}; +use rustc_index::vec::IndexVec; use rustc_middle::hir; use rustc_middle::hir::map::blocks::FnLikeNode; use rustc_middle::ich::StableHashingContext; @@ -101,7 +98,6 @@ struct Instrumentor<'a, 'tcx> { function_source_hash: Option, next_counter_id: u32, num_expressions: u32, - debug_expressions_cache: Option>, debug_counters: debug::DebugCounters, } @@ -120,7 +116,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { function_source_hash: None, next_counter_id: CounterValueReference::START.as_u32(), num_expressions: 0, - debug_expressions_cache: None, debug_counters: debug::DebugCounters::new(), } } @@ -163,7 +158,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { debug!("instrumenting {:?}, span: {}", def_id, source_map.span_to_string(body_span)); - let dump_spanview = pretty::dump_enabled(tcx, self.pass_name, def_id); let dump_graphviz = tcx.sess.opts.debugging_opts.dump_mir_graphviz; if dump_graphviz { @@ -177,17 +171,14 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { &self.bcb_dominators, ); - // When dumping coverage spanview files, create `SpanViewables` from the `coverage_spans`. - if dump_spanview { - let span_viewables = self.span_viewables(&coverage_spans); - let mut file = - pretty::create_dump_file(tcx, "html", None, self.pass_name, &0, mir_source) - .expect("Unexpected error creating MIR spanview HTML file"); - let crate_name = tcx.crate_name(def_id.krate); - let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate(); - let title = format!("{}.{} - Coverage Spans", crate_name, item_name); - spanview::write_document(tcx, def_id, span_viewables, &title, &mut file) - .expect("Unexpected IO error dumping coverage spans as HTML"); + if pretty::dump_enabled(tcx, self.pass_name, def_id) { + debug::dump_coverage_spanview( + tcx, + self.mir_body, + &self.basic_coverage_blocks, + self.pass_name, + &coverage_spans, + ); } // When debug logging, or generating the coverage graphviz output, initialize the following @@ -195,29 +186,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { let mut debug_used_expressions = debug::UsedExpressions::new(); if level_enabled!(tracing::Level::DEBUG) || dump_graphviz { debug_used_expressions.enable(); - - if debug_options().simplify_expressions { - self.debug_expressions_cache.replace(FxHashMap::default()); - } - // CAUTION! The `simplify_expressions` option is only helpful for some debugging - // situations and it can change the generated MIR `Coverage` statements (resulting in - // differences in behavior when enabled, under `DEBUG`, compared to normal operation and - // testing). - // - // For debugging purposes, it is sometimes helpful to simplify some expression - // equations: - // - // * `x + (y - x)` becomes just `y` - // * `x + (y + 0)` becomes just x + y. - // - // Expression dependencies can deeply nested expressions, which can look quite long in - // printed debug messages and in graphs produced by `-Zdump-graphviz`. In reality, each - // referenced/nested expression is only present because that value is necessary to - // compute a counter value for another part of the coverage report. Simplifying - // expressions Does not result in less `Coverage` statements, so there is very little, - // if any, benefit to binary size or runtime to simplifying expressions, and adds - // additional compile-time complexity. Only enable this temporarily, if helpful to parse - // the debug output. } // When debugging with BCB graphviz output, initialize additional data structures. @@ -230,12 +198,10 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // counters. let mut collect_intermediate_expressions = Vec::with_capacity(self.basic_coverage_blocks.num_nodes()); - let result = self.make_bcb_counters(&coverage_spans, &mut collect_intermediate_expressions); if result.is_ok() { // If debugging, add any intermediate expressions (which are not associated with any // BCB) to the `debug_used_expressions` map. - if debug_used_expressions.is_enabled() { for intermediate_expression in &collect_intermediate_expressions { debug_used_expressions.add_expression_operands(intermediate_expression); @@ -249,137 +215,36 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { &mut debug_used_expressions, ); - // The previous step looped through the `CoverageSpan`s and injected the counter from - // the `CoverageSpan`s `BasicCoverageBlock`, removing it from the BCB in the process - // (via `take_counter()`). - // - // Any other counter associated with a `BasicCoverageBlock`, or its incoming edge, but - // not associated with a `CoverageSpan`, should only exist if the counter is a - // `Expression` dependency (one of the expression operands). Collect them, and inject - // the additional counters into the MIR, without a reportable coverage span. - let mut bcb_counters_without_direct_coverage_spans = Vec::new(); - for (target_bcb, target_bcb_data) in self.basic_coverage_blocks.iter_enumerated_mut() { - if let Some(counter_kind) = target_bcb_data.take_counter() { - bcb_counters_without_direct_coverage_spans.push(( - None, - target_bcb, - counter_kind, - )); - } - if let Some(edge_counters) = target_bcb_data.take_edge_counters() { - for (from_bcb, counter_kind) in edge_counters { - bcb_counters_without_direct_coverage_spans.push(( - Some(from_bcb), - target_bcb, - counter_kind, - )); - } - } - } - - if debug_used_expressions.is_enabled() { - // Validate that every BCB or edge counter not directly associated with a coverage - // span is at least indirectly associated (it is a dependency of a BCB counter that - // _is_ associated with a coverage span). - let mut not_validated = bcb_counters_without_direct_coverage_spans - .iter() - .map(|(_, _, counter_kind)| counter_kind) - .collect::>(); - let mut validating_count = 0; - while not_validated.len() != validating_count { - let to_validate = not_validated.split_off(0); - validating_count = to_validate.len(); - for counter_kind in to_validate { - if debug_used_expressions.expression_is_used(counter_kind) { - debug_used_expressions.add_expression_operands(counter_kind); - } else { - not_validated.push(counter_kind); - } - } - } - } + // Inject other counters created as dependencies of coverage span counters. + self.inject_indirect_counters(&mut graphviz_data, &mut debug_used_expressions); - self.inject_indirect_counters( - bcb_counters_without_direct_coverage_spans, - &mut graphviz_data, - &mut debug_used_expressions, - ); + // Intermediate expressions will be injected as the final step, after generating debug + // output, if any. } if graphviz_data.is_enabled() { - let node_content = |bcb| { - self.bcb_to_string_sections( - self.bcb_data(bcb), - graphviz_data.get_bcb_coverage_spans_with_counters(bcb), - graphviz_data.get_bcb_dependency_counters(bcb), - // collect_intermediate_expressions are injected into the mir::START_BLOCK, so - // include them in the first BCB. - if bcb.index() == 0 { Some(&collect_intermediate_expressions) } else { None }, - ) - }; - let edge_labels = |from_bcb| { - let from_terminator = self.bcb_terminator(from_bcb); - let mut edge_labels = from_terminator.kind.fmt_successor_labels(); - edge_labels.retain(|label| label.to_string() != "unreachable"); - let edge_counters = from_terminator - .successors() - .map(|&successor_bb| graphviz_data.get_edge_counter(from_bcb, successor_bb)); - edge_labels - .iter() - .zip(edge_counters) - .map(|(label, some_counter)| { - if let Some(counter) = some_counter { - format!("{}\n{}", label, self.format_counter(counter)) - } else { - label.to_string() - } - }) - .collect::>() - }; - let graphviz_name = format!("Cov_{}_{}", def_id.krate.index(), def_id.index.index()); - let mut graphviz_writer = GraphvizWriter::new( + debug::dump_coverage_graphviz( + tcx, + self.mir_body, + self.pass_name, &self.basic_coverage_blocks, - &graphviz_name, - node_content, - edge_labels, + &self.debug_counters, + &graphviz_data, + &collect_intermediate_expressions, + &debug_used_expressions, ); - let unused_expressions = debug_used_expressions.get_unused_expressions(); - if unused_expressions.len() > 0 { - graphviz_writer.set_graph_label(&format!( - "Unused expressions:\n {}", - unused_expressions - .as_slice() - .iter() - .map(|(counter_kind, edge_from_bcb, target_bcb)| { - if let Some(from_bcb) = edge_from_bcb.as_ref() { - format!( - "{:?}->{:?}: {}", - from_bcb, - target_bcb, - self.format_counter(&counter_kind), - ) - } else { - format!("{:?}: {}", target_bcb, self.format_counter(&counter_kind),) - } - }) - .collect::>() - .join("\n ") - )); - } - let mut file = - pretty::create_dump_file(tcx, "dot", None, self.pass_name, &0, mir_source) - .expect("Unexpected error creating BasicCoverageBlock graphviz DOT file"); - graphviz_writer - .write_graphviz(tcx, &mut file) - .expect("Unexpected error writing BasicCoverageBlock graphviz DOT file"); } result.unwrap_or_else(|e: Error| { bug!("Error processing: {:?}: {:?}", self.mir_body.source.def_id(), e) }); - debug_used_expressions.check_no_unused(&self.debug_counters); + // Depending on current `debug_options()`, `alert_on_unused_expressions()` could panic, so + // this check is performed as late as possible, to allow other debug output (logs and dump + // files), which might be helpful in analyzing unused expressions, to still be generated. + debug_used_expressions.alert_on_unused_expressions(&self.debug_counters); + // Finally, inject the intermediate expressions collected along the way. for intermediate_expression in collect_intermediate_expressions { self.inject_intermediate_expression(intermediate_expression); } @@ -450,19 +315,43 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { false } + /// `inject_coverage_span_counters()` looped through the `CoverageSpan`s and injected the + /// counter from the `CoverageSpan`s `BasicCoverageBlock`, removing it from the BCB in the + /// process (via `take_counter()`). + /// + /// Any other counter associated with a `BasicCoverageBlock`, or its incoming edge, but not + /// associated with a `CoverageSpan`, should only exist if the counter is a `Expression` + /// dependency (one of the expression operands). Collect them, and inject the additional + /// counters into the MIR, without a reportable coverage span. fn inject_indirect_counters( &mut self, - bcb_counters_without_direct_coverage_spans: Vec<( - Option, - BasicCoverageBlock, - CoverageKind, - )>, graphviz_data: &mut debug::GraphvizData, debug_used_expressions: &mut debug::UsedExpressions, ) { + let mut bcb_counters_without_direct_coverage_spans = Vec::new(); + for (target_bcb, target_bcb_data) in self.basic_coverage_blocks.iter_enumerated_mut() { + if let Some(counter_kind) = target_bcb_data.take_counter() { + bcb_counters_without_direct_coverage_spans.push((None, target_bcb, counter_kind)); + } + if let Some(edge_counters) = target_bcb_data.take_edge_counters() { + for (from_bcb, counter_kind) in edge_counters { + bcb_counters_without_direct_coverage_spans.push(( + Some(from_bcb), + target_bcb, + counter_kind, + )); + } + } + } + + // If debug is enabled, validate that every BCB or edge counter not directly associated + // with a coverage span is at least indirectly associated (it is a dependency of a BCB + // counter that _is_ associated with a coverage span). + debug_used_expressions.validate(&bcb_counters_without_direct_coverage_spans); + for (edge_from_bcb, target_bcb, counter_kind) in bcb_counters_without_direct_coverage_spans { - debug_used_expressions.validate_expression_is_used( + debug_used_expressions.add_unused_expression_if_not_found( &counter_kind, edge_from_bcb, target_bcb, @@ -579,7 +468,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { #[inline] fn bcb_branches(&self, from_bcb: BasicCoverageBlock) -> Vec { - self.basic_coverage_blocks.successors[from_bcb] + self.bcb_successors(from_bcb) .iter() .map(|&to_bcb| BcbBranch::from_to(from_bcb, to_bcb, &self.basic_coverage_blocks)) .collect::>() @@ -597,67 +486,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { self.bcb_dominators.is_dominated_by(node, dom) } - fn bcb_to_string_sections( - &self, - bcb_data: &BasicCoverageBlockData, - some_coverage_spans_with_counters: Option<&Vec<(CoverageSpan, CoverageKind)>>, - some_dependency_counters: Option<&Vec>, - some_intermediate_expressions: Option<&Vec>, - ) -> Vec { - let len = bcb_data.basic_blocks.len(); - let mut sections = Vec::new(); - if let Some(collect_intermediate_expressions) = some_intermediate_expressions { - sections.push( - collect_intermediate_expressions - .iter() - .map(|expression| format!("Intermediate {}", self.format_counter(expression))) - .collect::>() - .join("\n"), - ); - } - if let Some(coverage_spans_with_counters) = some_coverage_spans_with_counters { - sections.push( - coverage_spans_with_counters - .iter() - .map(|(covspan, counter)| { - format!( - "{} at {}", - self.format_counter(counter), - covspan.format(self.tcx, self.mir_body) - ) - }) - .collect::>() - .join("\n"), - ); - } - if let Some(dependency_counters) = some_dependency_counters { - sections.push(format!( - "Non-coverage counters:\n {}", - dependency_counters - .iter() - .map(|counter| self.format_counter(counter)) - .collect::>() - .join(" \n"), - )); - } - if let Some(counter_kind) = &bcb_data.counter_kind { - sections.push(format!("{:?}", counter_kind)); - } - let non_term_blocks = bcb_data.basic_blocks[0..len - 1] - .iter() - .map(|&bb| format!("{:?}: {}", bb, term_type(&self.mir_body[bb].terminator().kind))) - .collect::>(); - if non_term_blocks.len() > 0 { - sections.push(non_term_blocks.join("\n")); - } - sections.push(format!( - "{:?}: {}", - bcb_data.basic_blocks.last().unwrap(), - term_type(&bcb_data.terminator(self.mir_body).kind) - )); - sections - } - /// Traverse the BCB CFG and add either a `Counter` or `Expression` to ever BCB, to be /// injected with `CoverageSpan`s. `Expressions` have no runtime overhead, so if a viable /// expression (adding or subtracting two other counters or expressions) can compute the same @@ -692,12 +520,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { let mut traversal = TraverseCoverageGraphWithLoops::new(&self.basic_coverage_blocks, &self.bcb_dominators); while let Some(bcb) = traversal.next() { - debug!( - "{:?} has {} successors:", - bcb, - self.basic_coverage_blocks.successors[bcb].len() - ); - for &successor in &self.basic_coverage_blocks.successors[bcb] { + debug!("{:?} has {} successors:", bcb, self.bcb_successors(bcb).len()); + for &successor in self.bcb_successors(bcb) { if successor == bcb { debug!( "{:?} has itself as its own successor. (Note, the compiled code will \ @@ -1156,53 +980,16 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { fn make_expression( &mut self, - mut lhs: ExpressionOperandId, + lhs: ExpressionOperandId, op: Op, - mut rhs: ExpressionOperandId, + rhs: ExpressionOperandId, block_label_fn: F, ) -> CoverageKind where F: Fn() -> Option, { - if let Some(expressions_cache) = self.debug_expressions_cache.as_ref() { - if let Some(CoverageKind::Expression { lhs: lhs_lhs, op, rhs: lhs_rhs, .. }) = - expressions_cache.get(&lhs) - { - if *lhs_rhs == ExpressionOperandId::ZERO { - lhs = *lhs_lhs; - } else if *op == Op::Subtract && *lhs_rhs == rhs { - if let Some(lhs_expression) = expressions_cache.get(lhs_lhs) { - let expression = lhs_expression.clone(); - return self.duplicate_expression(expression); - } else { - let counter = *lhs_lhs; - return self.make_identity_counter(counter); - } - } - } - - if let Some(CoverageKind::Expression { lhs: rhs_lhs, op, rhs: rhs_rhs, .. }) = - expressions_cache.get(&rhs) - { - if *rhs_rhs == ExpressionOperandId::ZERO { - rhs = *rhs_rhs; - } else if *op == Op::Subtract && *rhs_rhs == lhs { - if let Some(rhs_expression) = expressions_cache.get(rhs_lhs) { - let expression = rhs_expression.clone(); - return self.duplicate_expression(expression); - } else { - let counter = *rhs_lhs; - return self.make_identity_counter(counter); - } - } - } - } - let id = self.next_expression(); let expression = CoverageKind::Expression { id, lhs, op, rhs }; - if let Some(expressions_cache) = self.debug_expressions_cache.as_mut() { - expressions_cache.insert(id.into(), expression.clone()); - } if self.debug_counters.is_enabled() { self.debug_counters.add_counter(&expression, (block_label_fn)()); } @@ -1210,45 +997,14 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } fn make_identity_counter(&mut self, counter_operand: ExpressionOperandId) -> CoverageKind { - if let Some(expression) = - self.debug_expressions_cache.as_ref().map_or(None, |c| c.get(&counter_operand)) - { - let new_expression = expression.clone(); - self.duplicate_expression(new_expression) - } else { - let some_block_label = if self.debug_counters.is_enabled() { - self.debug_counters.some_block_label(counter_operand).cloned() - } else { - None - }; - self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO, || { - some_block_label.clone() - }) - } - } - - fn duplicate_expression(&mut self, mut expression: CoverageKind) -> CoverageKind { - let next_expression_id = if self.debug_expressions_cache.is_some() { - Some(self.next_expression()) + let some_block_label = if self.debug_counters.is_enabled() { + self.debug_counters.some_block_label(counter_operand).cloned() } else { None }; - let expressions_cache = self - .debug_expressions_cache - .as_mut() - .expect("`duplicate_expression()` requires the debug_expressions_cache"); - match expression { - CoverageKind::Expression { ref mut id, .. } => { - *id = next_expression_id.expect( - "next_expression_id should be Some if there is a debug_expressions_cache", - ); - expressions_cache.insert(id.into(), expression.clone()); - } - _ => { - bug!("make_duplicate_expression called with non-expression type: {:?}", expression) - } - } - expression + self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO, || { + some_block_label.clone() + }) } fn inject_statement( @@ -1287,21 +1043,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { data.statements.push(statement); } - /// Converts the computed `BasicCoverageBlockData`s into `SpanViewable`s. - fn span_viewables(&self, coverage_spans: &Vec) -> Vec { - let tcx = self.tcx; - let mut span_viewables = Vec::new(); - for coverage_span in coverage_spans { - let tooltip = coverage_span.format_coverage_statements(tcx, self.mir_body); - let CoverageSpan { span, bcb, .. } = coverage_span; - let bcb_data = self.bcb_data(*bcb); - let id = bcb_data.id(); - let leader_bb = bcb_data.leader_bb(); - span_viewables.push(SpanViewable { bb: leader_bb, span: *span, id, tooltip }); - } - span_viewables - } - #[inline(always)] fn body_span(&self) -> Span { self.hir_body.value.span From 31a0b6be2c5ac7d596e902ec69adae3cd7dc60dd Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Wed, 21 Oct 2020 19:43:12 -0700 Subject: [PATCH 17/18] new `counters.rs` for counter management, and improved comments overall --- .../src/transform/coverage/counters.rs | 641 +++++++++++++ .../rustc_mir/src/transform/coverage/debug.rs | 50 +- .../rustc_mir/src/transform/coverage/graph.rs | 31 +- .../rustc_mir/src/transform/coverage/mod.rs | 871 +++--------------- .../rustc_mir/src/transform/coverage/spans.rs | 10 +- 5 files changed, 852 insertions(+), 751 deletions(-) create mode 100644 compiler/rustc_mir/src/transform/coverage/counters.rs diff --git a/compiler/rustc_mir/src/transform/coverage/counters.rs b/compiler/rustc_mir/src/transform/coverage/counters.rs new file mode 100644 index 0000000000000..81fb1eed739fc --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/counters.rs @@ -0,0 +1,641 @@ +use super::Error; + +use super::debug; +use super::graph; +use super::spans; + +use debug::{DebugCounters, NESTED_INDENT}; +use graph::{BasicCoverageBlock, BcbBranch, CoverageGraph, TraverseCoverageGraphWithLoops}; +use spans::CoverageSpan; + +use rustc_data_structures::graph::WithNumNodes; +use rustc_index::bit_set::BitSet; +use rustc_middle::mir::coverage::*; + +/// Manages the counter and expression indexes/IDs to generate `CoverageKind` components for MIR +/// `Coverage` statements. +pub(crate) struct CoverageCounters { + function_source_hash: u64, + next_counter_id: u32, + num_expressions: u32, + pub debug_counters: DebugCounters, +} + +impl CoverageCounters { + pub fn new(function_source_hash: u64) -> Self { + Self { + function_source_hash, + next_counter_id: CounterValueReference::START.as_u32(), + num_expressions: 0, + debug_counters: DebugCounters::new(), + } + } + + /// Activate the `DebugCounters` data structures, to provide additional debug formatting + /// features when formating `CoverageKind` (counter) values. + pub fn enable_debug(&mut self) { + self.debug_counters.enable(); + } + + /// Makes `CoverageKind` `Counter`s and `Expressions` for the `BasicCoverageBlocks` directly or + /// indirectly associated with `CoverageSpans`, and returns additional `Expression`s + /// representing intermediate values. + pub fn make_bcb_counters( + &mut self, + basic_coverage_blocks: &mut CoverageGraph, + coverage_spans: &Vec, + ) -> Result, Error> { + let mut bcb_counters = BcbCounters::new(self, basic_coverage_blocks); + bcb_counters.make_bcb_counters(coverage_spans) + } + + fn make_counter(&mut self, debug_block_label_fn: F) -> CoverageKind + where + F: Fn() -> Option, + { + let counter = CoverageKind::Counter { + function_source_hash: self.function_source_hash, + id: self.next_counter(), + }; + if self.debug_counters.is_enabled() { + self.debug_counters.add_counter(&counter, (debug_block_label_fn)()); + } + counter + } + + fn make_expression( + &mut self, + lhs: ExpressionOperandId, + op: Op, + rhs: ExpressionOperandId, + debug_block_label_fn: F, + ) -> CoverageKind + where + F: Fn() -> Option, + { + let id = self.next_expression(); + let expression = CoverageKind::Expression { id, lhs, op, rhs }; + if self.debug_counters.is_enabled() { + self.debug_counters.add_counter(&expression, (debug_block_label_fn)()); + } + expression + } + + pub fn make_identity_counter(&mut self, counter_operand: ExpressionOperandId) -> CoverageKind { + let some_debug_block_label = if self.debug_counters.is_enabled() { + self.debug_counters.some_block_label(counter_operand).cloned() + } else { + None + }; + self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO, || { + some_debug_block_label.clone() + }) + } + + /// Counter IDs start from one and go up. + fn next_counter(&mut self) -> CounterValueReference { + assert!(self.next_counter_id < u32::MAX - self.num_expressions); + let next = self.next_counter_id; + self.next_counter_id += 1; + CounterValueReference::from(next) + } + + /// Expression IDs start from u32::MAX and go down because a Expression can reference + /// (add or subtract counts) of both Counter regions and Expression regions. The counter + /// expression operand IDs must be unique across both types. + fn next_expression(&mut self) -> InjectedExpressionId { + assert!(self.next_counter_id < u32::MAX - self.num_expressions); + let next = u32::MAX - self.num_expressions; + self.num_expressions += 1; + InjectedExpressionId::from(next) + } +} + +/// Traverse the `CoverageGraph` and add either a `Counter` or `Expression` to every BCB, to be +/// injected with `CoverageSpan`s. `Expressions` have no runtime overhead, so if a viable expression +/// (adding or subtracting two other counters or expressions) can compute the same result as an +/// embedded counter, an `Expression` should be used. +struct BcbCounters<'a> { + coverage_counters: &'a mut CoverageCounters, + basic_coverage_blocks: &'a mut CoverageGraph, +} + +impl<'a> BcbCounters<'a> { + fn new( + coverage_counters: &'a mut CoverageCounters, + basic_coverage_blocks: &'a mut CoverageGraph, + ) -> Self { + Self { coverage_counters, basic_coverage_blocks } + } + + /// If two `CoverageGraph` branch from another `BasicCoverageBlock`, one of the branches + /// can be counted by `Expression` by subtracting the other branch from the branching + /// block. Otherwise, the `BasicCoverageBlock` executed the least should have the `Counter`. + /// One way to predict which branch executes the least is by considering loops. A loop is exited + /// at a branch, so the branch that jumps to a `BasicCoverageBlock` outside the loop is almost + /// always executed less than the branch that does not exit the loop. + /// + /// Returns any non-code-span expressions created to represent intermediate values (such as to + /// add two counters so the result can be subtracted from another counter), or an Error with + /// message for subsequent debugging. + fn make_bcb_counters( + &mut self, + coverage_spans: &Vec, + ) -> Result, Error> { + debug!("make_bcb_counters(): adding a counter or expression to each BasicCoverageBlock"); + let num_bcbs = self.basic_coverage_blocks.num_nodes(); + let mut collect_intermediate_expressions = Vec::with_capacity(num_bcbs); + + let mut bcbs_with_coverage = BitSet::new_empty(num_bcbs); + for covspan in coverage_spans { + bcbs_with_coverage.insert(covspan.bcb); + } + + // FIXME(richkadel): Add more comments to explain the logic here and in the rest of this + // function, and refactor this function to break it up into smaller functions that are + // easier to understand. + + let mut traversal = TraverseCoverageGraphWithLoops::new(&self.basic_coverage_blocks); + while let Some(bcb) = traversal.next() { + debug!("{:?} has {} successors:", bcb, self.bcb_successors(bcb).len()); + for &successor in self.bcb_successors(bcb) { + if successor == bcb { + debug!( + "{:?} has itself as its own successor. (Note, the compiled code will \ + generate an infinite loop.)", + bcb + ); + // Don't re-add this successor to the worklist. We are already processing it. + break; + } + for context in traversal.context_stack.iter_mut().rev() { + // Add successors of the current BCB to the appropriate context. Successors that + // stay within a loop are added to the BCBs context worklist. Successors that + // exit the loop (they are not dominated by the loop header) must be reachable + // from other BCBs outside the loop, and they will be added to a different + // worklist. + // + // Branching blocks (with more than one successor) must be processed before + // blocks with only one successor, to prevent unnecessarily complicating + // Expression`s by creating a Counter in a `BasicCoverageBlock` that the + // branching block would have given an `Expression` (or vice versa). + let (some_successor_to_add, some_loop_header) = + if let Some((_, loop_header)) = context.loop_backedges { + if self.bcb_is_dominated_by(successor, loop_header) { + (Some(successor), Some(loop_header)) + } else { + (None, None) + } + } else { + (Some(successor), None) + }; + if let Some(successor_to_add) = some_successor_to_add { + if self.bcb_successors(successor_to_add).len() > 1 { + debug!( + "{:?} successor is branching. Prioritize it at the beginning of \ + the {}", + successor_to_add, + if let Some(loop_header) = some_loop_header { + format!("worklist for the loop headed by {:?}", loop_header) + } else { + String::from("non-loop worklist") + }, + ); + context.worklist.insert(0, successor_to_add); + } else { + debug!( + "{:?} successor is non-branching. Defer it to the end of the {}", + successor_to_add, + if let Some(loop_header) = some_loop_header { + format!("worklist for the loop headed by {:?}", loop_header) + } else { + String::from("non-loop worklist") + }, + ); + context.worklist.push(successor_to_add); + } + break; + } + } + } + + if !bcbs_with_coverage.contains(bcb) { + debug!( + "{:?} does not have any `CoverageSpan`s. A counter will only be added if \ + and when a covered BCB has an expression dependency.", + bcb, + ); + continue; + } + + debug!("{:?} has at least one `CoverageSpan`. Get or make its counter", bcb); + let bcb_counter_operand = + self.get_or_make_counter_operand(bcb, &mut collect_intermediate_expressions)?; + + let branch_needs_a_counter = + |branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none(); + + let branches = self.bcb_branches(bcb); + let needs_branch_counters = + branches.len() > 1 && branches.iter().any(branch_needs_a_counter); + + if needs_branch_counters { + let branching_bcb = bcb; + let branching_counter_operand = bcb_counter_operand; + + debug!( + "{:?} has some branch(es) without counters:\n {}", + branching_bcb, + branches + .iter() + .map(|branch| { + format!( + "{:?}: {:?}", + branch, + branch.counter(&self.basic_coverage_blocks) + ) + }) + .collect::>() + .join("\n "), + ); + + // At most one of the branches (or its edge, from the branching_bcb, + // if the branch has multiple incoming edges) can have a counter computed by + // expression. + // + // If at least one of the branches leads outside of a loop (`found_loop_exit` is + // true), and at least one other branch does not exit the loop (the first of which + // is captured in `some_reloop_branch`), it's likely any reloop branch will be + // executed far more often than loop exit branch, making the reloop branch a better + // candidate for an expression. + let mut some_reloop_branch: Option = None; + for context in traversal.context_stack.iter().rev() { + if let Some((backedge_from_bcbs, _)) = &context.loop_backedges { + let mut found_loop_exit = false; + for &branch in branches.iter() { + if backedge_from_bcbs.iter().any(|&backedge_from_bcb| { + self.bcb_is_dominated_by(backedge_from_bcb, branch.target_bcb) + }) { + if let Some(reloop_branch) = some_reloop_branch { + if reloop_branch.counter(&self.basic_coverage_blocks).is_none() + { + // we already found a candidate reloop_branch that still + // needs a counter + continue; + } + } + // The path from branch leads back to the top of the loop. Set this + // branch as the `reloop_branch`. If this branch already has a + // counter, and we find another reloop branch that doesn't have a + // counter yet, that branch will be selected as the `reloop_branch` + // instead. + some_reloop_branch = Some(branch); + } else { + // The path from branch leads outside this loop + found_loop_exit = true; + } + if found_loop_exit + && some_reloop_branch.filter(branch_needs_a_counter).is_some() + { + // Found both a branch that exits the loop and a branch that returns + // to the top of the loop (`reloop_branch`), and the `reloop_branch` + // doesn't already have a counter. + break; + } + } + if !found_loop_exit { + debug!( + "No branches exit the loop, so any branch without an existing \ + counter can have the `Expression`." + ); + break; + } + if some_reloop_branch.is_some() { + debug!( + "Found a branch that exits the loop and a branch the loops back to \ + the top of the loop (`reloop_branch`). The `reloop_branch` will \ + get the `Expression`, as long as it still needs a counter." + ); + break; + } + // else all branches exited this loop context, so run the same checks with + // the outer loop(s) + } + } + + // Select a branch for the expression, either the recommended `reloop_branch`, or + // if none was found, select any branch. + let expression_branch = if let Some(reloop_branch_without_counter) = + some_reloop_branch.filter(branch_needs_a_counter) + { + debug!( + "Selecting reloop_branch={:?} that still needs a counter, to get the \ + `Expression`", + reloop_branch_without_counter + ); + reloop_branch_without_counter + } else { + let &branch_without_counter = branches + .iter() + .find(|&&branch| branch.counter(&self.basic_coverage_blocks).is_none()) + .expect( + "needs_branch_counters was `true` so there should be at least one \ + branch", + ); + debug!( + "Selecting any branch={:?} that still needs a counter, to get the \ + `Expression` because there was no `reloop_branch`, or it already had a \ + counter", + branch_without_counter + ); + branch_without_counter + }; + + // Assign a Counter or Expression to each branch, plus additional + // `Expression`s, as needed, to sum up intermediate results. + let mut some_sumup_counter_operand = None; + for branch in branches { + if branch != expression_branch { + let branch_counter_operand = if branch.is_only_path_to_target() { + debug!( + " {:?} has only one incoming edge (from {:?}), so adding a \ + counter", + branch, branching_bcb + ); + self.get_or_make_counter_operand( + branch.target_bcb, + &mut collect_intermediate_expressions, + )? + } else { + debug!( + " {:?} has multiple incoming edges, so adding an edge counter", + branch + ); + self.get_or_make_edge_counter_operand( + branching_bcb, + branch.target_bcb, + &mut collect_intermediate_expressions, + )? + }; + if let Some(sumup_counter_operand) = + some_sumup_counter_operand.replace(branch_counter_operand) + { + let intermediate_expression = self.coverage_counters.make_expression( + branch_counter_operand, + Op::Add, + sumup_counter_operand, + || None, + ); + debug!( + " [new intermediate expression: {}]", + self.format_counter(&intermediate_expression) + ); + let intermediate_expression_operand = + intermediate_expression.as_operand_id(); + collect_intermediate_expressions.push(intermediate_expression); + some_sumup_counter_operand.replace(intermediate_expression_operand); + } + } + } + let sumup_counter_operand = + some_sumup_counter_operand.expect("sumup_counter_operand should have a value"); + debug!( + "Making an expression for the selected expression_branch: {:?} \ + (expression_branch predecessors: {:?})", + expression_branch, + self.bcb_predecessors(expression_branch.target_bcb), + ); + let expression = self.coverage_counters.make_expression( + branching_counter_operand, + Op::Subtract, + sumup_counter_operand, + || Some(format!("{:?}", expression_branch)), + ); + debug!( + "{:?} gets an expression: {}", + expression_branch, + self.format_counter(&expression) + ); + let bcb = expression_branch.target_bcb; + if expression_branch.is_only_path_to_target() { + self.basic_coverage_blocks[bcb].set_counter(expression)?; + } else { + self.basic_coverage_blocks[bcb] + .set_edge_counter_from(branching_bcb, expression)?; + } + } + } + + if traversal.is_complete() { + Ok(collect_intermediate_expressions) + } else { + Error::from_string(format!( + "`TraverseCoverageGraphWithLoops` missed some `BasicCoverageBlock`s: {:?}", + traversal.unvisited(), + )) + } + } + + fn get_or_make_counter_operand( + &mut self, + bcb: BasicCoverageBlock, + collect_intermediate_expressions: &mut Vec, + ) -> Result { + self.recursive_get_or_make_counter_operand(bcb, collect_intermediate_expressions, 1) + } + + fn recursive_get_or_make_counter_operand( + &mut self, + bcb: BasicCoverageBlock, + collect_intermediate_expressions: &mut Vec, + debug_indent_level: usize, + ) -> Result { + Ok({ + if let Some(counter_kind) = self.basic_coverage_blocks[bcb].counter() { + debug!( + "{}{:?} already has a counter: {}", + NESTED_INDENT.repeat(debug_indent_level), + bcb, + self.format_counter(counter_kind), + ); + counter_kind.as_operand_id() + } else { + let one_path_to_target = self.bcb_has_one_path_to_target(bcb); + if one_path_to_target || self.bcb_predecessors(bcb).contains(&bcb) { + let counter_kind = + self.coverage_counters.make_counter(|| Some(format!("{:?}", bcb))); + if one_path_to_target { + debug!( + "{}{:?} gets a new counter: {}", + NESTED_INDENT.repeat(debug_indent_level), + bcb, + self.format_counter(&counter_kind), + ); + } else { + debug!( + "{}{:?} has itself as its own predecessor. It can't be part of its own \ + Expression sum, so it will get its own new counter: {}. (Note, the \ + compiled code will generate an infinite loop.)", + NESTED_INDENT.repeat(debug_indent_level), + bcb, + self.format_counter(&counter_kind), + ); + } + self.basic_coverage_blocks[bcb].set_counter(counter_kind)? + } else { + let mut predecessors = self.bcb_predecessors(bcb).clone().into_iter(); + debug!( + "{}{:?} has multiple incoming edges and will get an expression that sums \ + them up...", + NESTED_INDENT.repeat(debug_indent_level), + bcb, + ); + let first_edge_counter_operand = self + .recursive_get_or_make_edge_counter_operand( + predecessors.next().unwrap(), + bcb, + collect_intermediate_expressions, + debug_indent_level + 1, + )?; + let mut some_sumup_edge_counter_operand = None; + for predecessor in predecessors { + let edge_counter_operand = self + .recursive_get_or_make_edge_counter_operand( + predecessor, + bcb, + collect_intermediate_expressions, + debug_indent_level + 1, + )?; + if let Some(sumup_edge_counter_operand) = + some_sumup_edge_counter_operand.replace(edge_counter_operand) + { + let intermediate_expression = self.coverage_counters.make_expression( + sumup_edge_counter_operand, + Op::Add, + edge_counter_operand, + || None, + ); + debug!( + "{}new intermediate expression: {}", + NESTED_INDENT.repeat(debug_indent_level), + self.format_counter(&intermediate_expression) + ); + let intermediate_expression_operand = + intermediate_expression.as_operand_id(); + collect_intermediate_expressions.push(intermediate_expression); + some_sumup_edge_counter_operand + .replace(intermediate_expression_operand); + } + } + let counter_kind = self.coverage_counters.make_expression( + first_edge_counter_operand, + Op::Add, + some_sumup_edge_counter_operand.unwrap(), + || Some(format!("{:?}", bcb)), + ); + debug!( + "{}{:?} gets a new counter (sum of predecessor counters): {}", + NESTED_INDENT.repeat(debug_indent_level), + bcb, + self.format_counter(&counter_kind) + ); + self.basic_coverage_blocks[bcb].set_counter(counter_kind)? + } + } + }) + } + + fn get_or_make_edge_counter_operand( + &mut self, + from_bcb: BasicCoverageBlock, + to_bcb: BasicCoverageBlock, + collect_intermediate_expressions: &mut Vec, + ) -> Result { + self.recursive_get_or_make_edge_counter_operand( + from_bcb, + to_bcb, + collect_intermediate_expressions, + 1, + ) + } + + fn recursive_get_or_make_edge_counter_operand( + &mut self, + from_bcb: BasicCoverageBlock, + to_bcb: BasicCoverageBlock, + collect_intermediate_expressions: &mut Vec, + debug_indent_level: usize, + ) -> Result { + Ok({ + let successors = self.bcb_successors(from_bcb).iter(); + if successors.len() > 1 { + if let Some(counter_kind) = + self.basic_coverage_blocks[to_bcb].edge_counter_from(from_bcb) + { + debug!( + "{}Edge {:?}->{:?} already has a counter: {}", + NESTED_INDENT.repeat(debug_indent_level), + from_bcb, + to_bcb, + self.format_counter(counter_kind) + ); + counter_kind.as_operand_id() + } else { + let counter_kind = self + .coverage_counters + .make_counter(|| Some(format!("{:?}->{:?}", from_bcb, to_bcb))); + debug!( + "{}Edge {:?}->{:?} gets a new counter: {}", + NESTED_INDENT.repeat(debug_indent_level), + from_bcb, + to_bcb, + self.format_counter(&counter_kind) + ); + self.basic_coverage_blocks[to_bcb] + .set_edge_counter_from(from_bcb, counter_kind)? + } + } else { + self.recursive_get_or_make_counter_operand( + from_bcb, + collect_intermediate_expressions, + debug_indent_level + 1, + )? + } + }) + } + + #[inline] + fn bcb_predecessors(&self, bcb: BasicCoverageBlock) -> &Vec { + &self.basic_coverage_blocks.predecessors[bcb] + } + + #[inline] + fn bcb_successors(&self, bcb: BasicCoverageBlock) -> &Vec { + &self.basic_coverage_blocks.successors[bcb] + } + + #[inline] + fn bcb_branches(&self, from_bcb: BasicCoverageBlock) -> Vec { + self.bcb_successors(from_bcb) + .iter() + .map(|&to_bcb| BcbBranch::from_to(from_bcb, to_bcb, &self.basic_coverage_blocks)) + .collect::>() + } + + /// Returns true if the BasicCoverageBlock has zero or one incoming edge. (If zero, it should be + /// the entry point for the function.) + #[inline] + fn bcb_has_one_path_to_target(&self, bcb: BasicCoverageBlock) -> bool { + self.bcb_predecessors(bcb).len() <= 1 + } + + #[inline] + fn bcb_is_dominated_by(&self, node: BasicCoverageBlock, dom: BasicCoverageBlock) -> bool { + self.basic_coverage_blocks.is_dominated_by(node, dom) + } + + #[inline] + fn format_counter(&self, counter_kind: &CoverageKind) -> String { + self.coverage_counters.debug_counters.format_counter(counter_kind) + } +} diff --git a/compiler/rustc_mir/src/transform/coverage/debug.rs b/compiler/rustc_mir/src/transform/coverage/debug.rs index 835636946bab9..7080975aee5ce 100644 --- a/compiler/rustc_mir/src/transform/coverage/debug.rs +++ b/compiler/rustc_mir/src/transform/coverage/debug.rs @@ -23,6 +23,10 @@ pub(crate) fn debug_options<'a>() -> &'a DebugOptions { &DEBUG_OPTIONS.get_or_init(|| DebugOptions::new()) } +/// Parses and maintains coverage-specific debug options captured from the environment variable +/// "RUSTC_COVERAGE_DEBUG_OPTIONS", if set. Options can be set on the command line by, for example: +/// +/// $ RUSTC_COVERAGE_DEBUG_OPTIONS=counter-format=block,allow_unused_expressions=n cargo build #[derive(Debug, Clone)] pub(crate) struct DebugOptions { pub allow_unused_expressions: bool, @@ -127,18 +131,17 @@ impl Default for ExpressionFormat { } } -#[derive(Debug)] -struct DebugCounter { - counter_kind: CoverageKind, - some_block_label: Option, -} - -impl DebugCounter { - fn new(counter_kind: CoverageKind, some_block_label: Option) -> Self { - Self { counter_kind, some_block_label } - } -} - +/// If enabled, this struct maintains a map from `CoverageKind` IDs (as `ExpressionOperandId`) to +/// the `CoverageKind` data and optional label (normally, the counter's associated +/// `BasicCoverageBlock` format string, if any). +/// +/// Use `format_counter` to convert one of these `CoverageKind` counters to a debug output string, +/// as directed by the `DebugOptions`. This allows the format of counter labels in logs and dump +/// files (including the `CoverageGraph` graphviz file) to be changed at runtime, via environment +/// variable. +/// +/// `DebugCounters` supports a recursive rendering of `Expression` counters, so they can be +/// presented as nested expressions such as `(bcb3 - (bcb0 + bcb1))`. pub(crate) struct DebugCounters { some_counters: Option>, } @@ -259,6 +262,21 @@ impl DebugCounters { } } +/// A non-public support class to `DebugCounters`. +#[derive(Debug)] +struct DebugCounter { + counter_kind: CoverageKind, + some_block_label: Option, +} + +impl DebugCounter { + fn new(counter_kind: CoverageKind, some_block_label: Option) -> Self { + Self { counter_kind, some_block_label } + } +} + +/// If enabled, this data structure captures additional debugging information used when generating +/// a Graphviz (.dot file) representation of the `CoverageGraph`, for debugging purposes. pub(crate) struct GraphvizData { some_bcb_to_coverage_spans_with_counters: Option>>, @@ -364,6 +382,10 @@ impl GraphvizData { } } +/// If enabled, this struct captures additional data used to track whether expressions were used, +/// directly or indirectly, to compute the coverage counts for all `CoverageSpan`s, and any that are +/// _not_ used are retained in the `unused_expressions` Vec, to be included in debug output (logs +/// and/or a `CoverageGraph` graphviz output). pub(crate) struct UsedExpressions { some_used_expression_operands: Option>>, @@ -492,6 +514,7 @@ impl UsedExpressions { } } +/// Generates the MIR pass `CoverageSpan`-specific spanview dump file. pub(crate) fn dump_coverage_spanview( tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>, @@ -531,6 +554,7 @@ fn span_viewables( span_viewables } +/// Generates the MIR pass coverage-specific graphviz dump file. pub(crate) fn dump_coverage_graphviz( tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>, @@ -678,6 +702,8 @@ fn bcb_to_string_sections( sections } +/// Returns a simple string representation of a `TerminatorKind` variant, indenpendent of any +/// values it might hold. pub(crate) fn term_type(kind: &TerminatorKind<'tcx>) -> &'static str { match kind { TerminatorKind::Goto { .. } => "Goto", diff --git a/compiler/rustc_mir/src/transform/coverage/graph.rs b/compiler/rustc_mir/src/transform/coverage/graph.rs index 23ec026468089..2c1b34ab5e5ea 100644 --- a/compiler/rustc_mir/src/transform/coverage/graph.rs +++ b/compiler/rustc_mir/src/transform/coverage/graph.rs @@ -12,11 +12,17 @@ use std::ops::{Index, IndexMut}; const ID_SEPARATOR: &str = ","; +/// A coverage-specific simplification of the MIR control flow graph (CFG). The `CoverageGraph`s +/// nodes are `BasicCoverageBlock`s, which encompass one or more MIR `BasicBlock`s, plus a +/// `CoverageKind` counter (to be added by `CoverageCounters::make_bcb_counters`), and an optional +/// set of additional counters--if needed--to count incoming edges, if there are more than one. +/// (These "edge counters" are eventually converted into new MIR `BasicBlock`s.) pub(crate) struct CoverageGraph { bcbs: IndexVec, bb_to_bcb: IndexVec>, pub successors: IndexVec>, pub predecessors: IndexVec>, + dominators: Option>, } impl CoverageGraph { @@ -55,7 +61,11 @@ impl CoverageGraph { } } - Self { bcbs, bb_to_bcb, successors, predecessors } + let mut basic_coverage_blocks = + Self { bcbs, bb_to_bcb, successors, predecessors, dominators: None }; + let dominators = dominators::dominators(&basic_coverage_blocks); + basic_coverage_blocks.dominators = Some(dominators); + basic_coverage_blocks } fn compute_basic_coverage_blocks( @@ -190,8 +200,13 @@ impl CoverageGraph { } #[inline(always)] - pub fn compute_bcb_dominators(&self) -> Dominators { - dominators::dominators(self) + pub fn is_dominated_by(&self, node: BasicCoverageBlock, dom: BasicCoverageBlock) -> bool { + self.dominators.as_ref().unwrap().is_dominated_by(node, dom) + } + + #[inline(always)] + pub fn dominators(&self) -> &Dominators { + self.dominators.as_ref().unwrap() } } @@ -498,12 +513,9 @@ pub(crate) struct TraverseCoverageGraphWithLoops { } impl TraverseCoverageGraphWithLoops { - pub fn new( - basic_coverage_blocks: &CoverageGraph, - dominators: &Dominators, - ) -> Self { + pub fn new(basic_coverage_blocks: &CoverageGraph) -> Self { let start_bcb = basic_coverage_blocks.start_node(); - let backedges = find_loop_backedges(basic_coverage_blocks, dominators); + let backedges = find_loop_backedges(basic_coverage_blocks); let mut context_stack = Vec::new(); context_stack.push(TraversalContext { loop_backedges: None, worklist: vec![start_bcb] }); // `context_stack` starts with a `TraversalContext` for the main function context (beginning @@ -558,7 +570,6 @@ impl TraverseCoverageGraphWithLoops { fn find_loop_backedges( basic_coverage_blocks: &CoverageGraph, - dominators: &Dominators, ) -> IndexVec> { let num_bcbs = basic_coverage_blocks.num_nodes(); let mut backedges = IndexVec::from_elem_n(Vec::::new(), num_bcbs); @@ -566,7 +577,7 @@ fn find_loop_backedges( // Identify loops by their backedges for (bcb, _) in basic_coverage_blocks.iter_enumerated() { for &successor in &basic_coverage_blocks.successors[bcb] { - if dominators.is_dominated_by(bcb, successor) { + if basic_coverage_blocks.is_dominated_by(bcb, successor) { let loop_header = successor; let backedge_from_bcb = bcb; debug!( diff --git a/compiler/rustc_mir/src/transform/coverage/mod.rs b/compiler/rustc_mir/src/transform/coverage/mod.rs index e81e5de1d3805..c84ccf19213b8 100644 --- a/compiler/rustc_mir/src/transform/coverage/mod.rs +++ b/compiler/rustc_mir/src/transform/coverage/mod.rs @@ -1,25 +1,21 @@ pub mod query; +mod counters; mod debug; mod graph; mod spans; -use debug::NESTED_INDENT; -use graph::{ - BasicCoverageBlock, BasicCoverageBlockData, BcbBranch, CoverageGraph, - TraverseCoverageGraphWithLoops, -}; +use counters::CoverageCounters; +use graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; use spans::{CoverageSpan, CoverageSpans}; use crate::transform::MirPass; use crate::util::pretty; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::graph::dominators::Dominators; use rustc_data_structures::graph::WithNumNodes; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; -use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; use rustc_middle::hir; use rustc_middle::hir::map::blocks::FnLikeNode; @@ -92,60 +88,24 @@ struct Instrumentor<'a, 'tcx> { pass_name: &'a str, tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>, - hir_body: &'tcx rustc_hir::Body<'tcx>, - bcb_dominators: Dominators, + body_span: Span, basic_coverage_blocks: CoverageGraph, - function_source_hash: Option, - next_counter_id: u32, - num_expressions: u32, - debug_counters: debug::DebugCounters, + coverage_counters: CoverageCounters, } impl<'a, 'tcx> Instrumentor<'a, 'tcx> { fn new(pass_name: &'a str, tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self { let hir_body = hir_body(tcx, mir_body.source.def_id()); + let body_span = hir_body.value.span; + let function_source_hash = hash_mir_source(tcx, hir_body); let basic_coverage_blocks = CoverageGraph::from_mir(mir_body); - let bcb_dominators = basic_coverage_blocks.compute_bcb_dominators(); Self { pass_name, tcx, mir_body, - hir_body, + body_span, basic_coverage_blocks, - bcb_dominators, - function_source_hash: None, - next_counter_id: CounterValueReference::START.as_u32(), - num_expressions: 0, - debug_counters: debug::DebugCounters::new(), - } - } - - /// Counter IDs start from one and go up. - fn next_counter(&mut self) -> CounterValueReference { - assert!(self.next_counter_id < u32::MAX - self.num_expressions); - let next = self.next_counter_id; - self.next_counter_id += 1; - CounterValueReference::from(next) - } - - /// Expression IDs start from u32::MAX and go down because a Expression can reference - /// (add or subtract counts) of both Counter regions and Expression regions. The counter - /// expression operand IDs must be unique across both types. - fn next_expression(&mut self) -> InjectedExpressionId { - assert!(self.next_counter_id < u32::MAX - self.num_expressions); - let next = u32::MAX - self.num_expressions; - self.num_expressions += 1; - InjectedExpressionId::from(next) - } - - fn function_source_hash(&mut self) -> u64 { - match self.function_source_hash { - Some(hash) => hash, - None => { - let hash = hash_mir_source(self.tcx, self.hir_body); - self.function_source_hash.replace(hash); - hash - } + coverage_counters: CoverageCounters::new(function_source_hash), } } @@ -154,21 +114,29 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { let source_map = tcx.sess.source_map(); let mir_source = self.mir_body.source; let def_id = mir_source.def_id(); - let body_span = self.body_span(); + let body_span = self.body_span; debug!("instrumenting {:?}, span: {}", def_id, source_map.span_to_string(body_span)); - let dump_graphviz = tcx.sess.opts.debugging_opts.dump_mir_graphviz; + let mut graphviz_data = debug::GraphvizData::new(); + let mut debug_used_expressions = debug::UsedExpressions::new(); + let dump_graphviz = tcx.sess.opts.debugging_opts.dump_mir_graphviz; if dump_graphviz { - self.debug_counters.enable(); + graphviz_data.enable(); + self.coverage_counters.enable_debug(); } + if dump_graphviz || level_enabled!(tracing::Level::DEBUG) { + debug_used_expressions.enable(); + } + + //////////////////////////////////////////////////// + // Compute `CoverageSpan`s from the `CoverageGraph`. let coverage_spans = CoverageSpans::generate_coverage_spans( &self.mir_body, body_span, &self.basic_coverage_blocks, - &self.bcb_dominators, ); if pretty::dump_enabled(tcx, self.pass_name, def_id) { @@ -181,72 +149,82 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { ); } - // When debug logging, or generating the coverage graphviz output, initialize the following - // data structures: - let mut debug_used_expressions = debug::UsedExpressions::new(); - if level_enabled!(tracing::Level::DEBUG) || dump_graphviz { - debug_used_expressions.enable(); - } - - // When debugging with BCB graphviz output, initialize additional data structures. - let mut graphviz_data = debug::GraphvizData::new(); - if dump_graphviz { - graphviz_data.enable(); - } - - // Analyze the coverage graph (aka, BCB control flow graph), and inject expression-optimized - // counters. - let mut collect_intermediate_expressions = - Vec::with_capacity(self.basic_coverage_blocks.num_nodes()); - let result = self.make_bcb_counters(&coverage_spans, &mut collect_intermediate_expressions); - if result.is_ok() { - // If debugging, add any intermediate expressions (which are not associated with any - // BCB) to the `debug_used_expressions` map. - if debug_used_expressions.is_enabled() { - for intermediate_expression in &collect_intermediate_expressions { - debug_used_expressions.add_expression_operands(intermediate_expression); + //////////////////////////////////////////////////// + // Create an optimized mix of `Counter`s and `Expression`s for the `CoverageGraph`. Ensure + // every `CoverageSpan` has a `Counter` or `Expression` assigned to its `BasicCoverageBlock` + // and all `Expression` dependencies (operands) are also generated, for any other + // `BasicCoverageBlock`s not already associated with a `CoverageSpan`. + // + // Intermediate expressions (used to compute other `Expression` values), which have no + // direct associate to any `BasicCoverageBlock`, are returned in the method `Result`. + let intermediate_expressions_or_error = self + .coverage_counters + .make_bcb_counters(&mut self.basic_coverage_blocks, &coverage_spans); + + let (result, intermediate_expressions) = match intermediate_expressions_or_error { + Ok(intermediate_expressions) => { + // If debugging, add any intermediate expressions (which are not associated with any + // BCB) to the `debug_used_expressions` map. + if debug_used_expressions.is_enabled() { + for intermediate_expression in &intermediate_expressions { + debug_used_expressions.add_expression_operands(intermediate_expression); + } } - } - // Inject a counter for each `CoverageSpan`. - self.inject_coverage_span_counters( - coverage_spans, - &mut graphviz_data, - &mut debug_used_expressions, - ); + //////////////////////////////////////////////////// + // Remove the counter or edge counter from of each `CoverageSpan`s associated + // `BasicCoverageBlock`, and inject a `Coverage` statement into the MIR. + self.inject_coverage_span_counters( + coverage_spans, + &mut graphviz_data, + &mut debug_used_expressions, + ); - // Inject other counters created as dependencies of coverage span counters. - self.inject_indirect_counters(&mut graphviz_data, &mut debug_used_expressions); + //////////////////////////////////////////////////// + // For any remaining `BasicCoverageBlock` counters (that were not associated with + // any `CoverageSpan`), inject `Coverage` statements (_without_ code region `Span`s) + // to ensure `BasicCoverageBlock` counters that other `Expression`s may depend on + // are in fact counted, even though they don't directly contribute to counting + // their own independent code region's coverage. + self.inject_indirect_counters(&mut graphviz_data, &mut debug_used_expressions); - // Intermediate expressions will be injected as the final step, after generating debug - // output, if any. - } + // Intermediate expressions will be injected as the final step, after generating + // debug output, if any. + //////////////////////////////////////////////////// + + (Ok(()), intermediate_expressions) + } + Err(e) => (Err(e), Vec::new()), + }; if graphviz_data.is_enabled() { + // Even if there was an error, a partial CoverageGraph can still generate a useful + // graphviz output. debug::dump_coverage_graphviz( tcx, self.mir_body, self.pass_name, &self.basic_coverage_blocks, - &self.debug_counters, + &self.coverage_counters.debug_counters, &graphviz_data, - &collect_intermediate_expressions, + &intermediate_expressions, &debug_used_expressions, ); } - result.unwrap_or_else(|e: Error| { + if let Err(e) = result { bug!("Error processing: {:?}: {:?}", self.mir_body.source.def_id(), e) - }); + }; // Depending on current `debug_options()`, `alert_on_unused_expressions()` could panic, so // this check is performed as late as possible, to allow other debug output (logs and dump // files), which might be helpful in analyzing unused expressions, to still be generated. - debug_used_expressions.alert_on_unused_expressions(&self.debug_counters); + debug_used_expressions.alert_on_unused_expressions(&self.coverage_counters.debug_counters); + //////////////////////////////////////////////////// // Finally, inject the intermediate expressions collected along the way. - for intermediate_expression in collect_intermediate_expressions { - self.inject_intermediate_expression(intermediate_expression); + for intermediate_expression in intermediate_expressions { + inject_intermediate_expression(self.mir_body, intermediate_expression); } } @@ -266,7 +244,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { ) { let tcx = self.tcx; let source_map = tcx.sess.source_map(); - let body_span = self.body_span(); + let body_span = self.body_span; let source_file = source_map.lookup_source_file(body_span.lo()); let file_name = Symbol::intern(&source_file.name.to_string()); @@ -275,7 +253,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { let bcb = covspan.bcb; let span = covspan.span; let counter_kind = if let Some(&counter_operand) = bcb_counters[bcb].as_ref() { - self.make_identity_counter(counter_operand) + self.coverage_counters.make_identity_counter(counter_operand) } else if let Some(counter_kind) = self.bcb_data_mut(bcb).take_counter() { bcb_counters[bcb] = Some(counter_kind.as_operand_id()); debug_used_expressions.add_expression_operands(&counter_kind); @@ -289,10 +267,19 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } else { Some(make_code_region(file_name, &source_file, span, body_span)) }; - self.inject_statement(counter_kind, self.bcb_last_bb(bcb), some_code_region); + inject_statement(self.mir_body, counter_kind, self.bcb_last_bb(bcb), some_code_region); } } + /// Returns true if the type of `BasicCoverageBlock` (specifically, it's `BasicBlock`s + /// `TerminatorKind`) with the given `Span` (relative to the `body_span`) is known to produce + /// a redundant coverage count. + /// + /// There is at least one case for this, and if it's not handled, the last line in a function + /// will be double-counted. + /// + /// If this method returns `true`, the counter (which other `Expressions` may depend on) is + /// still injected, but without an associated code region. fn is_code_region_redundant( &self, bcb: BasicCoverageBlock, @@ -366,47 +353,22 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { let from_bb = self.bcb_last_bb(from_bcb); let to_bb = self.bcb_leader_bb(target_bcb); + let new_bb = inject_edge_counter_basic_block(self.mir_body, from_bb, to_bb); + graphviz_data.set_edge_counter(from_bcb, new_bb, &counter_kind); debug!( "Edge {:?} (last {:?}) -> {:?} (leader {:?}) requires a new MIR \ - BasicBlock, for unclaimed edge counter {}", + BasicBlock {:?}, for unclaimed edge counter {}", edge_from_bcb, from_bb, target_bcb, to_bb, - self.format_counter(&counter_kind), - ); - debug!( - " from_bb {:?} has successors: {:?}", - from_bb, - self.mir_body[from_bb].terminator().successors(), - ); - let span = - self.mir_body[from_bb].terminator().source_info.span.shrink_to_hi(); - let new_bb = self.mir_body.basic_blocks_mut().push(BasicBlockData { - statements: vec![], // counter will be injected here - terminator: Some(Terminator { - source_info: SourceInfo::outermost(span), - kind: TerminatorKind::Goto { target: to_bb }, - }), - is_cleanup: false, - }); - debug!( - "Edge from_bcb={:?} to to_bb={:?} has edge_counter={}", - from_bcb, new_bb, self.format_counter(&counter_kind), ); - graphviz_data.set_edge_counter(from_bcb, new_bb, &counter_kind); - let edge_ref = self.mir_body[from_bb] - .terminator_mut() - .successors_mut() - .find(|successor| **successor == to_bb) - .expect("from_bb should have a successor for to_bb"); - *edge_ref = new_bb; new_bb } else { - graphviz_data.add_bcb_dependency_counter(target_bcb, &counter_kind); let target_bb = self.bcb_last_bb(target_bcb); + graphviz_data.add_bcb_dependency_counter(target_bcb, &counter_kind); debug!( "{:?} ({:?}) gets a new Coverage statement for unclaimed counter {}", target_bcb, @@ -416,21 +378,16 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { target_bb }; - self.inject_statement(counter_kind, inject_to_bb, None); + inject_statement(self.mir_body, counter_kind, inject_to_bb, None); } CoverageKind::Expression { .. } => { - self.inject_intermediate_expression(counter_kind) + inject_intermediate_expression(self.mir_body, counter_kind) } _ => bug!("CoverageKind should be a counter"), } } } - #[inline] - fn format_counter(&self, counter_kind: &CoverageKind) -> String { - self.debug_counters.format_counter(counter_kind) - } - #[inline] fn bcb_leader_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock { self.bcb_data(bcb).leader_bb() @@ -457,596 +414,68 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } #[inline] - fn bcb_predecessors(&self, bcb: BasicCoverageBlock) -> &Vec { - &self.basic_coverage_blocks.predecessors[bcb] - } - - #[inline] - fn bcb_successors(&self, bcb: BasicCoverageBlock) -> &Vec { - &self.basic_coverage_blocks.successors[bcb] - } - - #[inline] - fn bcb_branches(&self, from_bcb: BasicCoverageBlock) -> Vec { - self.bcb_successors(from_bcb) - .iter() - .map(|&to_bcb| BcbBranch::from_to(from_bcb, to_bcb, &self.basic_coverage_blocks)) - .collect::>() - } - - /// Returns true if the BasicCoverageBlock has zero or one incoming edge. (If zero, it should be - /// the entry point for the function.) - #[inline] - fn bcb_has_one_path_to_target(&self, bcb: BasicCoverageBlock) -> bool { - self.bcb_predecessors(bcb).len() <= 1 - } - - #[inline] - fn bcb_is_dominated_by(&self, node: BasicCoverageBlock, dom: BasicCoverageBlock) -> bool { - self.bcb_dominators.is_dominated_by(node, dom) - } - - /// Traverse the BCB CFG and add either a `Counter` or `Expression` to ever BCB, to be - /// injected with `CoverageSpan`s. `Expressions` have no runtime overhead, so if a viable - /// expression (adding or subtracting two other counters or expressions) can compute the same - /// result as an embedded counter, an `Expression` should be used. - /// - /// If two `CoverageGraph` branch from another `BasicCoverageBlock`, one of the branches - /// can be counted by `Expression` by subtracting the other branch from the branching - /// block. Otherwise, the `BasicCoverageBlock` executed the least should have the `Counter`. - /// One way to predict which branch executes the least is by considering loops. A loop is exited - /// at a branch, so the branch that jumps to a `BasicCoverageBlock` outside the loop is almost - /// always executed less than the branch that does not exit the loop. - /// - /// Returns non-code-span expressions created to represent intermediate values (if required), - /// such as to add two counters so the result can be subtracted from another counter. - fn make_bcb_counters( - &mut self, - coverage_spans: &Vec, - collect_intermediate_expressions: &mut Vec, - ) -> Result<(), Error> { - debug!("make_bcb_counters(): adding a counter or expression to each BasicCoverageBlock"); - let num_bcbs = self.basic_coverage_blocks.num_nodes(); - - let mut bcbs_with_coverage = BitSet::new_empty(num_bcbs); - for covspan in coverage_spans { - bcbs_with_coverage.insert(covspan.bcb); - } - - // FIXME(richkadel): Add more comments to explain the logic here and in the rest of this - // function, and refactor this function to break it up into smaller functions that are - // easier to understand. - - let mut traversal = - TraverseCoverageGraphWithLoops::new(&self.basic_coverage_blocks, &self.bcb_dominators); - while let Some(bcb) = traversal.next() { - debug!("{:?} has {} successors:", bcb, self.bcb_successors(bcb).len()); - for &successor in self.bcb_successors(bcb) { - if successor == bcb { - debug!( - "{:?} has itself as its own successor. (Note, the compiled code will \ - generate an infinite loop.)", - bcb - ); - // Don't re-add this successor to the worklist. We are already processing it. - break; - } - for context in traversal.context_stack.iter_mut().rev() { - // Add successors of the current BCB to the appropriate context. Successors that - // stay within a loop are added to the BCBs context worklist. Successors that - // exit the loop (they are not dominated by the loop header) must be reachable - // from other BCBs outside the loop, and they will be added to a different - // worklist. - // - // Branching blocks (with more than one successor) must be processed before - // blocks with only one successor, to prevent unnecessarily complicating - // Expression`s by creating a Counter in a `BasicCoverageBlock` that the - // branching block would have given an `Expression` (or vice versa). - let (some_successor_to_add, some_loop_header) = - if let Some((_, loop_header)) = context.loop_backedges { - if self.bcb_is_dominated_by(successor, loop_header) { - (Some(successor), Some(loop_header)) - } else { - (None, None) - } - } else { - (Some(successor), None) - }; - if let Some(successor_to_add) = some_successor_to_add { - if self.bcb_successors(successor_to_add).len() > 1 { - debug!( - "{:?} successor is branching. Prioritize it at the beginning of \ - the {}", - successor_to_add, - if let Some(loop_header) = some_loop_header { - format!("worklist for the loop headed by {:?}", loop_header) - } else { - String::from("non-loop worklist") - }, - ); - context.worklist.insert(0, successor_to_add); - } else { - debug!( - "{:?} successor is non-branching. Defer it to the end of the {}", - successor_to_add, - if let Some(loop_header) = some_loop_header { - format!("worklist for the loop headed by {:?}", loop_header) - } else { - String::from("non-loop worklist") - }, - ); - context.worklist.push(successor_to_add); - } - break; - } - } - } - - if !bcbs_with_coverage.contains(bcb) { - debug!( - "{:?} does not have any `CoverageSpan`s. A counter will only be added if \ - and when a covered BCB has an expression dependency.", - bcb, - ); - continue; - } - - debug!("{:?} has at least one `CoverageSpan`. Get or make its counter", bcb); - let bcb_counter_operand = - self.get_or_make_counter_operand(bcb, collect_intermediate_expressions)?; - - let branch_needs_a_counter = - |branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none(); - - let branches = self.bcb_branches(bcb); - let needs_branch_counters = - branches.len() > 1 && branches.iter().any(branch_needs_a_counter); - - if needs_branch_counters { - let branching_bcb = bcb; - let branching_counter_operand = bcb_counter_operand; - - debug!( - "{:?} has some branch(es) without counters:\n {}", - branching_bcb, - branches - .iter() - .map(|branch| { - format!( - "{:?}: {:?}", - branch, - branch.counter(&self.basic_coverage_blocks) - ) - }) - .collect::>() - .join("\n "), - ); - - // At most one of the branches (or its edge, from the branching_bcb, - // if the branch has multiple incoming edges) can have a counter computed by - // expression. - // - // If at least one of the branches leads outside of a loop (`found_loop_exit` is - // true), and at least one other branch does not exit the loop (the first of which - // is captured in `some_reloop_branch`), it's likely any reloop branch will be - // executed far more often than loop exit branch, making the reloop branch a better - // candidate for an expression. - let mut some_reloop_branch: Option = None; - for context in traversal.context_stack.iter().rev() { - if let Some((backedge_from_bcbs, _)) = &context.loop_backedges { - let mut found_loop_exit = false; - for &branch in branches.iter() { - if backedge_from_bcbs.iter().any(|&backedge_from_bcb| { - self.bcb_is_dominated_by(backedge_from_bcb, branch.target_bcb) - }) { - if let Some(reloop_branch) = some_reloop_branch { - if reloop_branch.counter(&self.basic_coverage_blocks).is_none() - { - // we already found a candidate reloop_branch that still - // needs a counter - continue; - } - } - // The path from branch leads back to the top of the loop. Set this - // branch as the `reloop_branch`. If this branch already has a - // counter, and we find another reloop branch that doesn't have a - // counter yet, that branch will be selected as the `reloop_branch` - // instead. - some_reloop_branch = Some(branch); - } else { - // The path from branch leads outside this loop - found_loop_exit = true; - } - if found_loop_exit - && some_reloop_branch.filter(branch_needs_a_counter).is_some() - { - // Found both a branch that exits the loop and a branch that returns - // to the top of the loop (`reloop_branch`), and the `reloop_branch` - // doesn't already have a counter. - break; - } - } - if !found_loop_exit { - debug!( - "No branches exit the loop, so any branch without an existing \ - counter can have the `Expression`." - ); - break; - } - if some_reloop_branch.is_some() { - debug!( - "Found a branch that exits the loop and a branch the loops back to \ - the top of the loop (`reloop_branch`). The `reloop_branch` will \ - get the `Expression`, as long as it still needs a counter." - ); - break; - } - // else all branches exited this loop context, so run the same checks with - // the outer loop(s) - } - } - - // Select a branch for the expression, either the recommended `reloop_branch`, or - // if none was found, select any branch. - let expression_branch = if let Some(reloop_branch_without_counter) = - some_reloop_branch.filter(branch_needs_a_counter) - { - debug!( - "Selecting reloop_branch={:?} that still needs a counter, to get the \ - `Expression`", - reloop_branch_without_counter - ); - reloop_branch_without_counter - } else { - let &branch_without_counter = branches - .iter() - .find(|&&branch| branch.counter(&self.basic_coverage_blocks).is_none()) - .expect( - "needs_branch_counters was `true` so there should be at least one \ - branch", - ); - debug!( - "Selecting any branch={:?} that still needs a counter, to get the \ - `Expression` because there was no `reloop_branch`, or it already had a \ - counter", - branch_without_counter - ); - branch_without_counter - }; - - // Assign a Counter or Expression to each branch, plus additional - // `Expression`s, as needed, to sum up intermediate results. - let mut some_sumup_counter_operand = None; - for branch in branches { - if branch != expression_branch { - let branch_counter_operand = if branch.is_only_path_to_target() { - debug!( - " {:?} has only one incoming edge (from {:?}), so adding a \ - counter", - branch, branching_bcb - ); - self.get_or_make_counter_operand( - branch.target_bcb, - collect_intermediate_expressions, - )? - } else { - debug!( - " {:?} has multiple incoming edges, so adding an edge counter", - branch - ); - self.get_or_make_edge_counter_operand( - branching_bcb, - branch.target_bcb, - collect_intermediate_expressions, - )? - }; - if let Some(sumup_counter_operand) = - some_sumup_counter_operand.replace(branch_counter_operand) - { - let intermediate_expression = self.make_expression( - branch_counter_operand, - Op::Add, - sumup_counter_operand, - || None, - ); - debug!( - " [new intermediate expression: {}]", - self.format_counter(&intermediate_expression) - ); - let intermediate_expression_operand = - intermediate_expression.as_operand_id(); - collect_intermediate_expressions.push(intermediate_expression); - some_sumup_counter_operand.replace(intermediate_expression_operand); - } - } - } - let sumup_counter_operand = - some_sumup_counter_operand.expect("sumup_counter_operand should have a value"); - debug!( - "Making an expression for the selected expression_branch: {:?} \ - (expression_branch predecessors: {:?})", - expression_branch, - self.bcb_predecessors(expression_branch.target_bcb), - ); - let expression = self.make_expression( - branching_counter_operand, - Op::Subtract, - sumup_counter_operand, - || Some(format!("{:?}", expression_branch)), - ); - debug!( - "{:?} gets an expression: {}", - expression_branch, - self.format_counter(&expression) - ); - if expression_branch.is_only_path_to_target() { - self.bcb_data_mut(expression_branch.target_bcb).set_counter(expression)?; - } else { - self.bcb_data_mut(expression_branch.target_bcb) - .set_edge_counter_from(branching_bcb, expression)?; - } - } - } - - if traversal.is_complete() { - Ok(()) - } else { - Error::from_string(format!( - "`TraverseCoverageGraphWithLoops` missed some `BasicCoverageBlock`s: {:?}", - traversal.unvisited(), - )) - } - } - - fn get_or_make_counter_operand( - &mut self, - bcb: BasicCoverageBlock, - collect_intermediate_expressions: &mut Vec, - ) -> Result { - self.recursive_get_or_make_counter_operand(bcb, collect_intermediate_expressions, 1) - } - - fn recursive_get_or_make_counter_operand( - &mut self, - bcb: BasicCoverageBlock, - collect_intermediate_expressions: &mut Vec, - debug_indent_level: usize, - ) -> Result { - Ok({ - if let Some(counter_kind) = self.basic_coverage_blocks[bcb].counter() { - debug!( - "{}{:?} already has a counter: {}", - NESTED_INDENT.repeat(debug_indent_level), - bcb, - self.format_counter(counter_kind), - ); - counter_kind.as_operand_id() - } else { - let one_path_to_target = self.bcb_has_one_path_to_target(bcb); - if one_path_to_target || self.bcb_predecessors(bcb).contains(&bcb) { - let counter_kind = self.make_counter(|| Some(format!("{:?}", bcb))); - if one_path_to_target { - debug!( - "{}{:?} gets a new counter: {}", - NESTED_INDENT.repeat(debug_indent_level), - bcb, - self.format_counter(&counter_kind), - ); - } else { - debug!( - "{}{:?} has itself as its own predecessor. It can't be part of its own \ - Expression sum, so it will get its own new counter: {}. (Note, the \ - compiled code will generate an infinite loop.)", - NESTED_INDENT.repeat(debug_indent_level), - bcb, - self.format_counter(&counter_kind), - ); - } - self.basic_coverage_blocks[bcb].set_counter(counter_kind)? - } else { - let mut predecessors = self.bcb_predecessors(bcb).clone().into_iter(); - debug!( - "{}{:?} has multiple incoming edges and will get an expression that sums \ - them up...", - NESTED_INDENT.repeat(debug_indent_level), - bcb, - ); - let first_edge_counter_operand = self - .recursive_get_or_make_edge_counter_operand( - predecessors.next().unwrap(), - bcb, - collect_intermediate_expressions, - debug_indent_level + 1, - )?; - let mut some_sumup_edge_counter_operand = None; - for predecessor in predecessors { - let edge_counter_operand = self - .recursive_get_or_make_edge_counter_operand( - predecessor, - bcb, - collect_intermediate_expressions, - debug_indent_level + 1, - )?; - if let Some(sumup_edge_counter_operand) = - some_sumup_edge_counter_operand.replace(edge_counter_operand) - { - let intermediate_expression = self.make_expression( - sumup_edge_counter_operand, - Op::Add, - edge_counter_operand, - || None, - ); - debug!( - "{}new intermediate expression: {}", - NESTED_INDENT.repeat(debug_indent_level), - self.format_counter(&intermediate_expression) - ); - let intermediate_expression_operand = - intermediate_expression.as_operand_id(); - collect_intermediate_expressions.push(intermediate_expression); - some_sumup_edge_counter_operand - .replace(intermediate_expression_operand); - } - } - let counter_kind = self.make_expression( - first_edge_counter_operand, - Op::Add, - some_sumup_edge_counter_operand.unwrap(), - || Some(format!("{:?}", bcb)), - ); - debug!( - "{}{:?} gets a new counter (sum of predecessor counters): {}", - NESTED_INDENT.repeat(debug_indent_level), - bcb, - self.format_counter(&counter_kind) - ); - self.basic_coverage_blocks[bcb].set_counter(counter_kind)? - } - } - }) - } - - fn get_or_make_edge_counter_operand( - &mut self, - from_bcb: BasicCoverageBlock, - to_bcb: BasicCoverageBlock, - collect_intermediate_expressions: &mut Vec, - ) -> Result { - self.recursive_get_or_make_edge_counter_operand( - from_bcb, - to_bcb, - collect_intermediate_expressions, - 1, - ) - } - - fn recursive_get_or_make_edge_counter_operand( - &mut self, - from_bcb: BasicCoverageBlock, - to_bcb: BasicCoverageBlock, - collect_intermediate_expressions: &mut Vec, - debug_indent_level: usize, - ) -> Result { - Ok({ - let successors = self.bcb_successors(from_bcb).iter(); - if successors.len() > 1 { - if let Some(counter_kind) = - self.basic_coverage_blocks[to_bcb].edge_counter_from(from_bcb) - { - debug!( - "{}Edge {:?}->{:?} already has a counter: {}", - NESTED_INDENT.repeat(debug_indent_level), - from_bcb, - to_bcb, - self.format_counter(counter_kind) - ); - counter_kind.as_operand_id() - } else { - let counter_kind = - self.make_counter(|| Some(format!("{:?}->{:?}", from_bcb, to_bcb))); - debug!( - "{}Edge {:?}->{:?} gets a new counter: {}", - NESTED_INDENT.repeat(debug_indent_level), - from_bcb, - to_bcb, - self.format_counter(&counter_kind) - ); - self.basic_coverage_blocks[to_bcb] - .set_edge_counter_from(from_bcb, counter_kind)? - } - } else { - self.recursive_get_or_make_counter_operand( - from_bcb, - collect_intermediate_expressions, - debug_indent_level + 1, - )? - } - }) - } - - fn make_counter(&mut self, block_label_fn: F) -> CoverageKind - where - F: Fn() -> Option, - { - let counter = CoverageKind::Counter { - function_source_hash: self.function_source_hash(), - id: self.next_counter(), - }; - if self.debug_counters.is_enabled() { - self.debug_counters.add_counter(&counter, (block_label_fn)()); - } - counter - } - - fn make_expression( - &mut self, - lhs: ExpressionOperandId, - op: Op, - rhs: ExpressionOperandId, - block_label_fn: F, - ) -> CoverageKind - where - F: Fn() -> Option, - { - let id = self.next_expression(); - let expression = CoverageKind::Expression { id, lhs, op, rhs }; - if self.debug_counters.is_enabled() { - self.debug_counters.add_counter(&expression, (block_label_fn)()); - } - expression - } - - fn make_identity_counter(&mut self, counter_operand: ExpressionOperandId) -> CoverageKind { - let some_block_label = if self.debug_counters.is_enabled() { - self.debug_counters.some_block_label(counter_operand).cloned() - } else { - None - }; - self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO, || { - some_block_label.clone() - }) + fn format_counter(&self, counter_kind: &CoverageKind) -> String { + self.coverage_counters.debug_counters.format_counter(counter_kind) } +} - fn inject_statement( - &mut self, - counter_kind: CoverageKind, - bb: BasicBlock, - some_code_region: Option, - ) { - debug!( - " injecting statement {:?} for {:?} at code region: {:?}", - counter_kind, bb, some_code_region - ); - let data = &mut self.mir_body[bb]; - let source_info = data.terminator().source_info; - let statement = Statement { - source_info, - kind: StatementKind::Coverage(box Coverage { - kind: counter_kind, - code_region: some_code_region, - }), - }; - data.statements.push(statement); - } +fn inject_edge_counter_basic_block( + mir_body: &mut mir::Body<'tcx>, + from_bb: BasicBlock, + to_bb: BasicBlock, +) -> BasicBlock { + let span = mir_body[from_bb].terminator().source_info.span.shrink_to_hi(); + let new_bb = mir_body.basic_blocks_mut().push(BasicBlockData { + statements: vec![], // counter will be injected here + terminator: Some(Terminator { + source_info: SourceInfo::outermost(span), + kind: TerminatorKind::Goto { target: to_bb }, + }), + is_cleanup: false, + }); + let edge_ref = mir_body[from_bb] + .terminator_mut() + .successors_mut() + .find(|successor| **successor == to_bb) + .expect("from_bb should have a successor for to_bb"); + *edge_ref = new_bb; + new_bb +} - // Non-code expressions are injected into the coverage map, without generating executable code. - fn inject_intermediate_expression(&mut self, expression: CoverageKind) { - debug_assert!(if let CoverageKind::Expression { .. } = expression { true } else { false }); - debug!(" injecting non-code expression {:?}", expression); - let inject_in_bb = mir::START_BLOCK; - let data = &mut self.mir_body[inject_in_bb]; - let source_info = data.terminator().source_info; - let statement = Statement { - source_info, - kind: StatementKind::Coverage(box Coverage { kind: expression, code_region: None }), - }; - data.statements.push(statement); - } +fn inject_statement( + mir_body: &mut mir::Body<'tcx>, + counter_kind: CoverageKind, + bb: BasicBlock, + some_code_region: Option, +) { + debug!( + " injecting statement {:?} for {:?} at code region: {:?}", + counter_kind, bb, some_code_region + ); + let data = &mut mir_body[bb]; + let source_info = data.terminator().source_info; + let statement = Statement { + source_info, + kind: StatementKind::Coverage(box Coverage { + kind: counter_kind, + code_region: some_code_region, + }), + }; + data.statements.push(statement); +} - #[inline(always)] - fn body_span(&self) -> Span { - self.hir_body.value.span - } +// Non-code expressions are injected into the coverage map, without generating executable code. +fn inject_intermediate_expression(mir_body: &mut mir::Body<'tcx>, expression: CoverageKind) { + debug_assert!(if let CoverageKind::Expression { .. } = expression { true } else { false }); + debug!(" injecting non-code expression {:?}", expression); + let inject_in_bb = mir::START_BLOCK; + let data = &mut mir_body[inject_in_bb]; + let source_info = data.terminator().source_info; + let statement = Statement { + source_info, + kind: StatementKind::Coverage(box Coverage { kind: expression, code_region: None }), + }; + data.statements.push(statement); } /// Convert the Span into its file name, start line and column, and end line and column diff --git a/compiler/rustc_mir/src/transform/coverage/spans.rs b/compiler/rustc_mir/src/transform/coverage/spans.rs index 491ebedd51ea2..61b5b1480532a 100644 --- a/compiler/rustc_mir/src/transform/coverage/spans.rs +++ b/compiler/rustc_mir/src/transform/coverage/spans.rs @@ -3,7 +3,6 @@ use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; use crate::util::spanview::source_range_no_file; -use rustc_data_structures::graph::dominators::Dominators; use rustc_data_structures::graph::WithNumNodes; use rustc_index::bit_set::BitSet; use rustc_middle::mir::{ @@ -179,9 +178,6 @@ pub struct CoverageSpans<'a, 'tcx> { /// The BasicCoverageBlock Control Flow Graph (BCB CFG). basic_coverage_blocks: &'a CoverageGraph, - /// The BCB CFG's dominators tree, used to compute the dominance relationships, if any. - bcb_dominators: &'a Dominators, - /// The initial set of `CoverageSpan`s, sorted by `Span` (`lo` and `hi`) and by relative /// dominance between the `BasicCoverageBlock`s of equal `Span`s. sorted_spans_iter: Option>, @@ -222,13 +218,11 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { mir_body: &'a mir::Body<'tcx>, body_span: Span, basic_coverage_blocks: &'a CoverageGraph, - bcb_dominators: &'a Dominators, ) -> Vec { let mut coverage_spans = CoverageSpans { mir_body, body_span, basic_coverage_blocks, - bcb_dominators, sorted_spans_iter: None, refined_spans: Vec::with_capacity(basic_coverage_blocks.num_nodes() * 2), some_curr: None, @@ -293,7 +287,7 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { // dominators always come after the dominated equal spans). When later // comparing two spans in order, the first will either dominate the second, // or they will have no dominator relationship. - self.bcb_dominators.rank_partial_cmp(b.bcb, a.bcb) + self.basic_coverage_blocks.dominators().rank_partial_cmp(b.bcb, a.bcb) } } else { // Sort hi() in reverse order so shorter spans are attempted after longer spans. @@ -649,7 +643,7 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { } fn span_bcb_is_dominated_by(&self, covspan: &CoverageSpan, dom_covspan: &CoverageSpan) -> bool { - self.bcb_dominators.is_dominated_by(covspan.bcb, dom_covspan.bcb) + self.basic_coverage_blocks.is_dominated_by(covspan.bcb, dom_covspan.bcb) } } From 17c1f1488eaace86884b7a544f44d8dc991f7ca9 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Thu, 22 Oct 2020 00:57:53 -0700 Subject: [PATCH 18/18] Refactored make_bcb_counters() into several smaller functions It could be refactored even further, if that's preferred/requested, but its much better than it was ;-). --- .../src/transform/coverage/counters.rs | 476 ++++++++---------- .../rustc_mir/src/transform/coverage/graph.rs | 72 ++- 2 files changed, 288 insertions(+), 260 deletions(-) diff --git a/compiler/rustc_mir/src/transform/coverage/counters.rs b/compiler/rustc_mir/src/transform/coverage/counters.rs index 81fb1eed739fc..a3ae302152486 100644 --- a/compiler/rustc_mir/src/transform/coverage/counters.rs +++ b/compiler/rustc_mir/src/transform/coverage/counters.rs @@ -156,284 +156,124 @@ impl<'a> BcbCounters<'a> { // easier to understand. let mut traversal = TraverseCoverageGraphWithLoops::new(&self.basic_coverage_blocks); - while let Some(bcb) = traversal.next() { - debug!("{:?} has {} successors:", bcb, self.bcb_successors(bcb).len()); - for &successor in self.bcb_successors(bcb) { - if successor == bcb { - debug!( - "{:?} has itself as its own successor. (Note, the compiled code will \ - generate an infinite loop.)", - bcb - ); - // Don't re-add this successor to the worklist. We are already processing it. - break; - } - for context in traversal.context_stack.iter_mut().rev() { - // Add successors of the current BCB to the appropriate context. Successors that - // stay within a loop are added to the BCBs context worklist. Successors that - // exit the loop (they are not dominated by the loop header) must be reachable - // from other BCBs outside the loop, and they will be added to a different - // worklist. - // - // Branching blocks (with more than one successor) must be processed before - // blocks with only one successor, to prevent unnecessarily complicating - // Expression`s by creating a Counter in a `BasicCoverageBlock` that the - // branching block would have given an `Expression` (or vice versa). - let (some_successor_to_add, some_loop_header) = - if let Some((_, loop_header)) = context.loop_backedges { - if self.bcb_is_dominated_by(successor, loop_header) { - (Some(successor), Some(loop_header)) - } else { - (None, None) - } - } else { - (Some(successor), None) - }; - if let Some(successor_to_add) = some_successor_to_add { - if self.bcb_successors(successor_to_add).len() > 1 { - debug!( - "{:?} successor is branching. Prioritize it at the beginning of \ - the {}", - successor_to_add, - if let Some(loop_header) = some_loop_header { - format!("worklist for the loop headed by {:?}", loop_header) - } else { - String::from("non-loop worklist") - }, - ); - context.worklist.insert(0, successor_to_add); - } else { - debug!( - "{:?} successor is non-branching. Defer it to the end of the {}", - successor_to_add, - if let Some(loop_header) = some_loop_header { - format!("worklist for the loop headed by {:?}", loop_header) - } else { - String::from("non-loop worklist") - }, - ); - context.worklist.push(successor_to_add); - } - break; - } + while let Some(bcb) = traversal.next(self.basic_coverage_blocks) { + if bcbs_with_coverage.contains(bcb) { + debug!("{:?} has at least one `CoverageSpan`. Get or make its counter", bcb); + let branching_counter_operand = + self.get_or_make_counter_operand(bcb, &mut collect_intermediate_expressions)?; + + if self.bcb_needs_branch_counters(bcb) { + self.make_branch_counters( + &mut traversal, + bcb, + branching_counter_operand, + &mut collect_intermediate_expressions, + )?; } - } - - if !bcbs_with_coverage.contains(bcb) { + } else { debug!( "{:?} does not have any `CoverageSpan`s. A counter will only be added if \ and when a covered BCB has an expression dependency.", bcb, ); - continue; } + } - debug!("{:?} has at least one `CoverageSpan`. Get or make its counter", bcb); - let bcb_counter_operand = - self.get_or_make_counter_operand(bcb, &mut collect_intermediate_expressions)?; - - let branch_needs_a_counter = - |branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none(); - - let branches = self.bcb_branches(bcb); - let needs_branch_counters = - branches.len() > 1 && branches.iter().any(branch_needs_a_counter); - - if needs_branch_counters { - let branching_bcb = bcb; - let branching_counter_operand = bcb_counter_operand; - - debug!( - "{:?} has some branch(es) without counters:\n {}", - branching_bcb, - branches - .iter() - .map(|branch| { - format!( - "{:?}: {:?}", - branch, - branch.counter(&self.basic_coverage_blocks) - ) - }) - .collect::>() - .join("\n "), - ); - - // At most one of the branches (or its edge, from the branching_bcb, - // if the branch has multiple incoming edges) can have a counter computed by - // expression. - // - // If at least one of the branches leads outside of a loop (`found_loop_exit` is - // true), and at least one other branch does not exit the loop (the first of which - // is captured in `some_reloop_branch`), it's likely any reloop branch will be - // executed far more often than loop exit branch, making the reloop branch a better - // candidate for an expression. - let mut some_reloop_branch: Option = None; - for context in traversal.context_stack.iter().rev() { - if let Some((backedge_from_bcbs, _)) = &context.loop_backedges { - let mut found_loop_exit = false; - for &branch in branches.iter() { - if backedge_from_bcbs.iter().any(|&backedge_from_bcb| { - self.bcb_is_dominated_by(backedge_from_bcb, branch.target_bcb) - }) { - if let Some(reloop_branch) = some_reloop_branch { - if reloop_branch.counter(&self.basic_coverage_blocks).is_none() - { - // we already found a candidate reloop_branch that still - // needs a counter - continue; - } - } - // The path from branch leads back to the top of the loop. Set this - // branch as the `reloop_branch`. If this branch already has a - // counter, and we find another reloop branch that doesn't have a - // counter yet, that branch will be selected as the `reloop_branch` - // instead. - some_reloop_branch = Some(branch); - } else { - // The path from branch leads outside this loop - found_loop_exit = true; - } - if found_loop_exit - && some_reloop_branch.filter(branch_needs_a_counter).is_some() - { - // Found both a branch that exits the loop and a branch that returns - // to the top of the loop (`reloop_branch`), and the `reloop_branch` - // doesn't already have a counter. - break; - } - } - if !found_loop_exit { - debug!( - "No branches exit the loop, so any branch without an existing \ - counter can have the `Expression`." - ); - break; - } - if some_reloop_branch.is_some() { - debug!( - "Found a branch that exits the loop and a branch the loops back to \ - the top of the loop (`reloop_branch`). The `reloop_branch` will \ - get the `Expression`, as long as it still needs a counter." - ); - break; - } - // else all branches exited this loop context, so run the same checks with - // the outer loop(s) - } - } + if traversal.is_complete() { + Ok(collect_intermediate_expressions) + } else { + Error::from_string(format!( + "`TraverseCoverageGraphWithLoops` missed some `BasicCoverageBlock`s: {:?}", + traversal.unvisited(), + )) + } + } - // Select a branch for the expression, either the recommended `reloop_branch`, or - // if none was found, select any branch. - let expression_branch = if let Some(reloop_branch_without_counter) = - some_reloop_branch.filter(branch_needs_a_counter) - { + fn make_branch_counters( + &mut self, + traversal: &mut TraverseCoverageGraphWithLoops, + branching_bcb: BasicCoverageBlock, + branching_counter_operand: ExpressionOperandId, + collect_intermediate_expressions: &mut Vec, + ) -> Result<(), Error> { + let branches = self.bcb_branches(branching_bcb); + debug!( + "{:?} has some branch(es) without counters:\n {}", + branching_bcb, + branches + .iter() + .map(|branch| { + format!("{:?}: {:?}", branch, branch.counter(&self.basic_coverage_blocks)) + }) + .collect::>() + .join("\n "), + ); + + let expression_branch = self.choose_preferred_expression_branch(traversal, &branches); + // Assign a Counter or Expression to each branch, plus additional + // `Expression`s, as needed, to sum up intermediate results. + let mut some_sumup_counter_operand = None; + for branch in branches { + if branch != expression_branch { + let branch_counter_operand = if branch.is_only_path_to_target() { debug!( - "Selecting reloop_branch={:?} that still needs a counter, to get the \ - `Expression`", - reloop_branch_without_counter + " {:?} has only one incoming edge (from {:?}), so adding a \ + counter", + branch, branching_bcb ); - reloop_branch_without_counter + self.get_or_make_counter_operand( + branch.target_bcb, + collect_intermediate_expressions, + )? } else { - let &branch_without_counter = branches - .iter() - .find(|&&branch| branch.counter(&self.basic_coverage_blocks).is_none()) - .expect( - "needs_branch_counters was `true` so there should be at least one \ - branch", - ); + debug!(" {:?} has multiple incoming edges, so adding an edge counter", branch); + self.get_or_make_edge_counter_operand( + branching_bcb, + branch.target_bcb, + collect_intermediate_expressions, + )? + }; + if let Some(sumup_counter_operand) = + some_sumup_counter_operand.replace(branch_counter_operand) + { + let intermediate_expression = self.coverage_counters.make_expression( + branch_counter_operand, + Op::Add, + sumup_counter_operand, + || None, + ); debug!( - "Selecting any branch={:?} that still needs a counter, to get the \ - `Expression` because there was no `reloop_branch`, or it already had a \ - counter", - branch_without_counter + " [new intermediate expression: {}]", + self.format_counter(&intermediate_expression) ); - branch_without_counter - }; - - // Assign a Counter or Expression to each branch, plus additional - // `Expression`s, as needed, to sum up intermediate results. - let mut some_sumup_counter_operand = None; - for branch in branches { - if branch != expression_branch { - let branch_counter_operand = if branch.is_only_path_to_target() { - debug!( - " {:?} has only one incoming edge (from {:?}), so adding a \ - counter", - branch, branching_bcb - ); - self.get_or_make_counter_operand( - branch.target_bcb, - &mut collect_intermediate_expressions, - )? - } else { - debug!( - " {:?} has multiple incoming edges, so adding an edge counter", - branch - ); - self.get_or_make_edge_counter_operand( - branching_bcb, - branch.target_bcb, - &mut collect_intermediate_expressions, - )? - }; - if let Some(sumup_counter_operand) = - some_sumup_counter_operand.replace(branch_counter_operand) - { - let intermediate_expression = self.coverage_counters.make_expression( - branch_counter_operand, - Op::Add, - sumup_counter_operand, - || None, - ); - debug!( - " [new intermediate expression: {}]", - self.format_counter(&intermediate_expression) - ); - let intermediate_expression_operand = - intermediate_expression.as_operand_id(); - collect_intermediate_expressions.push(intermediate_expression); - some_sumup_counter_operand.replace(intermediate_expression_operand); - } - } - } - let sumup_counter_operand = - some_sumup_counter_operand.expect("sumup_counter_operand should have a value"); - debug!( - "Making an expression for the selected expression_branch: {:?} \ - (expression_branch predecessors: {:?})", - expression_branch, - self.bcb_predecessors(expression_branch.target_bcb), - ); - let expression = self.coverage_counters.make_expression( - branching_counter_operand, - Op::Subtract, - sumup_counter_operand, - || Some(format!("{:?}", expression_branch)), - ); - debug!( - "{:?} gets an expression: {}", - expression_branch, - self.format_counter(&expression) - ); - let bcb = expression_branch.target_bcb; - if expression_branch.is_only_path_to_target() { - self.basic_coverage_blocks[bcb].set_counter(expression)?; - } else { - self.basic_coverage_blocks[bcb] - .set_edge_counter_from(branching_bcb, expression)?; + let intermediate_expression_operand = intermediate_expression.as_operand_id(); + collect_intermediate_expressions.push(intermediate_expression); + some_sumup_counter_operand.replace(intermediate_expression_operand); } } } - - if traversal.is_complete() { - Ok(collect_intermediate_expressions) + let sumup_counter_operand = + some_sumup_counter_operand.expect("sumup_counter_operand should have a value"); + debug!( + "Making an expression for the selected expression_branch: {:?} \ + (expression_branch predecessors: {:?})", + expression_branch, + self.bcb_predecessors(expression_branch.target_bcb), + ); + let expression = self.coverage_counters.make_expression( + branching_counter_operand, + Op::Subtract, + sumup_counter_operand, + || Some(format!("{:?}", expression_branch)), + ); + debug!("{:?} gets an expression: {}", expression_branch, self.format_counter(&expression)); + let bcb = expression_branch.target_bcb; + if expression_branch.is_only_path_to_target() { + self.basic_coverage_blocks[bcb].set_counter(expression)?; } else { - Error::from_string(format!( - "`TraverseCoverageGraphWithLoops` missed some `BasicCoverageBlock`s: {:?}", - traversal.unvisited(), - )) + self.basic_coverage_blocks[bcb].set_edge_counter_from(branching_bcb, expression)?; } + Ok(()) } fn get_or_make_counter_operand( @@ -604,6 +444,117 @@ impl<'a> BcbCounters<'a> { }) } + /// Select a branch for the expression, either the recommended `reloop_branch`, or + /// if none was found, select any branch. + fn choose_preferred_expression_branch( + &self, + traversal: &TraverseCoverageGraphWithLoops, + branches: &Vec, + ) -> BcbBranch { + let branch_needs_a_counter = + |branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none(); + + let some_reloop_branch = self.find_some_reloop_branch(traversal, &branches); + if let Some(reloop_branch_without_counter) = + some_reloop_branch.filter(branch_needs_a_counter) + { + debug!( + "Selecting reloop_branch={:?} that still needs a counter, to get the \ + `Expression`", + reloop_branch_without_counter + ); + reloop_branch_without_counter + } else { + let &branch_without_counter = branches + .iter() + .find(|&&branch| branch.counter(&self.basic_coverage_blocks).is_none()) + .expect( + "needs_branch_counters was `true` so there should be at least one \ + branch", + ); + debug!( + "Selecting any branch={:?} that still needs a counter, to get the \ + `Expression` because there was no `reloop_branch`, or it already had a \ + counter", + branch_without_counter + ); + branch_without_counter + } + } + + /// At most one of the branches (or its edge, from the branching_bcb, + /// if the branch has multiple incoming edges) can have a counter computed by + /// expression. + /// + /// If at least one of the branches leads outside of a loop (`found_loop_exit` is + /// true), and at least one other branch does not exit the loop (the first of which + /// is captured in `some_reloop_branch`), it's likely any reloop branch will be + /// executed far more often than loop exit branch, making the reloop branch a better + /// candidate for an expression. + fn find_some_reloop_branch( + &self, + traversal: &TraverseCoverageGraphWithLoops, + branches: &Vec, + ) -> Option { + let branch_needs_a_counter = + |branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none(); + + let mut some_reloop_branch: Option = None; + for context in traversal.context_stack.iter().rev() { + if let Some((backedge_from_bcbs, _)) = &context.loop_backedges { + let mut found_loop_exit = false; + for &branch in branches.iter() { + if backedge_from_bcbs.iter().any(|&backedge_from_bcb| { + self.bcb_is_dominated_by(backedge_from_bcb, branch.target_bcb) + }) { + if let Some(reloop_branch) = some_reloop_branch { + if reloop_branch.counter(&self.basic_coverage_blocks).is_none() { + // we already found a candidate reloop_branch that still + // needs a counter + continue; + } + } + // The path from branch leads back to the top of the loop. Set this + // branch as the `reloop_branch`. If this branch already has a + // counter, and we find another reloop branch that doesn't have a + // counter yet, that branch will be selected as the `reloop_branch` + // instead. + some_reloop_branch = Some(branch); + } else { + // The path from branch leads outside this loop + found_loop_exit = true; + } + if found_loop_exit + && some_reloop_branch.filter(branch_needs_a_counter).is_some() + { + // Found both a branch that exits the loop and a branch that returns + // to the top of the loop (`reloop_branch`), and the `reloop_branch` + // doesn't already have a counter. + break; + } + } + if !found_loop_exit { + debug!( + "No branches exit the loop, so any branch without an existing \ + counter can have the `Expression`." + ); + break; + } + if some_reloop_branch.is_some() { + debug!( + "Found a branch that exits the loop and a branch the loops back to \ + the top of the loop (`reloop_branch`). The `reloop_branch` will \ + get the `Expression`, as long as it still needs a counter." + ); + break; + } + // else all branches exited this loop context, so run the same checks with + // the outer loop(s) + } + } + some_reloop_branch + } + #[inline] fn bcb_predecessors(&self, bcb: BasicCoverageBlock) -> &Vec { &self.basic_coverage_blocks.predecessors[bcb] @@ -622,6 +573,13 @@ impl<'a> BcbCounters<'a> { .collect::>() } + fn bcb_needs_branch_counters(&self, bcb: BasicCoverageBlock) -> bool { + let branch_needs_a_counter = + |branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none(); + let branches = self.bcb_branches(bcb); + branches.len() > 1 && branches.iter().any(branch_needs_a_counter) + } + /// Returns true if the BasicCoverageBlock has zero or one incoming edge. (If zero, it should be /// the entry point for the function.) #[inline] diff --git a/compiler/rustc_mir/src/transform/coverage/graph.rs b/compiler/rustc_mir/src/transform/coverage/graph.rs index 2c1b34ab5e5ea..8406254170196 100644 --- a/compiler/rustc_mir/src/transform/coverage/graph.rs +++ b/compiler/rustc_mir/src/transform/coverage/graph.rs @@ -526,7 +526,7 @@ impl TraverseCoverageGraphWithLoops { Self { backedges, context_stack, visited } } - pub fn next(&mut self) -> Option { + pub fn next(&mut self, basic_coverage_blocks: &CoverageGraph) -> Option { debug!( "TraverseCoverageGraphWithLoops::next - context_stack: {:?}", self.context_stack.iter().rev().collect::>() @@ -551,11 +551,81 @@ impl TraverseCoverageGraphWithLoops { worklist: Vec::new(), }); } + self.extend_worklist(basic_coverage_blocks, next_bcb); return Some(next_bcb); } None } + pub fn extend_worklist( + &mut self, + basic_coverage_blocks: &CoverageGraph, + bcb: BasicCoverageBlock, + ) { + let successors = &basic_coverage_blocks.successors[bcb]; + debug!("{:?} has {} successors:", bcb, successors.len()); + for &successor in successors { + if successor == bcb { + debug!( + "{:?} has itself as its own successor. (Note, the compiled code will \ + generate an infinite loop.)", + bcb + ); + // Don't re-add this successor to the worklist. We are already processing it. + break; + } + for context in self.context_stack.iter_mut().rev() { + // Add successors of the current BCB to the appropriate context. Successors that + // stay within a loop are added to the BCBs context worklist. Successors that + // exit the loop (they are not dominated by the loop header) must be reachable + // from other BCBs outside the loop, and they will be added to a different + // worklist. + // + // Branching blocks (with more than one successor) must be processed before + // blocks with only one successor, to prevent unnecessarily complicating + // `Expression`s by creating a Counter in a `BasicCoverageBlock` that the + // branching block would have given an `Expression` (or vice versa). + let (some_successor_to_add, some_loop_header) = + if let Some((_, loop_header)) = context.loop_backedges { + if basic_coverage_blocks.is_dominated_by(successor, loop_header) { + (Some(successor), Some(loop_header)) + } else { + (None, None) + } + } else { + (Some(successor), None) + }; + if let Some(successor_to_add) = some_successor_to_add { + if basic_coverage_blocks.successors[successor_to_add].len() > 1 { + debug!( + "{:?} successor is branching. Prioritize it at the beginning of \ + the {}", + successor_to_add, + if let Some(loop_header) = some_loop_header { + format!("worklist for the loop headed by {:?}", loop_header) + } else { + String::from("non-loop worklist") + }, + ); + context.worklist.insert(0, successor_to_add); + } else { + debug!( + "{:?} successor is non-branching. Defer it to the end of the {}", + successor_to_add, + if let Some(loop_header) = some_loop_header { + format!("worklist for the loop headed by {:?}", loop_header) + } else { + String::from("non-loop worklist") + }, + ); + context.worklist.push(successor_to_add); + } + break; + } + } + } + } + pub fn is_complete(&self) -> bool { self.visited.count() == self.visited.domain_size() }