Skip to content

Commit 2de5a33

Browse files
Zalatharranger-ross
authored andcommitted
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 f9ac1b0 commit 2de5a33

File tree

4 files changed

+96
-44
lines changed

4 files changed

+96
-44
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,14 @@ impl<'tcx> FunctionCoverageCollector<'tcx> {
6666
// For each expression ID that is directly used by one or more mappings,
6767
// mark it as not-yet-seen. This indicates that we expect to see a
6868
// corresponding `ExpressionUsed` statement during MIR traversal.
69-
for mapping in function_coverage_info.mappings.iter() {
69+
for mapping in function_coverage_info
70+
.mappings
71+
.iter()
72+
// For many-armed branches, some branch mappings will have expressions
73+
// that don't correspond to any node in the control-flow graph, so don't
74+
// expect to see `ExpressionUsed` statements for them.
75+
.filter(|m| !matches!(m.kind, MappingKind::Branch { .. }))
76+
{
7077
// Currently we only worry about ordinary code mappings.
7178
// For branch and MC/DC mappings, expressions might not correspond
7279
// to any particular point in the control-flow graph.
@@ -196,7 +203,11 @@ impl<'tcx> FunctionCoverage<'tcx> {
196203
/// Return the source hash, generated from the HIR node structure, and used to indicate whether
197204
/// or not the source code structure changed between different compilations.
198205
pub(crate) fn source_hash(&self) -> u64 {
199-
if self.is_used { self.function_coverage_info.function_source_hash } else { 0 }
206+
if self.is_used {
207+
self.function_coverage_info.function_source_hash
208+
} else {
209+
0
210+
}
200211
}
201212

202213
/// Returns an iterator over all filenames used by this function's mappings.
@@ -240,7 +251,11 @@ impl<'tcx> FunctionCoverage<'tcx> {
240251
}
241252

242253
fn counter_for_term(&self, term: CovTerm) -> Counter {
243-
if self.is_zero_term(term) { Counter::ZERO } else { Counter::from_term(term) }
254+
if self.is_zero_term(term) {
255+
Counter::ZERO
256+
} else {
257+
Counter::from_term(term)
258+
}
244259
}
245260

246261
fn is_zero_term(&self, term: CovTerm) -> bool {

compiler/rustc_mir_transform/src/coverage/counters.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,12 @@ impl CoverageCounters {
122122
self.set_bcb_edge_counter(from_bcb, to_bcb, counter)
123123
}
124124

125-
fn make_expression(&mut self, lhs: BcbCounter, op: Op, rhs: BcbCounter) -> BcbCounter {
125+
pub(super) fn make_expression(
126+
&mut self,
127+
lhs: BcbCounter,
128+
op: Op,
129+
rhs: BcbCounter,
130+
) -> BcbCounter {
126131
let new_expr = BcbExpression { lhs, op, rhs };
127132
*self
128133
.expressions_memo

compiler/rustc_mir_transform/src/coverage/mappings.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,11 @@ pub(super) struct CodeMapping {
2222
pub(super) bcb: BasicCoverageBlock,
2323
}
2424

25-
/// This is separate from [`MCDCBranch`] to help prepare for larger changes
26-
/// that will be needed for improved branch coverage in the future.
27-
/// (See <https://github.com/rust-lang/rust/pull/124217>.)
2825
#[derive(Debug)]
29-
pub(super) struct BranchPair {
26+
pub(super) struct BranchArm {
3027
pub(super) span: Span,
31-
pub(super) true_bcb: BasicCoverageBlock,
32-
pub(super) false_bcb: BasicCoverageBlock,
28+
pub(super) pre_guard_bcb: BasicCoverageBlock,
29+
pub(super) arm_taken_bcb: BasicCoverageBlock,
3330
}
3431

3532
/// Associates an MC/DC branch span with condition info besides fields for normal branch.
@@ -61,7 +58,7 @@ pub(super) struct ExtractedMappings {
6158
/// only public so that other code can still use exhaustive destructuring.
6259
pub(super) num_bcbs: usize,
6360
pub(super) code_mappings: Vec<CodeMapping>,
64-
pub(super) branch_pairs: Vec<BranchPair>,
61+
pub(super) branch_arm_lists: Vec<Vec<BranchArm>>,
6562
pub(super) mcdc_bitmap_bytes: u32,
6663
pub(super) mcdc_branches: Vec<MCDCBranch>,
6764
pub(super) mcdc_decisions: Vec<MCDCDecision>,
@@ -76,7 +73,7 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>(
7673
basic_coverage_blocks: &CoverageGraph,
7774
) -> ExtractedMappings {
7875
let mut code_mappings = vec![];
79-
let mut branch_pairs = vec![];
76+
let mut branch_arm_lists = vec![];
8077
let mut mcdc_bitmap_bytes = 0;
8178
let mut mcdc_branches = vec![];
8279
let mut mcdc_decisions = vec![];
@@ -98,7 +95,7 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>(
9895
extract_refined_covspans(mir_body, hir_info, basic_coverage_blocks, &mut code_mappings);
9996
}
10097

101-
branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, basic_coverage_blocks));
98+
branch_arm_lists.extend(extract_branch_arm_lists(mir_body, hir_info, basic_coverage_blocks));
10299

103100
extract_mcdc_mappings(
104101
mir_body,
@@ -112,7 +109,7 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>(
112109
ExtractedMappings {
113110
num_bcbs: basic_coverage_blocks.num_nodes(),
114111
code_mappings,
115-
branch_pairs,
112+
branch_arm_lists,
116113
mcdc_bitmap_bytes,
117114
mcdc_branches,
118115
mcdc_decisions,
@@ -125,7 +122,7 @@ impl ExtractedMappings {
125122
let Self {
126123
num_bcbs,
127124
code_mappings,
128-
branch_pairs,
125+
branch_arm_lists,
129126
mcdc_bitmap_bytes: _,
130127
mcdc_branches,
131128
mcdc_decisions,
@@ -140,9 +137,11 @@ impl ExtractedMappings {
140137
for &CodeMapping { span: _, bcb } in code_mappings {
141138
insert(bcb);
142139
}
143-
for &BranchPair { true_bcb, false_bcb, .. } in branch_pairs {
144-
insert(true_bcb);
145-
insert(false_bcb);
140+
for &BranchArm { span: _, pre_guard_bcb, arm_taken_bcb } in
141+
branch_arm_lists.iter().flatten()
142+
{
143+
insert(pre_guard_bcb);
144+
insert(arm_taken_bcb);
146145
}
147146
for &MCDCBranch { true_bcb, false_bcb, .. } in mcdc_branches {
148147
insert(true_bcb);
@@ -192,16 +191,16 @@ fn resolve_block_markers(
192191
}
193192

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

200-
pub(super) fn extract_branch_pairs(
199+
pub(super) fn extract_branch_arm_lists(
201200
mir_body: &mir::Body<'_>,
202201
hir_info: &ExtractedHirInfo,
203202
basic_coverage_blocks: &CoverageGraph,
204-
) -> Vec<BranchPair> {
203+
) -> Vec<Vec<BranchArm>> {
205204
let Some(coverage_info_hi) = mir_body.coverage_info_hi.as_deref() else { return vec![] };
206205

207206
let block_markers = resolve_block_markers(coverage_info_hi, mir_body);
@@ -223,7 +222,8 @@ pub(super) fn extract_branch_pairs(
223222
let true_bcb = bcb_from_marker(true_marker)?;
224223
let false_bcb = bcb_from_marker(false_marker)?;
225224

226-
Some(BranchPair { span, true_bcb, false_bcb })
225+
let arm = |bcb| BranchArm { span, pre_guard_bcb: bcb, arm_taken_bcb: bcb };
226+
Some(vec![arm(true_bcb), arm(false_bcb)])
227227
})
228228
.collect::<Vec<_>>()
229229
}

compiler/rustc_mir_transform/src/coverage/mod.rs

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_hir::intravisit::{Visitor, walk_expr};
1313
use rustc_middle::hir::map::Map;
1414
use rustc_middle::hir::nested_filter;
1515
use rustc_middle::mir::coverage::{
16-
CoverageKind, DecisionInfo, FunctionCoverageInfo, Mapping, MappingKind, SourceRegion,
16+
CoverageKind, DecisionInfo, FunctionCoverageInfo, Mapping, MappingKind, Op, SourceRegion,
1717
};
1818
use rustc_middle::mir::{
1919
self, BasicBlock, BasicBlockData, SourceInfo, Statement, StatementKind, Terminator,
@@ -27,7 +27,7 @@ use tracing::{debug, debug_span, instrument, trace};
2727

2828
use crate::coverage::counters::{CounterIncrementSite, CoverageCounters};
2929
use crate::coverage::graph::CoverageGraph;
30-
use crate::coverage::mappings::ExtractedMappings;
30+
use crate::coverage::mappings::{BranchArm, ExtractedMappings};
3131

3232
/// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected
3333
/// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen
@@ -95,10 +95,10 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
9595
}
9696

9797
let bcb_has_counter_mappings = |bcb| bcbs_with_counter_mappings.contains(bcb);
98-
let coverage_counters =
98+
let mut coverage_counters =
9999
CoverageCounters::make_bcb_counters(&basic_coverage_blocks, bcb_has_counter_mappings);
100100

101-
let mappings = create_mappings(tcx, &hir_info, &extracted_mappings, &coverage_counters);
101+
let mappings = create_mappings(tcx, &hir_info, &extracted_mappings, &mut coverage_counters);
102102
if mappings.is_empty() {
103103
// No spans could be converted into valid mappings, so skip this function.
104104
debug!("no spans could be converted into valid mappings; skipping");
@@ -140,7 +140,7 @@ fn create_mappings<'tcx>(
140140
tcx: TyCtxt<'tcx>,
141141
hir_info: &ExtractedHirInfo,
142142
extracted_mappings: &ExtractedMappings,
143-
coverage_counters: &CoverageCounters,
143+
coverage_counters: &mut CoverageCounters,
144144
) -> Vec<Mapping> {
145145
let source_map = tcx.sess.source_map();
146146
let body_span = hir_info.body_span;
@@ -153,25 +153,67 @@ fn create_mappings<'tcx>(
153153
&source_file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy(),
154154
);
155155

156-
let term_for_bcb = |bcb| {
157-
coverage_counters
158-
.bcb_counter(bcb)
159-
.expect("all BCBs with spans were given counters")
160-
.as_term()
161-
};
162156
let region_for_span = |span: Span| make_source_region(source_map, file_name, span, body_span);
163157

164158
// Fully destructure the mappings struct to make sure we don't miss any kinds.
165159
let ExtractedMappings {
166160
num_bcbs: _,
167161
code_mappings,
168-
branch_pairs,
162+
branch_arm_lists,
169163
mcdc_bitmap_bytes: _,
170164
mcdc_branches,
171165
mcdc_decisions,
172166
} = extracted_mappings;
173167
let mut mappings = Vec::new();
174168

169+
// Process branch arms first, because they might need to mutate `coverage_counters`
170+
// to create new expressions.
171+
for arm_list in branch_arm_lists {
172+
let mut arms_rev = arm_list.iter().rev();
173+
174+
let mut rest_counter = {
175+
// The last arm's span is ignored, because its BCB is only used as the
176+
// false branch of the second-last arm; it's not a branch of its own.
177+
let Some(&BranchArm { span: _, pre_guard_bcb, arm_taken_bcb }) = arms_rev.next() else {
178+
continue;
179+
};
180+
debug_assert_eq!(pre_guard_bcb, arm_taken_bcb, "last arm should not have a guard");
181+
coverage_counters.bcb_counter(pre_guard_bcb).expect("all relevant BCBs have counters")
182+
};
183+
184+
// All relevant BCBs should have counters, so we can `.unwrap()` them.
185+
for &BranchArm { span, pre_guard_bcb, arm_taken_bcb } in arms_rev {
186+
// Number of times the pattern matched.
187+
let matched_counter = coverage_counters.bcb_counter(pre_guard_bcb).unwrap();
188+
// Number of times the pattern matched and the guard succeeded.
189+
let arm_taken_counter = coverage_counters.bcb_counter(arm_taken_bcb).unwrap();
190+
// Total number of times execution logically reached this pattern.
191+
let reached_counter =
192+
coverage_counters.make_expression(rest_counter, Op::Add, arm_taken_counter);
193+
// Number of times execution reached this pattern, but didn't match it.
194+
let unmatched_counter =
195+
coverage_counters.make_expression(reached_counter, Op::Subtract, matched_counter);
196+
197+
let kind = MappingKind::Branch {
198+
true_term: matched_counter.as_term(),
199+
false_term: unmatched_counter.as_term(),
200+
};
201+
202+
if let Some(source_region) = region_for_span(span) {
203+
mappings.push(Mapping { kind, source_region });
204+
}
205+
206+
rest_counter = reached_counter;
207+
}
208+
}
209+
210+
let term_for_bcb = |bcb| {
211+
coverage_counters
212+
.bcb_counter(bcb)
213+
.expect("all BCBs with spans were given counters")
214+
.as_term()
215+
};
216+
175217
mappings.extend(code_mappings.iter().filter_map(
176218
// Ordinary code mappings are the simplest kind.
177219
|&mappings::CodeMapping { span, bcb }| {
@@ -181,16 +223,6 @@ fn create_mappings<'tcx>(
181223
},
182224
));
183225

184-
mappings.extend(branch_pairs.iter().filter_map(
185-
|&mappings::BranchPair { span, true_bcb, false_bcb }| {
186-
let true_term = term_for_bcb(true_bcb);
187-
let false_term = term_for_bcb(false_bcb);
188-
let kind = MappingKind::Branch { true_term, false_term };
189-
let source_region = region_for_span(span)?;
190-
Some(Mapping { kind, source_region })
191-
},
192-
));
193-
194226
mappings.extend(mcdc_branches.iter().filter_map(
195227
|&mappings::MCDCBranch { span, true_bcb, false_bcb, condition_info, decision_depth: _ }| {
196228
let source_region = region_for_span(span)?;

0 commit comments

Comments
 (0)