Skip to content

Commit ec65234

Browse files
committed
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.
1 parent 92f6c8e commit ec65234

File tree

5 files changed

+76
-34
lines changed

5 files changed

+76
-34
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,15 @@ impl<'tcx> FunctionCoverageCollector<'tcx> {
6565
// For each expression ID that is directly used by one or more mappings,
6666
// mark it as not-yet-seen. This indicates that we expect to see a
6767
// corresponding `ExpressionUsed` statement during MIR traversal.
68-
for term in function_coverage_info.mappings.iter().flat_map(|m| m.kind.terms()) {
68+
for term in function_coverage_info
69+
.mappings
70+
.iter()
71+
// For many-armed branches, some branch mappings will have expressions
72+
// that don't correspond to any node in the control-flow graph, so don't
73+
// expect to see `ExpressionUsed` statements for them.
74+
.filter(|m| !matches!(m.kind, MappingKind::Branch { .. }))
75+
.flat_map(|m| m.kind.terms())
76+
{
6977
if let CovTerm::Expression(id) = term {
7078
expressions_seen.remove(id);
7179
}

compiler/rustc_mir_transform/src/coverage/counters.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,12 @@ impl CoverageCounters {
9595
BcbCounter::Counter { id }
9696
}
9797

98-
fn make_expression(&mut self, lhs: BcbCounter, op: Op, rhs: BcbCounter) -> BcbCounter {
98+
pub(super) fn make_expression(
99+
&mut self,
100+
lhs: BcbCounter,
101+
op: Op,
102+
rhs: BcbCounter,
103+
) -> BcbCounter {
99104
// Replace `(a + b) - b` with `a`, since this happens often.
100105
if op == Op::Subtract
101106
&& let BcbCounter::Expression { id } = lhs

compiler/rustc_mir_transform/src/coverage/mod.rs

+43-13
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ mod tests;
99

1010
use self::counters::{CounterIncrementSite, CoverageCounters};
1111
use self::graph::{BasicCoverageBlock, CoverageGraph};
12-
use self::spans::{BcbBranchPair, BcbMapping, BcbMappingKind, CoverageSpans};
12+
use self::spans::{BcbBranchArm, BcbMapping, BcbMappingKind, CoverageSpans};
1313

1414
use crate::MirPass;
1515

@@ -83,10 +83,10 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
8383
// and all `Expression` dependencies (operands) are also generated, for any other
8484
// `BasicCoverageBlock`s not already associated with a coverage span.
8585
let bcb_has_coverage_spans = |bcb| coverage_spans.bcb_has_coverage_spans(bcb);
86-
let coverage_counters =
86+
let mut coverage_counters =
8787
CoverageCounters::make_bcb_counters(&basic_coverage_blocks, bcb_has_coverage_spans);
8888

89-
let mappings = create_mappings(tcx, &hir_info, &coverage_spans, &coverage_counters);
89+
let mappings = create_mappings(tcx, &hir_info, &coverage_spans, &mut coverage_counters);
9090
if mappings.is_empty() {
9191
// No spans could be converted into valid mappings, so skip this function.
9292
debug!("no spans could be converted into valid mappings; skipping");
@@ -120,7 +120,7 @@ fn create_mappings<'tcx>(
120120
tcx: TyCtxt<'tcx>,
121121
hir_info: &ExtractedHirInfo,
122122
coverage_spans: &CoverageSpans,
123-
coverage_counters: &CoverageCounters,
123+
coverage_counters: &mut CoverageCounters,
124124
) -> Vec<Mapping> {
125125
let source_map = tcx.sess.source_map();
126126
let body_span = hir_info.body_span;
@@ -169,15 +169,45 @@ fn create_mappings<'tcx>(
169169
},
170170
));
171171

172-
mappings.extend(coverage_spans.branch_pairs.iter().filter_map(
173-
|&BcbBranchPair { span, true_bcb, false_bcb }| {
174-
let true_term = term_for_bcb(true_bcb);
175-
let false_term = term_for_bcb(false_bcb);
176-
let kind = MappingKind::Branch { true_term, false_term };
177-
let code_region = make_code_region(source_map, file_name, span, body_span)?;
178-
Some(Mapping { kind, code_region })
179-
},
180-
));
172+
for arm_list in &coverage_spans.branch_arm_lists {
173+
let mut arms_rev = arm_list.iter().rev();
174+
175+
let mut rest_counter = {
176+
// The last arm's span is ignored, because its BCB is only used as the
177+
// false branch of the second-last arm; it's not a branch of its own.
178+
let Some(&BcbBranchArm { span: _, pre_guard_bcb, arm_taken_bcb }) = arms_rev.next()
179+
else {
180+
continue;
181+
};
182+
debug_assert_eq!(pre_guard_bcb, arm_taken_bcb, "last arm should not have a guard");
183+
coverage_counters.bcb_counter(pre_guard_bcb).expect("all relevant BCBs have counters")
184+
};
185+
186+
// All relevant BCBs should have counters, so we can `.unwrap()` them.
187+
for &BcbBranchArm { span, pre_guard_bcb, arm_taken_bcb } in arms_rev {
188+
// Number of times the pattern matched.
189+
let matched_counter = coverage_counters.bcb_counter(pre_guard_bcb).unwrap();
190+
// Number of times the pattern matched and the guard succeeded.
191+
let arm_taken_counter = coverage_counters.bcb_counter(arm_taken_bcb).unwrap();
192+
// Total number of times execution logically reached this pattern.
193+
let reached_counter =
194+
coverage_counters.make_expression(rest_counter, Op::Add, arm_taken_counter);
195+
// Number of times execution reached this pattern, but didn't match it.
196+
let unmatched_counter =
197+
coverage_counters.make_expression(reached_counter, Op::Subtract, matched_counter);
198+
199+
let kind = MappingKind::Branch {
200+
true_term: matched_counter.as_term(),
201+
false_term: unmatched_counter.as_term(),
202+
};
203+
204+
if let Some(code_region) = make_code_region(source_map, file_name, span, body_span) {
205+
mappings.push(Mapping { kind, code_region });
206+
}
207+
208+
rest_counter = reached_counter;
209+
}
210+
}
181211

182212
mappings
183213
}

compiler/rustc_mir_transform/src/coverage/spans.rs

+12-14
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,17 @@ pub(super) struct BcbMapping {
3737
pub(super) span: Span,
3838
}
3939

40-
/// This is separate from [`BcbMappingKind`] to help prepare for larger changes
41-
/// that will be needed for improved branch coverage in the future.
42-
/// (See <https://github.com./rust-lang/rust/pull/124217>.)
4340
#[derive(Debug)]
44-
pub(super) struct BcbBranchPair {
41+
pub(super) struct BcbBranchArm {
4542
pub(super) span: Span,
46-
pub(super) true_bcb: BasicCoverageBlock,
47-
pub(super) false_bcb: BasicCoverageBlock,
43+
pub(super) pre_guard_bcb: BasicCoverageBlock,
44+
pub(super) arm_taken_bcb: BasicCoverageBlock,
4845
}
4946

5047
pub(super) struct CoverageSpans {
5148
bcb_has_mappings: BitSet<BasicCoverageBlock>,
5249
pub(super) mappings: Vec<BcbMapping>,
53-
pub(super) branch_pairs: Vec<BcbBranchPair>,
50+
pub(super) branch_arm_lists: Vec<Vec<BcbBranchArm>>,
5451
test_vector_bitmap_bytes: u32,
5552
}
5653

@@ -74,7 +71,7 @@ pub(super) fn generate_coverage_spans(
7471
basic_coverage_blocks: &CoverageGraph,
7572
) -> Option<CoverageSpans> {
7673
let mut mappings = vec![];
77-
let mut branch_pairs = vec![];
74+
let mut branch_arm_lists = vec![];
7875

7976
if hir_info.is_async_fn {
8077
// An async function desugars into a function that returns a future,
@@ -96,7 +93,7 @@ pub(super) fn generate_coverage_spans(
9693
BcbMapping { kind: BcbMappingKind::Code(bcb), span }
9794
}));
9895

99-
branch_pairs.extend(from_mir::extract_branch_pairs(
96+
branch_arm_lists.extend(from_mir::extract_branch_arm_lists(
10097
mir_body,
10198
hir_info,
10299
basic_coverage_blocks,
@@ -109,7 +106,7 @@ pub(super) fn generate_coverage_spans(
109106
));
110107
}
111108

112-
if mappings.is_empty() && branch_pairs.is_empty() {
109+
if mappings.is_empty() && branch_arm_lists.is_empty() {
113110
return None;
114111
}
115112

@@ -135,12 +132,13 @@ pub(super) fn generate_coverage_spans(
135132
}
136133
}
137134
}
138-
for &BcbBranchPair { true_bcb, false_bcb, .. } in &branch_pairs {
139-
insert(true_bcb);
140-
insert(false_bcb);
135+
for &BcbBranchArm { span: _, pre_guard_bcb, arm_taken_bcb } in branch_arm_lists.iter().flatten()
136+
{
137+
insert(pre_guard_bcb);
138+
insert(arm_taken_bcb);
141139
}
142140

143-
Some(CoverageSpans { bcb_has_mappings, mappings, branch_pairs, test_vector_bitmap_bytes })
141+
Some(CoverageSpans { bcb_has_mappings, mappings, branch_arm_lists, test_vector_bitmap_bytes })
144142
}
145143

146144
#[derive(Debug)]

compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
1313
use crate::coverage::graph::{
1414
BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
1515
};
16-
use crate::coverage::spans::{BcbBranchPair, BcbMapping, BcbMappingKind};
16+
use crate::coverage::spans::{BcbBranchArm, BcbMapping, BcbMappingKind};
1717
use crate::coverage::ExtractedHirInfo;
1818

1919
/// Traverses the MIR body to produce an initial collection of coverage-relevant
@@ -388,16 +388,16 @@ fn resolve_block_markers(
388388
}
389389

390390
// FIXME: There is currently a lot of redundancy between
391-
// `extract_branch_pairs` and `extract_mcdc_mappings`. This is needed so
391+
// `extract_branch_arm_lists` and `extract_mcdc_mappings`. This is needed so
392392
// that they can each be modified without interfering with the other, but in
393393
// the long term we should try to bring them together again when branch coverage
394394
// and MC/DC coverage support are more mature.
395395

396-
pub(super) fn extract_branch_pairs(
396+
pub(super) fn extract_branch_arm_lists(
397397
mir_body: &mir::Body<'_>,
398398
hir_info: &ExtractedHirInfo,
399399
basic_coverage_blocks: &CoverageGraph,
400-
) -> Vec<BcbBranchPair> {
400+
) -> Vec<Vec<BcbBranchArm>> {
401401
let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { return vec![] };
402402

403403
let block_markers = resolve_block_markers(branch_info, mir_body);
@@ -420,7 +420,8 @@ pub(super) fn extract_branch_pairs(
420420
let true_bcb = bcb_from_marker(true_marker)?;
421421
let false_bcb = bcb_from_marker(false_marker)?;
422422

423-
Some(BcbBranchPair { span, true_bcb, false_bcb })
423+
let arm = |bcb| BcbBranchArm { span, pre_guard_bcb: bcb, arm_taken_bcb: bcb };
424+
Some(vec![arm(true_bcb), arm(false_bcb)])
424425
})
425426
.collect::<Vec<_>>()
426427
}

0 commit comments

Comments
 (0)