Skip to content

Commit

Permalink
coverage: Treat each match arm as a "branch" for branch coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
Zalathar committed Apr 22, 2024
1 parent 0f181b5 commit 68b1f2c
Show file tree
Hide file tree
Showing 12 changed files with 254 additions and 65 deletions.
31 changes: 31 additions & 0 deletions compiler/rustc_mir_build/src/build/coverageinfo.rs
Expand Up @@ -38,6 +38,12 @@ struct NotInfo {
is_flipped: bool,
}

pub(crate) struct MatchArm {
pub(crate) source_info: SourceInfo,
pub(crate) pre_binding_block: Option<BasicBlock>,
pub(crate) arm_block: BasicBlock,
}

impl BranchInfoBuilder {
/// Creates a new branch info builder, but only if branch coverage instrumentation
/// is enabled and `def_id` represents a function that is eligible for coverage.
Expand Down Expand Up @@ -152,6 +158,31 @@ impl BranchInfoBuilder {
self.branch_arm_lists.push(vec![arm(true_marker), arm(false_marker)]);
}

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.inject_block_marker(cfg, source_info, pre_binding_block.unwrap());
let arm_taken_marker = self.inject_block_marker(cfg, source_info, arm_block);
BranchArm { span: source_info.span, pre_guard_marker, arm_taken_marker }
})
.collect::<Vec<_>>();

self.branch_arm_lists.push(branch_arms);
}

fn next_block_marker_id(&mut self) -> BlockMarkerId {
let id = BlockMarkerId::from_usize(self.num_block_markers);
self.num_block_markers += 1;
Expand Down
20 changes: 20 additions & 0 deletions compiler/rustc_mir_build/src/build/matches/mod.rs
Expand Up @@ -5,6 +5,7 @@
//! This also includes code for pattern bindings in `let` statements and
//! function parameters.

use crate::build::coverageinfo;
use crate::build::expr::as_place::PlaceBuilder;
use crate::build::scope::DropKind;
use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard};
Expand Down Expand Up @@ -461,6 +462,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
outer_source_info: SourceInfo,
fake_borrow_temps: Vec<(Place<'tcx>, Local)>,
) -> 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)| {
Expand Down Expand Up @@ -495,6 +498,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,
Expand All @@ -504,6 +509,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 {
Expand All @@ -515,6 +528,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();

Expand Down
33 changes: 22 additions & 11 deletions 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)

2 changes: 2 additions & 0 deletions tests/coverage/branch/guard-simple.coverage
Expand Up @@ -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| _ => {}
Expand Down
46 changes: 31 additions & 15 deletions 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)

5 changes: 5 additions & 0 deletions tests/coverage/branch/guard.coverage
Expand Up @@ -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");
Expand Down

0 comments on commit 68b1f2c

Please sign in to comment.