From b71dcdb3dd200b061285eb8d6e864b2cfb469718 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 21 Apr 2024 18:50:05 +1000 Subject: [PATCH 1/4] coverage: Add another simple test for guarded match arms --- tests/coverage/branch/guard-simple.cov-map | 24 ++++++++++++++++++ tests/coverage/branch/guard-simple.coverage | 28 +++++++++++++++++++++ tests/coverage/branch/guard-simple.rs | 19 ++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 tests/coverage/branch/guard-simple.cov-map create mode 100644 tests/coverage/branch/guard-simple.coverage create mode 100644 tests/coverage/branch/guard-simple.rs diff --git a/tests/coverage/branch/guard-simple.cov-map b/tests/coverage/branch/guard-simple.cov-map new file mode 100644 index 0000000000000..8eb9c54ff4bca --- /dev/null +++ b/tests/coverage/branch/guard-simple.cov-map @@ -0,0 +1,24 @@ +Function name: guard_simple::never_taken +Raw bytes (56): 0x[01, 01, 04, 01, 05, 11, 09, 0f, 0d, 05, 09, 08, 01, 08, 01, 02, 1e, 20, 05, 02, 02, 0e, 00, 1e, 05, 00, 22, 00, 24, 11, 01, 0e, 00, 1e, 20, 09, 06, 00, 0e, 00, 1e, 09, 00, 22, 00, 24, 0d, 01, 0e, 00, 10, 0b, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 4 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(4), rhs = Counter(2) +- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(3) +- expression 3 operands: lhs = Counter(1), rhs = Counter(2) +Number of file 0 mappings: 8 +- Code(Counter(0)) at (prev + 8, 1) to (start + 2, 30) +- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 2, 14) to (start + 0, 30) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 34) to (start + 0, 36) +- Code(Counter(4)) at (prev + 1, 14) to (start + 0, 30) +- Branch { true: Counter(2), false: Expression(1, Sub) } at (prev + 0, 14) to (start + 0, 30) + true = c2 + false = (c4 - c2) +- Code(Counter(2)) at (prev + 0, 34) to (start + 0, 36) +- Code(Counter(3)) at (prev + 1, 14) to (start + 0, 16) +- Code(Expression(2, Add)) at (prev + 2, 1) to (start + 0, 2) + = ((c1 + c2) + c3) + diff --git a/tests/coverage/branch/guard-simple.coverage b/tests/coverage/branch/guard-simple.coverage new file mode 100644 index 0000000000000..ac90c48c44342 --- /dev/null +++ b/tests/coverage/branch/guard-simple.coverage @@ -0,0 +1,28 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| |//@ compile-flags: -Zcoverage-options=branch + LL| |//@ llvm-cov-flags: --show-branches=count + LL| | + LL| |use core::hint::black_box; + LL| | + LL| 1|fn never_taken() { + LL| 1| match black_box(false) { + LL| 1| _ if black_box(false) => {} + ^0 + ------------------ + | Branch (LL:14): [True: 0, False: 1] + ------------------ + LL| 1| _ if black_box(false) => {} + ^0 + ------------------ + | Branch (LL:14): [True: 0, False: 1] + ------------------ + LL| 1| _ => {} + LL| | } + LL| 1|} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | never_taken(); + LL| |} + diff --git a/tests/coverage/branch/guard-simple.rs b/tests/coverage/branch/guard-simple.rs new file mode 100644 index 0000000000000..92fea499cd98d --- /dev/null +++ b/tests/coverage/branch/guard-simple.rs @@ -0,0 +1,19 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 +//@ compile-flags: -Zcoverage-options=branch +//@ llvm-cov-flags: --show-branches=count + +use core::hint::black_box; + +fn never_taken() { + match black_box(false) { + _ if black_box(false) => {} + _ if black_box(false) => {} + _ => {} + } +} + +#[coverage(off)] +fn main() { + never_taken(); +} From 79e5b4f7c06bd1f476aa833d0ca80c1472cce158 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 21 Apr 2024 14:11:14 +1000 Subject: [PATCH 2/4] coverage: Represent branches as a list of arms Within the `InstrumentCoverage` pass, we now represent branches as a list of arms, instead of a true/false pair, until we prepare the final table of mappings to be attached to the MIR body. (We then flatten the list into two-way branches by treating each arm as a branch between its success block, and the total of all later arms.) Currently all of the branches produced by MIR building are still two-way, but this is a step towards allowing many-way branches. --- .../src/coverageinfo/map_data.rs | 10 ++- .../src/coverage/counters.rs | 7 +- .../src/coverage/mappings.rs | 36 ++++----- .../rustc_mir_transform/src/coverage/mod.rs | 76 +++++++++++++------ 4 files changed, 87 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs index b969fe27a99be..a73ae810c2278 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs @@ -66,7 +66,15 @@ impl<'tcx> FunctionCoverageCollector<'tcx> { // For each expression ID that is directly used by one or more mappings, // mark it as not-yet-seen. This indicates that we expect to see a // corresponding `ExpressionUsed` statement during MIR traversal. - for term in function_coverage_info.mappings.iter().flat_map(|m| m.kind.terms()) { + for term in function_coverage_info + .mappings + .iter() + // For many-armed branches, some branch mappings will have expressions + // that don't correspond to any node in the control-flow graph, so don't + // expect to see `ExpressionUsed` statements for them. + .filter(|m| !matches!(m.kind, MappingKind::Branch { .. })) + .flat_map(|m| m.kind.terms()) + { if let CovTerm::Expression(id) = term { expressions_seen.remove(id); } diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index a8b0f4a8d6df4..40fdc905147b6 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -101,7 +101,12 @@ impl CoverageCounters { BcbCounter::Counter { id } } - fn make_expression(&mut self, lhs: BcbCounter, op: Op, rhs: BcbCounter) -> BcbCounter { + pub(super) fn make_expression( + &mut self, + lhs: BcbCounter, + op: Op, + rhs: BcbCounter, + ) -> BcbCounter { let new_expr = BcbExpression { lhs, op, rhs }; *self .expressions_memo diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs index 759bb7c1f9d96..81fe971cf9215 100644 --- a/compiler/rustc_mir_transform/src/coverage/mappings.rs +++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs @@ -21,14 +21,11 @@ pub(super) struct CodeMapping { pub(super) bcb: BasicCoverageBlock, } -/// This is separate from [`MCDCBranch`] to help prepare for larger changes -/// that will be needed for improved branch coverage in the future. -/// (See .) #[derive(Debug)] -pub(super) struct BranchPair { +pub(super) struct BranchArm { pub(super) span: Span, - pub(super) true_bcb: BasicCoverageBlock, - pub(super) false_bcb: BasicCoverageBlock, + pub(super) pre_guard_bcb: BasicCoverageBlock, + pub(super) arm_taken_bcb: BasicCoverageBlock, } /// Associates an MC/DC branch span with condition info besides fields for normal branch. @@ -56,7 +53,7 @@ pub(super) struct MCDCDecision { #[derive(Default)] pub(super) struct ExtractedMappings { pub(super) code_mappings: Vec, - pub(super) branch_pairs: Vec, + pub(super) branch_arm_lists: Vec>, pub(super) mcdc_bitmap_bytes: u32, pub(super) mcdc_branches: Vec, pub(super) mcdc_decisions: Vec, @@ -71,7 +68,7 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>( basic_coverage_blocks: &CoverageGraph, ) -> ExtractedMappings { let mut code_mappings = vec![]; - let mut branch_pairs = vec![]; + let mut branch_arm_lists = vec![]; let mut mcdc_bitmap_bytes = 0; let mut mcdc_branches = vec![]; let mut mcdc_decisions = vec![]; @@ -93,7 +90,7 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>( extract_refined_covspans(mir_body, hir_info, basic_coverage_blocks, &mut code_mappings); } - branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, basic_coverage_blocks)); + branch_arm_lists.extend(extract_branch_arm_lists(mir_body, hir_info, basic_coverage_blocks)); extract_mcdc_mappings( mir_body, @@ -106,7 +103,7 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>( ExtractedMappings { code_mappings, - branch_pairs, + branch_arm_lists, mcdc_bitmap_bytes, mcdc_branches, mcdc_decisions, @@ -121,7 +118,7 @@ impl ExtractedMappings { // Fully destructure self to make sure we don't miss any fields that have mappings. let Self { code_mappings, - branch_pairs, + branch_arm_lists, mcdc_bitmap_bytes: _, mcdc_branches, mcdc_decisions, @@ -136,9 +133,11 @@ impl ExtractedMappings { for &CodeMapping { span: _, bcb } in code_mappings { insert(bcb); } - for &BranchPair { true_bcb, false_bcb, .. } in branch_pairs { - insert(true_bcb); - insert(false_bcb); + for &BranchArm { span: _, pre_guard_bcb, arm_taken_bcb } in + branch_arm_lists.iter().flatten() + { + insert(pre_guard_bcb); + insert(arm_taken_bcb); } for &MCDCBranch { true_bcb, false_bcb, .. } in mcdc_branches { insert(true_bcb); @@ -179,16 +178,16 @@ fn resolve_block_markers( } // FIXME: There is currently a lot of redundancy between -// `extract_branch_pairs` and `extract_mcdc_mappings`. This is needed so +// `extract_branch_arm_lists` and `extract_mcdc_mappings`. This is needed so // that they can each be modified without interfering with the other, but in // the long term we should try to bring them together again when branch coverage // and MC/DC coverage support are more mature. -pub(super) fn extract_branch_pairs( +pub(super) fn extract_branch_arm_lists( mir_body: &mir::Body<'_>, hir_info: &ExtractedHirInfo, basic_coverage_blocks: &CoverageGraph, -) -> Vec { +) -> Vec> { let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { return vec![] }; let block_markers = resolve_block_markers(branch_info, mir_body); @@ -211,7 +210,8 @@ pub(super) fn extract_branch_pairs( let true_bcb = bcb_from_marker(true_marker)?; let false_bcb = bcb_from_marker(false_marker)?; - Some(BranchPair { span, true_bcb, false_bcb }) + let arm = |bcb| BranchArm { span, pre_guard_bcb: bcb, arm_taken_bcb: bcb }; + Some(vec![arm(true_bcb), arm(false_bcb)]) }) .collect::>() } diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 4a64d21f3d17b..a8c8c37d8d08a 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -8,7 +8,7 @@ mod spans; mod tests; use rustc_middle::mir::coverage::{ - CodeRegion, CoverageKind, DecisionInfo, FunctionCoverageInfo, Mapping, MappingKind, + CodeRegion, CoverageKind, DecisionInfo, FunctionCoverageInfo, Mapping, MappingKind, Op, }; use rustc_middle::mir::{ self, BasicBlock, BasicBlockData, SourceInfo, Statement, StatementKind, Terminator, @@ -21,7 +21,7 @@ use rustc_span::{BytePos, Pos, RelativeBytePos, Span, Symbol}; use crate::coverage::counters::{CounterIncrementSite, CoverageCounters}; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; -use crate::coverage::mappings::ExtractedMappings; +use crate::coverage::mappings::{BranchArm, ExtractedMappings}; use crate::MirPass; /// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected @@ -91,10 +91,10 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: } let bcb_has_counter_mappings = |bcb| bcbs_with_counter_mappings.contains(bcb); - let coverage_counters = + let mut coverage_counters = CoverageCounters::make_bcb_counters(&basic_coverage_blocks, bcb_has_counter_mappings); - let mappings = create_mappings(tcx, &hir_info, &extracted_mappings, &coverage_counters); + let mappings = create_mappings(tcx, &hir_info, &extracted_mappings, &mut coverage_counters); if mappings.is_empty() { // No spans could be converted into valid mappings, so skip this function. debug!("no spans could be converted into valid mappings; skipping"); @@ -136,7 +136,7 @@ fn create_mappings<'tcx>( tcx: TyCtxt<'tcx>, hir_info: &ExtractedHirInfo, extracted_mappings: &ExtractedMappings, - coverage_counters: &CoverageCounters, + coverage_counters: &mut CoverageCounters, ) -> Vec { let source_map = tcx.sess.source_map(); let body_span = hir_info.body_span; @@ -148,24 +148,66 @@ fn create_mappings<'tcx>( &source_file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy(), ); - let term_for_bcb = |bcb| { - coverage_counters - .bcb_counter(bcb) - .expect("all BCBs with spans were given counters") - .as_term() - }; let region_for_span = |span: Span| make_code_region(source_map, file_name, span, body_span); // Fully destructure the mappings struct to make sure we don't miss any kinds. let ExtractedMappings { code_mappings, - branch_pairs, + branch_arm_lists, mcdc_bitmap_bytes: _, mcdc_branches, mcdc_decisions, } = extracted_mappings; let mut mappings = Vec::new(); + // Process branch arms first, because they might need to mutate `coverage_counters` + // to create new expressions. + for arm_list in branch_arm_lists { + let mut arms_rev = arm_list.iter().rev(); + + let mut rest_counter = { + // The last arm's span is ignored, because its BCB is only used as the + // false branch of the second-last arm; it's not a branch of its own. + let Some(&BranchArm { span: _, pre_guard_bcb, arm_taken_bcb }) = arms_rev.next() else { + continue; + }; + debug_assert_eq!(pre_guard_bcb, arm_taken_bcb, "last arm should not have a guard"); + coverage_counters.bcb_counter(pre_guard_bcb).expect("all relevant BCBs have counters") + }; + + // All relevant BCBs should have counters, so we can `.unwrap()` them. + for &BranchArm { span, pre_guard_bcb, arm_taken_bcb } in arms_rev { + // Number of times the pattern matched. + let matched_counter = coverage_counters.bcb_counter(pre_guard_bcb).unwrap(); + // Number of times the pattern matched and the guard succeeded. + let arm_taken_counter = coverage_counters.bcb_counter(arm_taken_bcb).unwrap(); + // Total number of times execution logically reached this pattern. + let reached_counter = + coverage_counters.make_expression(rest_counter, Op::Add, arm_taken_counter); + // Number of times execution reached this pattern, but didn't match it. + let unmatched_counter = + coverage_counters.make_expression(reached_counter, Op::Subtract, matched_counter); + + let kind = MappingKind::Branch { + true_term: matched_counter.as_term(), + false_term: unmatched_counter.as_term(), + }; + + if let Some(code_region) = region_for_span(span) { + mappings.push(Mapping { kind, code_region }); + } + + rest_counter = reached_counter; + } + } + + let term_for_bcb = |bcb| { + coverage_counters + .bcb_counter(bcb) + .expect("all BCBs with spans were given counters") + .as_term() + }; + mappings.extend(code_mappings.iter().filter_map( // Ordinary code mappings are the simplest kind. |&mappings::CodeMapping { span, bcb }| { @@ -175,16 +217,6 @@ fn create_mappings<'tcx>( }, )); - mappings.extend(branch_pairs.iter().filter_map( - |&mappings::BranchPair { span, true_bcb, false_bcb }| { - let true_term = term_for_bcb(true_bcb); - let false_term = term_for_bcb(false_bcb); - let kind = MappingKind::Branch { true_term, false_term }; - let code_region = region_for_span(span)?; - Some(Mapping { kind, code_region }) - }, - )); - mappings.extend(mcdc_branches.iter().filter_map( |&mappings::MCDCBranch { span, true_bcb, false_bcb, condition_info, decision_depth: _ }| { let code_region = region_for_span(span)?; From a513c92fdcd720efe8132a6db48ec8e4b07557b2 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 21 Apr 2024 14:28:08 +1000 Subject: [PATCH 3/4] coverage: Represent branches as a list of arms during MIR building, too --- compiler/rustc_middle/src/mir/coverage.rs | 12 +++-- compiler/rustc_middle/src/mir/pretty.rs | 19 ++++---- .../rustc_mir_build/src/build/coverageinfo.rs | 23 ++++----- .../src/coverage/mappings.rs | 47 ++++++++++++------- ...rage_cleanup.main.CleanupPostBorrowck.diff | 7 ++- ...erage_cleanup.main.InstrumentCoverage.diff | 7 ++- 6 files changed, 72 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index da25fbb0a82b3..1597d8a34a978 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -282,17 +282,21 @@ pub struct BranchInfo { /// injected into the MIR body. This makes it possible to allocate per-ID /// data structures without having to scan the entire body first. pub num_block_markers: usize, - pub branch_spans: Vec, + pub branch_arm_lists: Vec>, pub mcdc_branch_spans: Vec, pub mcdc_decision_spans: Vec, } #[derive(Clone, Debug)] #[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] -pub struct BranchSpan { +pub struct BranchArm { pub span: Span, - pub true_marker: BlockMarkerId, - pub false_marker: BlockMarkerId, + /// Marks the block that is jumped to after this arm's pattern matches, + /// but before its guard is checked. + pub pre_guard_marker: BlockMarkerId, + /// Marks the block that is jumped to after this arm's guard succeeds. + /// If this is equal to `pre_guard_marker`, the arm has no guard. + pub arm_taken_marker: BlockMarkerId, } #[derive(Copy, Clone, Debug)] diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 4657f4dcf8132..0b1f09926ee3e 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -487,14 +487,18 @@ fn write_coverage_branch_info( branch_info: &coverage::BranchInfo, w: &mut dyn io::Write, ) -> io::Result<()> { - let coverage::BranchInfo { branch_spans, mcdc_branch_spans, mcdc_decision_spans, .. } = + let coverage::BranchInfo { branch_arm_lists, mcdc_branch_spans, mcdc_decision_spans, .. } = branch_info; - for coverage::BranchSpan { span, true_marker, false_marker } in branch_spans { - writeln!( - w, - "{INDENT}coverage branch {{ true: {true_marker:?}, false: {false_marker:?} }} => {span:?}", - )?; + for arms in branch_arm_lists { + writeln!(w, "{INDENT}coverage branches {{")?; + for coverage::BranchArm { span, pre_guard_marker, arm_taken_marker } in arms { + writeln!(w, "{INDENT}{INDENT}{pre_guard_marker:?}, {arm_taken_marker:?} => {span:?}")?; + } + writeln!(w, "{INDENT}}}")?; + } + if !branch_arm_lists.is_empty() { + writeln!(w)?; } for coverage::MCDCBranchSpan { @@ -521,8 +525,7 @@ fn write_coverage_branch_info( )?; } - if !branch_spans.is_empty() || !mcdc_branch_spans.is_empty() || !mcdc_decision_spans.is_empty() - { + if !mcdc_branch_spans.is_empty() || !mcdc_decision_spans.is_empty() { writeln!(w)?; } diff --git a/compiler/rustc_mir_build/src/build/coverageinfo.rs b/compiler/rustc_mir_build/src/build/coverageinfo.rs index 876faca5172ac..17df2595907d6 100644 --- a/compiler/rustc_mir_build/src/build/coverageinfo.rs +++ b/compiler/rustc_mir_build/src/build/coverageinfo.rs @@ -2,7 +2,7 @@ use std::assert_matches::assert_matches; use std::collections::hash_map::Entry; use rustc_data_structures::fx::FxHashMap; -use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind}; +use rustc_middle::mir::coverage::{BlockMarkerId, BranchArm, CoverageKind}; use rustc_middle::mir::{self, BasicBlock, SourceInfo, UnOp}; use rustc_middle::thir::{ExprId, ExprKind, Pat, Thir}; use rustc_middle::ty::TyCtxt; @@ -18,7 +18,7 @@ pub(crate) struct BranchInfoBuilder { nots: FxHashMap, markers: BlockMarkerGen, - branch_spans: Vec, + branch_arm_lists: Vec>, mcdc_info: Option, } @@ -70,7 +70,7 @@ impl BranchInfoBuilder { Some(Self { nots: FxHashMap::default(), markers: BlockMarkerGen::default(), - branch_spans: vec![], + branch_arm_lists: vec![], mcdc_info: tcx.sess.instrument_coverage_mcdc().then(MCDCInfoBuilder::new), }) } else { @@ -141,11 +141,12 @@ impl BranchInfoBuilder { let true_marker = self.markers.inject_block_marker(cfg, source_info, true_block); let false_marker = self.markers.inject_block_marker(cfg, source_info, false_block); - self.branch_spans.push(BranchSpan { + let arm = |marker| BranchArm { span: source_info.span, - true_marker, - false_marker, - }); + pre_guard_marker: marker, + arm_taken_marker: marker, + }; + self.branch_arm_lists.push(vec![arm(true_marker), arm(false_marker)]); } } @@ -153,12 +154,12 @@ impl BranchInfoBuilder { let Self { nots: _, markers: BlockMarkerGen { num_block_markers }, - branch_spans, + branch_arm_lists, mcdc_info, } = self; if num_block_markers == 0 { - assert!(branch_spans.is_empty()); + assert!(branch_arm_lists.is_empty()); return None; } @@ -167,7 +168,7 @@ impl BranchInfoBuilder { Some(Box::new(mir::coverage::BranchInfo { num_block_markers, - branch_spans, + branch_arm_lists, mcdc_branch_spans, mcdc_decision_spans, })) @@ -240,7 +241,7 @@ impl<'tcx> Builder<'_, 'tcx> { } /// If branch coverage is enabled, inject marker statements into `then_block` - /// and `else_block`, and record their IDs in the table of branch spans. + /// and `else_block`, and record their IDs in the branch table. pub(crate) fn visit_coverage_branch_condition( &mut self, mut expr_id: ExprId, diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs index 81fe971cf9215..3e3ac6a180e56 100644 --- a/compiler/rustc_mir_transform/src/coverage/mappings.rs +++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs @@ -3,7 +3,7 @@ use std::collections::BTreeSet; use rustc_data_structures::graph::DirectedGraph; use rustc_index::bit_set::BitSet; use rustc_index::IndexVec; -use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, ConditionInfo, CoverageKind}; +use rustc_middle::mir::coverage::{BlockMarkerId, ConditionInfo, CoverageKind}; use rustc_middle::mir::{self, BasicBlock, StatementKind}; use rustc_middle::ty::TyCtxt; use rustc_span::Span; @@ -193,25 +193,40 @@ pub(super) fn extract_branch_arm_lists( let block_markers = resolve_block_markers(branch_info, mir_body); branch_info - .branch_spans + .branch_arm_lists .iter() - .filter_map(|&BranchSpan { span: raw_span, true_marker, false_marker }| { - // For now, ignore any branch span that was introduced by - // expansion. This makes things like assert macros less noisy. - if !raw_span.ctxt().outer_expn_data().is_root() { - return None; + .filter_map(|arms| { + let mut bcb_arms = Vec::with_capacity(arms.len()); + + // If any arm can't be resolved, return None to skip the entire list + // of arms that contains it. + for &mir::coverage::BranchArm { span: raw_span, pre_guard_marker, arm_taken_marker } in + arms + { + // For now, ignore any branch span that was introduced by + // expansion. This makes things like assert macros less noisy. + if !raw_span.ctxt().outer_expn_data().is_root() { + return None; + } + + let (span, _) = + unexpand_into_body_span_with_visible_macro(raw_span, hir_info.body_span)?; + + let pre_guard_bcb = + basic_coverage_blocks.bcb_from_bb(block_markers[pre_guard_marker]?)?; + let arm_taken_bcb = + basic_coverage_blocks.bcb_from_bb(block_markers[arm_taken_marker]?)?; + + bcb_arms.push(BranchArm { span, pre_guard_bcb, arm_taken_bcb }); } - let (span, _) = - unexpand_into_body_span_with_visible_macro(raw_span, hir_info.body_span)?; - - let bcb_from_marker = - |marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?); + assert_eq!(arms.len(), bcb_arms.len()); - let true_bcb = bcb_from_marker(true_marker)?; - let false_bcb = bcb_from_marker(false_marker)?; + if bcb_arms.len() < 2 { + debug_assert!(false, "MIR building shouldn't create branches with <2 arms"); + return None; + } - let arm = |bcb| BranchArm { span, pre_guard_bcb: bcb, arm_taken_bcb: bcb }; - Some(vec![arm(true_bcb), arm(false_bcb)]) + Some(bcb_arms) }) .collect::>() } diff --git a/tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff index efb1559baf5eb..974ef70d5c9cd 100644 --- a/tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff +++ b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff @@ -5,14 +5,17 @@ let mut _0: (); let mut _1: bool; - coverage branch { true: BlockMarkerId(0), false: BlockMarkerId(1) } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0) + coverage branches { + BlockMarkerId(0), BlockMarkerId(0) => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0) + BlockMarkerId(1), BlockMarkerId(1) => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0) + } coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Subtract, rhs: Counter(1) }; + coverage Branch { true_term: Expression(0), false_term: Counter(1) } => $DIR/instrument_coverage_cleanup.rs:14:8 - 14:36; coverage Code(Counter(0)) => $DIR/instrument_coverage_cleanup.rs:13:1 - 14:36; coverage Code(Expression(0)) => $DIR/instrument_coverage_cleanup.rs:14:37 - 14:39; coverage Code(Counter(1)) => $DIR/instrument_coverage_cleanup.rs:14:39 - 14:40; coverage Code(Counter(0)) => $DIR/instrument_coverage_cleanup.rs:15:1 - 15:2; - coverage Branch { true_term: Expression(0), false_term: Counter(1) } => $DIR/instrument_coverage_cleanup.rs:14:8 - 14:36; bb0: { Coverage::CounterIncrement(0); diff --git a/tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff index a0fe9a5c05cd1..f6d7fb296ec03 100644 --- a/tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff +++ b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff @@ -5,14 +5,17 @@ let mut _0: (); let mut _1: bool; - coverage branch { true: BlockMarkerId(0), false: BlockMarkerId(1) } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0) + coverage branches { + BlockMarkerId(0), BlockMarkerId(0) => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0) + BlockMarkerId(1), BlockMarkerId(1) => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0) + } + coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Subtract, rhs: Counter(1) }; ++ coverage Branch { true_term: Expression(0), false_term: Counter(1) } => $DIR/instrument_coverage_cleanup.rs:14:8 - 14:36; + coverage Code(Counter(0)) => $DIR/instrument_coverage_cleanup.rs:13:1 - 14:36; + coverage Code(Expression(0)) => $DIR/instrument_coverage_cleanup.rs:14:37 - 14:39; + coverage Code(Counter(1)) => $DIR/instrument_coverage_cleanup.rs:14:39 - 14:40; + coverage Code(Counter(0)) => $DIR/instrument_coverage_cleanup.rs:15:1 - 15:2; -+ coverage Branch { true_term: Expression(0), false_term: Counter(1) } => $DIR/instrument_coverage_cleanup.rs:14:8 - 14:36; + bb0: { + Coverage::CounterIncrement(0); From 59595611af7b2987bff41dbfbe434eaaf72fa3e8 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 18 Apr 2024 20:39:02 +1000 Subject: [PATCH 4/4] coverage: Treat each match arm as a "branch" for branch coverage --- .../rustc_mir_build/src/build/coverageinfo.rs | 32 +++++ .../rustc_mir_build/src/build/matches/mod.rs | 21 ++- tests/coverage/branch/guard-simple.cov-map | 33 +++-- tests/coverage/branch/guard-simple.coverage | 2 + tests/coverage/branch/guard.cov-map | 46 ++++-- tests/coverage/branch/guard.coverage | 5 + tests/coverage/branch/match-arms.cov-map | 132 ++++++++++++++---- tests/coverage/branch/match-arms.coverage | 15 +- tests/coverage/branch/match-arms.rs | 2 +- ...ch_match_arms.main.InstrumentCoverage.diff | 21 +++ 10 files changed, 249 insertions(+), 60 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/coverageinfo.rs b/compiler/rustc_mir_build/src/build/coverageinfo.rs index 17df2595907d6..6634514f9cfca 100644 --- a/compiler/rustc_mir_build/src/build/coverageinfo.rs +++ b/compiler/rustc_mir_build/src/build/coverageinfo.rs @@ -33,6 +33,12 @@ struct NotInfo { is_flipped: bool, } +pub(crate) struct MatchArm { + pub(crate) source_info: SourceInfo, + pub(crate) pre_binding_block: Option, + pub(crate) arm_block: BasicBlock, +} + #[derive(Default)] struct BlockMarkerGen { num_block_markers: usize, @@ -150,6 +156,32 @@ impl BranchInfoBuilder { } } + pub(crate) fn add_match_arms(&mut self, cfg: &mut CFG<'_>, arms: &[MatchArm]) { + // Match expressions with 0-1 arms don't have any branches for their arms. + if arms.len() < 2 { + return; + } + + // FIXME(#124118) The current implementation of branch coverage for + // match arms can't handle or-patterns. + if arms.iter().any(|arm| arm.pre_binding_block.is_none()) { + return; + } + + let branch_arms = arms + .iter() + .map(|&MatchArm { source_info, pre_binding_block, arm_block }| { + let pre_guard_marker = + self.markers.inject_block_marker(cfg, source_info, pre_binding_block.unwrap()); + let arm_taken_marker = + self.markers.inject_block_marker(cfg, source_info, arm_block); + BranchArm { span: source_info.span, pre_guard_marker, arm_taken_marker } + }) + .collect::>(); + + self.branch_arm_lists.push(branch_arms); + } + pub(crate) fn into_done(self) -> Option> { let Self { nots: _, diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 932406fd1aaf2..baf810cb718f0 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -8,7 +8,7 @@ use crate::build::expr::as_place::PlaceBuilder; use crate::build::scope::DropKind; use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard}; -use crate::build::{BlockAnd, BlockAndExtension, Builder}; +use crate::build::{coverageinfo, BlockAnd, BlockAndExtension, Builder}; use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode}; use rustc_data_structures::{fx::FxIndexMap, stack::ensure_sufficient_stack}; use rustc_hir::{BindingMode, ByRef}; @@ -473,6 +473,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { outer_source_info: SourceInfo, fake_borrow_temps: Vec<(Place<'tcx>, Local, FakeBorrowKind)>, ) -> BlockAnd<()> { + let mut coverage_match_arms = self.coverage_branch_info.is_some().then_some(vec![]); + let arm_end_blocks: Vec<_> = arm_candidates .into_iter() .map(|(arm, candidate)| { @@ -507,6 +509,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { opt_scrutinee_place, ); + let pre_binding_block = candidate.pre_binding_block; + let arm_block = this.bind_pattern( outer_source_info, candidate, @@ -516,6 +520,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { false, ); + if let Some(coverage_match_arms) = coverage_match_arms.as_mut() { + coverage_match_arms.push(coverageinfo::MatchArm { + source_info: this.source_info(arm.pattern.span), + pre_binding_block, + arm_block, + }) + } + this.fixed_temps_scope = old_dedup_scope; if let Some(source_scope) = scope { @@ -527,6 +539,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }) .collect(); + if let Some(coverage_match_arms) = coverage_match_arms { + self.coverage_branch_info + .as_mut() + .expect("checked when creating `coverage_match_arms`") + .add_match_arms(&mut self.cfg, &coverage_match_arms); + } + // all the arm blocks will rejoin here let end_block = self.cfg.start_new_block(); diff --git a/tests/coverage/branch/guard-simple.cov-map b/tests/coverage/branch/guard-simple.cov-map index 8eb9c54ff4bca..50654ae661afb 100644 --- a/tests/coverage/branch/guard-simple.cov-map +++ b/tests/coverage/branch/guard-simple.cov-map @@ -1,24 +1,35 @@ Function name: guard_simple::never_taken -Raw bytes (56): 0x[01, 01, 04, 01, 05, 11, 09, 0f, 0d, 05, 09, 08, 01, 08, 01, 02, 1e, 20, 05, 02, 02, 0e, 00, 1e, 05, 00, 22, 00, 24, 11, 01, 0e, 00, 1e, 20, 09, 06, 00, 0e, 00, 1e, 09, 00, 22, 00, 24, 0d, 01, 0e, 00, 10, 0b, 02, 01, 00, 02] +Raw bytes (80): 0x[01, 01, 09, 07, 01, 17, 05, 0d, 09, 01, 05, 17, 11, 0d, 09, 11, 09, 23, 0d, 05, 09, 0a, 01, 08, 01, 02, 1e, 20, 01, 02, 02, 09, 00, 0a, 20, 05, 0e, 00, 0e, 00, 1e, 05, 00, 22, 00, 24, 20, 11, 12, 01, 09, 00, 0a, 11, 00, 0e, 00, 1e, 20, 09, 1a, 00, 0e, 00, 1e, 09, 00, 22, 00, 24, 0d, 01, 0e, 00, 10, 1f, 02, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 4 -- expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(4), rhs = Counter(2) -- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(3) -- expression 3 operands: lhs = Counter(1), rhs = Counter(2) -Number of file 0 mappings: 8 +Number of expressions: 9 +- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(0) +- expression 1 operands: lhs = Expression(5, Add), rhs = Counter(1) +- expression 2 operands: lhs = Counter(3), rhs = Counter(2) +- expression 3 operands: lhs = Counter(0), rhs = Counter(1) +- expression 4 operands: lhs = Expression(5, Add), rhs = Counter(4) +- expression 5 operands: lhs = Counter(3), rhs = Counter(2) +- expression 6 operands: lhs = Counter(4), rhs = Counter(2) +- expression 7 operands: lhs = Expression(8, Add), rhs = Counter(3) +- expression 8 operands: lhs = Counter(1), rhs = Counter(2) +Number of file 0 mappings: 10 - Code(Counter(0)) at (prev + 8, 1) to (start + 2, 30) -- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 2, 14) to (start + 0, 30) +- Branch { true: Counter(0), false: Expression(0, Sub) } at (prev + 2, 9) to (start + 0, 10) + true = c0 + false = (((c3 + c2) + c1) - c0) +- Branch { true: Counter(1), false: Expression(3, Sub) } at (prev + 0, 14) to (start + 0, 30) true = c1 false = (c0 - c1) - Code(Counter(1)) at (prev + 0, 34) to (start + 0, 36) -- Code(Counter(4)) at (prev + 1, 14) to (start + 0, 30) -- Branch { true: Counter(2), false: Expression(1, Sub) } at (prev + 0, 14) to (start + 0, 30) +- Branch { true: Counter(4), false: Expression(4, Sub) } at (prev + 1, 9) to (start + 0, 10) + true = c4 + false = ((c3 + c2) - c4) +- Code(Counter(4)) at (prev + 0, 14) to (start + 0, 30) +- Branch { true: Counter(2), false: Expression(6, Sub) } at (prev + 0, 14) to (start + 0, 30) true = c2 false = (c4 - c2) - Code(Counter(2)) at (prev + 0, 34) to (start + 0, 36) - Code(Counter(3)) at (prev + 1, 14) to (start + 0, 16) -- Code(Expression(2, Add)) at (prev + 2, 1) to (start + 0, 2) +- Code(Expression(7, Add)) at (prev + 2, 1) to (start + 0, 2) = ((c1 + c2) + c3) diff --git a/tests/coverage/branch/guard-simple.coverage b/tests/coverage/branch/guard-simple.coverage index ac90c48c44342..06ad332a927da 100644 --- a/tests/coverage/branch/guard-simple.coverage +++ b/tests/coverage/branch/guard-simple.coverage @@ -10,11 +10,13 @@ LL| 1| _ if black_box(false) => {} ^0 ------------------ + | Branch (LL:9): [True: 1, False: 0] | Branch (LL:14): [True: 0, False: 1] ------------------ LL| 1| _ if black_box(false) => {} ^0 ------------------ + | Branch (LL:9): [True: 1, False: 0] | Branch (LL:14): [True: 0, False: 1] ------------------ LL| 1| _ => {} diff --git a/tests/coverage/branch/guard.cov-map b/tests/coverage/branch/guard.cov-map index d67c3d349a14e..18dd0c302cbfa 100644 --- a/tests/coverage/branch/guard.cov-map +++ b/tests/coverage/branch/guard.cov-map @@ -1,32 +1,48 @@ Function name: guard::branch_match_guard -Raw bytes (85): 0x[01, 01, 06, 19, 0d, 05, 09, 0f, 15, 13, 11, 17, 0d, 05, 09, 0d, 01, 0c, 01, 01, 10, 1d, 03, 0b, 00, 0c, 15, 01, 14, 02, 0a, 0d, 03, 0e, 00, 0f, 19, 00, 14, 00, 19, 20, 0d, 02, 00, 14, 00, 1e, 0d, 00, 1d, 02, 0a, 11, 03, 0e, 00, 0f, 1d, 00, 14, 00, 19, 20, 11, 09, 00, 14, 00, 1e, 11, 00, 1d, 02, 0a, 17, 03, 0e, 02, 0a, 0b, 04, 01, 00, 02] +Raw bytes (120): 0x[01, 01, 0d, 1b, 0d, 33, 11, 05, 09, 03, 19, 19, 0d, 1b, 1d, 33, 11, 05, 09, 05, 09, 2b, 15, 2f, 11, 33, 0d, 05, 09, 10, 01, 0c, 01, 01, 10, 1d, 03, 0b, 00, 0c, 20, 15, 03, 01, 09, 00, 10, 15, 00, 14, 02, 0a, 20, 19, 0e, 03, 09, 00, 10, 0d, 00, 0e, 00, 0f, 19, 00, 14, 00, 19, 20, 0d, 12, 00, 14, 00, 1e, 0d, 00, 1d, 02, 0a, 20, 1d, 16, 03, 09, 00, 10, 11, 00, 0e, 00, 0f, 1d, 00, 14, 00, 19, 20, 11, 09, 00, 14, 00, 1e, 11, 00, 1d, 02, 0a, 33, 03, 0e, 02, 0a, 27, 04, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 6 -- expression 0 operands: lhs = Counter(6), rhs = Counter(3) -- expression 1 operands: lhs = Counter(1), rhs = Counter(2) -- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(5) -- expression 3 operands: lhs = Expression(4, Add), rhs = Counter(4) -- expression 4 operands: lhs = Expression(5, Add), rhs = Counter(3) -- expression 5 operands: lhs = Counter(1), rhs = Counter(2) -Number of file 0 mappings: 13 +Number of expressions: 13 +- expression 0 operands: lhs = Expression(6, Add), rhs = Counter(3) +- expression 1 operands: lhs = Expression(12, Add), rhs = Counter(4) +- expression 2 operands: lhs = Counter(1), rhs = Counter(2) +- expression 3 operands: lhs = Expression(0, Add), rhs = Counter(6) +- expression 4 operands: lhs = Counter(6), rhs = Counter(3) +- expression 5 operands: lhs = Expression(6, Add), rhs = Counter(7) +- expression 6 operands: lhs = Expression(12, Add), rhs = Counter(4) +- expression 7 operands: lhs = Counter(1), rhs = Counter(2) +- expression 8 operands: lhs = Counter(1), rhs = Counter(2) +- expression 9 operands: lhs = Expression(10, Add), rhs = Counter(5) +- expression 10 operands: lhs = Expression(11, Add), rhs = Counter(4) +- expression 11 operands: lhs = Expression(12, Add), rhs = Counter(3) +- expression 12 operands: lhs = Counter(1), rhs = Counter(2) +Number of file 0 mappings: 16 - Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16) - Code(Counter(7)) at (prev + 3, 11) to (start + 0, 12) -- Code(Counter(5)) at (prev + 1, 20) to (start + 2, 10) -- Code(Counter(3)) at (prev + 3, 14) to (start + 0, 15) +- Branch { true: Counter(5), false: Expression(0, Add) } at (prev + 1, 9) to (start + 0, 16) + true = c5 + false = (((c1 + c2) + c4) + c3) +- Code(Counter(5)) at (prev + 0, 20) to (start + 2, 10) +- Branch { true: Counter(6), false: Expression(3, Sub) } at (prev + 3, 9) to (start + 0, 16) + true = c6 + false = ((((c1 + c2) + c4) + c3) - c6) +- Code(Counter(3)) at (prev + 0, 14) to (start + 0, 15) - Code(Counter(6)) at (prev + 0, 20) to (start + 0, 25) -- Branch { true: Counter(3), false: Expression(0, Sub) } at (prev + 0, 20) to (start + 0, 30) +- Branch { true: Counter(3), false: Expression(4, Sub) } at (prev + 0, 20) to (start + 0, 30) true = c3 false = (c6 - c3) - Code(Counter(3)) at (prev + 0, 29) to (start + 2, 10) -- Code(Counter(4)) at (prev + 3, 14) to (start + 0, 15) +- Branch { true: Counter(7), false: Expression(5, Sub) } at (prev + 3, 9) to (start + 0, 16) + true = c7 + false = (((c1 + c2) + c4) - c7) +- Code(Counter(4)) at (prev + 0, 14) to (start + 0, 15) - Code(Counter(7)) at (prev + 0, 20) to (start + 0, 25) - Branch { true: Counter(4), false: Counter(2) } at (prev + 0, 20) to (start + 0, 30) true = c4 false = c2 - Code(Counter(4)) at (prev + 0, 29) to (start + 2, 10) -- Code(Expression(5, Add)) at (prev + 3, 14) to (start + 2, 10) +- Code(Expression(12, Add)) at (prev + 3, 14) to (start + 2, 10) = (c1 + c2) -- Code(Expression(2, Add)) at (prev + 4, 1) to (start + 0, 2) +- Code(Expression(9, Add)) at (prev + 4, 1) to (start + 0, 2) = ((((c1 + c2) + c3) + c4) + c5) diff --git a/tests/coverage/branch/guard.coverage b/tests/coverage/branch/guard.coverage index f89b965b5d0f7..6b1b650d4703c 100644 --- a/tests/coverage/branch/guard.coverage +++ b/tests/coverage/branch/guard.coverage @@ -14,17 +14,22 @@ LL| | LL| 1| match x { LL| 1| Some(0) => { + ------------------ + | Branch (LL:9): [True: 1, False: 3] + ------------------ LL| 1| println!("zero"); LL| 1| } LL| 3| Some(x) if x % 2 == 0 => { ^2 ------------------ + | Branch (LL:9): [True: 3, False: 0] | Branch (LL:20): [True: 2, False: 1] ------------------ LL| 2| println!("is nonzero and even"); LL| 2| } LL| 1| Some(x) if x % 3 == 0 => { ------------------ + | Branch (LL:9): [True: 1, False: 0] | Branch (LL:20): [True: 1, False: 0] ------------------ LL| 1| println!("is nonzero and odd, but divisible by 3"); diff --git a/tests/coverage/branch/match-arms.cov-map b/tests/coverage/branch/match-arms.cov-map index 1f17f11baaa07..04c39db4c5c31 100644 --- a/tests/coverage/branch/match-arms.cov-map +++ b/tests/coverage/branch/match-arms.cov-map @@ -1,60 +1,130 @@ Function name: match_arms::guards -Raw bytes (88): 0x[01, 01, 08, 07, 15, 0b, 11, 0f, 0d, 00, 09, 17, 25, 1b, 21, 1f, 1d, 03, 19, 0c, 01, 30, 01, 01, 10, 29, 03, 0b, 00, 10, 19, 01, 11, 00, 29, 20, 19, 09, 00, 17, 00, 1b, 1d, 01, 11, 00, 29, 20, 1d, 0d, 00, 17, 00, 1b, 21, 01, 11, 00, 29, 20, 21, 11, 00, 17, 00, 1b, 25, 01, 11, 00, 29, 20, 25, 15, 00, 17, 00, 1b, 03, 01, 0e, 00, 18, 13, 03, 05, 01, 02] +Raw bytes (212): 0x[01, 01, 2a, 07, 35, 2b, 19, 4b, 1d, 67, 21, 9b, 01, 25, 9f, 01, 15, a3, 01, 11, a7, 01, 0d, 00, 09, 2b, 31, 4b, 1d, 67, 21, 9b, 01, 25, 9f, 01, 15, a3, 01, 11, a7, 01, 0d, 00, 09, 4b, 2d, 67, 21, 9b, 01, 25, 9f, 01, 15, a3, 01, 11, a7, 01, 0d, 00, 09, 67, 29, 9b, 01, 25, 9f, 01, 15, a3, 01, 11, a7, 01, 0d, 00, 09, 9f, 01, 15, a3, 01, 11, a7, 01, 0d, 00, 09, 8f, 01, 25, 93, 01, 21, 97, 01, 1d, 9b, 01, 19, 9f, 01, 15, a3, 01, 11, a7, 01, 0d, 00, 09, 10, 01, 30, 01, 01, 10, 29, 03, 0b, 00, 10, 20, 35, 02, 01, 09, 00, 13, 19, 00, 11, 00, 29, 20, 19, 09, 00, 17, 00, 1b, 20, 31, 26, 01, 09, 00, 13, 1d, 00, 11, 00, 29, 20, 1d, 0d, 00, 17, 00, 1b, 20, 2d, 46, 01, 09, 00, 13, 21, 00, 11, 00, 29, 20, 21, 11, 00, 17, 00, 1b, 20, 29, 62, 01, 09, 00, 13, 25, 00, 11, 00, 29, 20, 25, 15, 00, 17, 00, 1b, 9b, 01, 01, 0e, 00, 18, 8b, 01, 03, 05, 01, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 8 -- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(5) -- expression 1 operands: lhs = Expression(2, Add), rhs = Counter(4) -- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(3) -- expression 3 operands: lhs = Zero, rhs = Counter(2) -- expression 4 operands: lhs = Expression(5, Add), rhs = Counter(9) -- expression 5 operands: lhs = Expression(6, Add), rhs = Counter(8) -- expression 6 operands: lhs = Expression(7, Add), rhs = Counter(7) -- expression 7 operands: lhs = Expression(0, Add), rhs = Counter(6) -Number of file 0 mappings: 12 +Number of expressions: 42 +- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(13) +- expression 1 operands: lhs = Expression(10, Add), rhs = Counter(6) +- expression 2 operands: lhs = Expression(18, Add), rhs = Counter(7) +- expression 3 operands: lhs = Expression(25, Add), rhs = Counter(8) +- expression 4 operands: lhs = Expression(38, Add), rhs = Counter(9) +- expression 5 operands: lhs = Expression(39, Add), rhs = Counter(5) +- expression 6 operands: lhs = Expression(40, Add), rhs = Counter(4) +- expression 7 operands: lhs = Expression(41, Add), rhs = Counter(3) +- expression 8 operands: lhs = Zero, rhs = Counter(2) +- expression 9 operands: lhs = Expression(10, Add), rhs = Counter(12) +- expression 10 operands: lhs = Expression(18, Add), rhs = Counter(7) +- expression 11 operands: lhs = Expression(25, Add), rhs = Counter(8) +- expression 12 operands: lhs = Expression(38, Add), rhs = Counter(9) +- expression 13 operands: lhs = Expression(39, Add), rhs = Counter(5) +- expression 14 operands: lhs = Expression(40, Add), rhs = Counter(4) +- expression 15 operands: lhs = Expression(41, Add), rhs = Counter(3) +- expression 16 operands: lhs = Zero, rhs = Counter(2) +- expression 17 operands: lhs = Expression(18, Add), rhs = Counter(11) +- expression 18 operands: lhs = Expression(25, Add), rhs = Counter(8) +- expression 19 operands: lhs = Expression(38, Add), rhs = Counter(9) +- expression 20 operands: lhs = Expression(39, Add), rhs = Counter(5) +- expression 21 operands: lhs = Expression(40, Add), rhs = Counter(4) +- expression 22 operands: lhs = Expression(41, Add), rhs = Counter(3) +- expression 23 operands: lhs = Zero, rhs = Counter(2) +- expression 24 operands: lhs = Expression(25, Add), rhs = Counter(10) +- expression 25 operands: lhs = Expression(38, Add), rhs = Counter(9) +- expression 26 operands: lhs = Expression(39, Add), rhs = Counter(5) +- expression 27 operands: lhs = Expression(40, Add), rhs = Counter(4) +- expression 28 operands: lhs = Expression(41, Add), rhs = Counter(3) +- expression 29 operands: lhs = Zero, rhs = Counter(2) +- expression 30 operands: lhs = Expression(39, Add), rhs = Counter(5) +- expression 31 operands: lhs = Expression(40, Add), rhs = Counter(4) +- expression 32 operands: lhs = Expression(41, Add), rhs = Counter(3) +- expression 33 operands: lhs = Zero, rhs = Counter(2) +- expression 34 operands: lhs = Expression(35, Add), rhs = Counter(9) +- expression 35 operands: lhs = Expression(36, Add), rhs = Counter(8) +- expression 36 operands: lhs = Expression(37, Add), rhs = Counter(7) +- expression 37 operands: lhs = Expression(38, Add), rhs = Counter(6) +- expression 38 operands: lhs = Expression(39, Add), rhs = Counter(5) +- expression 39 operands: lhs = Expression(40, Add), rhs = Counter(4) +- expression 40 operands: lhs = Expression(41, Add), rhs = Counter(3) +- expression 41 operands: lhs = Zero, rhs = Counter(2) +Number of file 0 mappings: 16 - Code(Counter(0)) at (prev + 48, 1) to (start + 1, 16) - Code(Counter(10)) at (prev + 3, 11) to (start + 0, 16) -- Code(Counter(6)) at (prev + 1, 17) to (start + 0, 41) +- Branch { true: Counter(13), false: Expression(0, Sub) } at (prev + 1, 9) to (start + 0, 19) + true = c13 + false = (((((((((Zero + c2) + c3) + c4) + c5) + c9) + c8) + c7) + c6) - c13) +- Code(Counter(6)) at (prev + 0, 17) to (start + 0, 41) - Branch { true: Counter(6), false: Counter(2) } at (prev + 0, 23) to (start + 0, 27) true = c6 false = c2 -- Code(Counter(7)) at (prev + 1, 17) to (start + 0, 41) +- Branch { true: Counter(12), false: Expression(9, Sub) } at (prev + 1, 9) to (start + 0, 19) + true = c12 + false = ((((((((Zero + c2) + c3) + c4) + c5) + c9) + c8) + c7) - c12) +- Code(Counter(7)) at (prev + 0, 17) to (start + 0, 41) - Branch { true: Counter(7), false: Counter(3) } at (prev + 0, 23) to (start + 0, 27) true = c7 false = c3 -- Code(Counter(8)) at (prev + 1, 17) to (start + 0, 41) +- Branch { true: Counter(11), false: Expression(17, Sub) } at (prev + 1, 9) to (start + 0, 19) + true = c11 + false = (((((((Zero + c2) + c3) + c4) + c5) + c9) + c8) - c11) +- Code(Counter(8)) at (prev + 0, 17) to (start + 0, 41) - Branch { true: Counter(8), false: Counter(4) } at (prev + 0, 23) to (start + 0, 27) true = c8 false = c4 -- Code(Counter(9)) at (prev + 1, 17) to (start + 0, 41) +- Branch { true: Counter(10), false: Expression(24, Sub) } at (prev + 1, 9) to (start + 0, 19) + true = c10 + false = ((((((Zero + c2) + c3) + c4) + c5) + c9) - c10) +- Code(Counter(9)) at (prev + 0, 17) to (start + 0, 41) - Branch { true: Counter(9), false: Counter(5) } at (prev + 0, 23) to (start + 0, 27) true = c9 false = c5 -- Code(Expression(0, Add)) at (prev + 1, 14) to (start + 0, 24) +- Code(Expression(38, Add)) at (prev + 1, 14) to (start + 0, 24) = ((((Zero + c2) + c3) + c4) + c5) -- Code(Expression(4, Add)) at (prev + 3, 5) to (start + 1, 2) +- Code(Expression(34, Add)) at (prev + 3, 5) to (start + 1, 2) = ((((((((Zero + c2) + c3) + c4) + c5) + c6) + c7) + c8) + c9) Function name: match_arms::match_arms -Raw bytes (51): 0x[01, 01, 06, 05, 07, 0b, 11, 09, 0d, 13, 02, 17, 09, 11, 0d, 07, 01, 18, 01, 01, 10, 05, 03, 0b, 00, 10, 11, 01, 11, 00, 21, 0d, 01, 11, 00, 21, 09, 01, 11, 00, 21, 02, 01, 11, 00, 21, 0f, 03, 05, 01, 02] +Raw bytes (102): 0x[01, 01, 15, 17, 0d, 4a, 09, 05, 4f, 53, 11, 09, 0d, 4a, 09, 05, 4f, 53, 11, 09, 0d, 05, 4f, 53, 11, 09, 0d, 05, 4f, 53, 11, 09, 0d, 43, 4a, 47, 09, 11, 0d, 05, 4f, 53, 11, 09, 0d, 0a, 01, 18, 01, 01, 10, 05, 03, 0b, 00, 10, 20, 11, 03, 01, 09, 00, 13, 11, 00, 11, 00, 21, 20, 0d, 17, 01, 09, 00, 13, 0d, 00, 11, 00, 21, 20, 09, 4a, 01, 09, 00, 13, 09, 00, 11, 00, 21, 4a, 01, 11, 00, 21, 3f, 03, 05, 01, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 6 -- expression 0 operands: lhs = Counter(1), rhs = Expression(1, Add) -- expression 1 operands: lhs = Expression(2, Add), rhs = Counter(4) -- expression 2 operands: lhs = Counter(2), rhs = Counter(3) -- expression 3 operands: lhs = Expression(4, Add), rhs = Expression(0, Sub) -- expression 4 operands: lhs = Expression(5, Add), rhs = Counter(2) -- expression 5 operands: lhs = Counter(4), rhs = Counter(3) -Number of file 0 mappings: 7 +Number of expressions: 21 +- expression 0 operands: lhs = Expression(5, Add), rhs = Counter(3) +- expression 1 operands: lhs = Expression(18, Sub), rhs = Counter(2) +- expression 2 operands: lhs = Counter(1), rhs = Expression(19, Add) +- expression 3 operands: lhs = Expression(20, Add), rhs = Counter(4) +- expression 4 operands: lhs = Counter(2), rhs = Counter(3) +- expression 5 operands: lhs = Expression(18, Sub), rhs = Counter(2) +- expression 6 operands: lhs = Counter(1), rhs = Expression(19, Add) +- expression 7 operands: lhs = Expression(20, Add), rhs = Counter(4) +- expression 8 operands: lhs = Counter(2), rhs = Counter(3) +- expression 9 operands: lhs = Counter(1), rhs = Expression(19, Add) +- expression 10 operands: lhs = Expression(20, Add), rhs = Counter(4) +- expression 11 operands: lhs = Counter(2), rhs = Counter(3) +- expression 12 operands: lhs = Counter(1), rhs = Expression(19, Add) +- expression 13 operands: lhs = Expression(20, Add), rhs = Counter(4) +- expression 14 operands: lhs = Counter(2), rhs = Counter(3) +- expression 15 operands: lhs = Expression(16, Add), rhs = Expression(18, Sub) +- expression 16 operands: lhs = Expression(17, Add), rhs = Counter(2) +- expression 17 operands: lhs = Counter(4), rhs = Counter(3) +- expression 18 operands: lhs = Counter(1), rhs = Expression(19, Add) +- expression 19 operands: lhs = Expression(20, Add), rhs = Counter(4) +- expression 20 operands: lhs = Counter(2), rhs = Counter(3) +Number of file 0 mappings: 10 - Code(Counter(0)) at (prev + 24, 1) to (start + 1, 16) - Code(Counter(1)) at (prev + 3, 11) to (start + 0, 16) -- Code(Counter(4)) at (prev + 1, 17) to (start + 0, 33) -- Code(Counter(3)) at (prev + 1, 17) to (start + 0, 33) -- Code(Counter(2)) at (prev + 1, 17) to (start + 0, 33) -- Code(Expression(0, Sub)) at (prev + 1, 17) to (start + 0, 33) +- Branch { true: Counter(4), false: Expression(0, Add) } at (prev + 1, 9) to (start + 0, 19) + true = c4 + false = (((c1 - ((c2 + c3) + c4)) + c2) + c3) +- Code(Counter(4)) at (prev + 0, 17) to (start + 0, 33) +- Branch { true: Counter(3), false: Expression(5, Add) } at (prev + 1, 9) to (start + 0, 19) + true = c3 + false = ((c1 - ((c2 + c3) + c4)) + c2) +- Code(Counter(3)) at (prev + 0, 17) to (start + 0, 33) +- Branch { true: Counter(2), false: Expression(18, Sub) } at (prev + 1, 9) to (start + 0, 19) + true = c2 + false = (c1 - ((c2 + c3) + c4)) +- Code(Counter(2)) at (prev + 0, 17) to (start + 0, 33) +- Code(Expression(18, Sub)) at (prev + 1, 17) to (start + 0, 33) = (c1 - ((c2 + c3) + c4)) -- Code(Expression(3, Add)) at (prev + 3, 5) to (start + 1, 2) +- Code(Expression(15, Add)) at (prev + 3, 5) to (start + 1, 2) = (((c4 + c3) + c2) + (c1 - ((c2 + c3) + c4))) Function name: match_arms::or_patterns diff --git a/tests/coverage/branch/match-arms.coverage b/tests/coverage/branch/match-arms.coverage index ea8a6f97ab154..c02c86c202e21 100644 --- a/tests/coverage/branch/match-arms.coverage +++ b/tests/coverage/branch/match-arms.coverage @@ -26,8 +26,17 @@ LL| | LL| 15| match value { LL| 8| Enum::D(d) => consume(d), + ------------------ + | Branch (LL:9): [True: 8, False: 7] + ------------------ LL| 4| Enum::C(c) => consume(c), + ------------------ + | Branch (LL:9): [True: 4, False: 3] + ------------------ LL| 2| Enum::B(b) => consume(b), + ------------------ + | Branch (LL:9): [True: 2, False: 1] + ------------------ LL| 1| Enum::A(a) => consume(a), LL| | } LL| | @@ -53,18 +62,22 @@ LL| 3| match value { LL| 8| Enum::D(d) if cond => consume(d), ------------------ + | Branch (LL:9): [True: 24, False: 21] | Branch (LL:23): [True: 8, False: 16] ------------------ LL| 4| Enum::C(c) if cond => consume(c), ------------------ + | Branch (LL:9): [True: 12, False: 25] | Branch (LL:23): [True: 4, False: 8] ------------------ LL| 2| Enum::B(b) if cond => consume(b), ------------------ + | Branch (LL:9): [True: 6, False: 27] | Branch (LL:23): [True: 2, False: 4] ------------------ LL| 1| Enum::A(a) if cond => consume(a), ------------------ + | Branch (LL:9): [True: 3, False: 28] | Branch (LL:23): [True: 1, False: 2] ------------------ LL| 30| _ => consume(0), @@ -101,5 +114,5 @@ LL| | } LL| |} LL| | - LL| |// FIXME(#124118) Actually instrument match arms for branch coverage. + LL| |// FIXME(#124118) Support match expressions with or-patterns. diff --git a/tests/coverage/branch/match-arms.rs b/tests/coverage/branch/match-arms.rs index 63151f59ffe9b..0cc55e2212b72 100644 --- a/tests/coverage/branch/match-arms.rs +++ b/tests/coverage/branch/match-arms.rs @@ -87,4 +87,4 @@ fn main() { } } -// FIXME(#124118) Actually instrument match arms for branch coverage. +// FIXME(#124118) Support match expressions with or-patterns. diff --git a/tests/mir-opt/coverage/branch_match_arms.main.InstrumentCoverage.diff b/tests/mir-opt/coverage/branch_match_arms.main.InstrumentCoverage.diff index e60f71f47b1ed..889d02a7e9647 100644 --- a/tests/mir-opt/coverage/branch_match_arms.main.InstrumentCoverage.diff +++ b/tests/mir-opt/coverage/branch_match_arms.main.InstrumentCoverage.diff @@ -26,12 +26,25 @@ debug a => _9; } + coverage branches { + BlockMarkerId(0), BlockMarkerId(1) => $DIR/branch_match_arms.rs:16:9: 16:19 (#0) + BlockMarkerId(2), BlockMarkerId(3) => $DIR/branch_match_arms.rs:17:9: 17:19 (#0) + BlockMarkerId(4), BlockMarkerId(5) => $DIR/branch_match_arms.rs:18:9: 18:19 (#0) + BlockMarkerId(6), BlockMarkerId(7) => $DIR/branch_match_arms.rs:19:9: 19:19 (#0) + } + + coverage ExpressionId(0) => Expression { lhs: Counter(1), op: Add, rhs: Counter(2) }; + coverage ExpressionId(1) => Expression { lhs: Expression(0), op: Add, rhs: Counter(3) }; + coverage ExpressionId(2) => Expression { lhs: Counter(0), op: Subtract, rhs: Expression(1) }; + coverage ExpressionId(3) => Expression { lhs: Counter(3), op: Add, rhs: Counter(2) }; + coverage ExpressionId(4) => Expression { lhs: Expression(3), op: Add, rhs: Counter(1) }; + coverage ExpressionId(5) => Expression { lhs: Expression(4), op: Add, rhs: Expression(2) }; ++ coverage ExpressionId(6) => Expression { lhs: Expression(2), op: Add, rhs: Counter(1) }; ++ coverage ExpressionId(7) => Expression { lhs: Expression(6), op: Add, rhs: Counter(2) }; ++ coverage ExpressionId(8) => Expression { lhs: Expression(7), op: Add, rhs: Counter(3) }; ++ coverage Branch { true_term: Counter(1), false_term: Expression(2) } => $DIR/branch_match_arms.rs:18:9 - 18:19; ++ coverage Branch { true_term: Counter(2), false_term: Expression(6) } => $DIR/branch_match_arms.rs:17:9 - 17:19; ++ coverage Branch { true_term: Counter(3), false_term: Expression(7) } => $DIR/branch_match_arms.rs:16:9 - 16:19; + coverage Code(Counter(0)) => $DIR/branch_match_arms.rs:14:1 - 15:21; + coverage Code(Counter(3)) => $DIR/branch_match_arms.rs:16:17 - 16:33; + coverage Code(Counter(2)) => $DIR/branch_match_arms.rs:17:17 - 17:33; @@ -55,16 +68,19 @@ bb2: { + Coverage::CounterIncrement(3); + Coverage::BlockMarker(0); falseEdge -> [real: bb6, imaginary: bb3]; } bb3: { + Coverage::CounterIncrement(2); + Coverage::BlockMarker(2); falseEdge -> [real: bb8, imaginary: bb4]; } bb4: { + Coverage::CounterIncrement(1); + Coverage::BlockMarker(4); falseEdge -> [real: bb10, imaginary: bb5]; } @@ -74,6 +90,8 @@ _9 = ((_1 as A).0: u32); StorageLive(_10); _10 = _9; + Coverage::BlockMarker(6); + Coverage::BlockMarker(7); _0 = consume(move _10) -> [return: bb12, unwind: bb14]; } @@ -82,6 +100,7 @@ _3 = ((_1 as D).0: u32); StorageLive(_4); _4 = _3; + Coverage::BlockMarker(1); _0 = consume(move _4) -> [return: bb7, unwind: bb14]; } @@ -96,6 +115,7 @@ _5 = ((_1 as C).0: u32); StorageLive(_6); _6 = _5; + Coverage::BlockMarker(3); _0 = consume(move _6) -> [return: bb9, unwind: bb14]; } @@ -110,6 +130,7 @@ _7 = ((_1 as B).0: u32); StorageLive(_8); _8 = _7; + Coverage::BlockMarker(5); _0 = consume(move _8) -> [return: bb11, unwind: bb14]; }