Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 8712975

Browse files
author
zhuyunxing
committedJun 20, 2024
coverage. Introduce MCDCBranchMarker to trace branch blocks of conditions
1 parent c002a8f commit 8712975

File tree

7 files changed

+276
-191
lines changed

7 files changed

+276
-191
lines changed
 

‎compiler/rustc_middle/src/mir/coverage.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -291,8 +291,8 @@ pub struct BranchInfo {
291291
/// data structures without having to scan the entire body first.
292292
pub num_block_markers: usize,
293293
pub branch_spans: Vec<BranchSpan>,
294-
pub mcdc_branch_spans: Vec<MCDCBranchSpan>,
295-
pub mcdc_decision_spans: Vec<MCDCDecisionSpan>,
294+
pub mcdc_degraded_spans: Vec<MCDCBranchSpan>,
295+
pub mcdc_spans: Vec<(MCDCDecisionSpan, Vec<MCDCBranchSpan>)>,
296296
}
297297

298298
#[derive(Clone, Debug)]
@@ -325,12 +325,16 @@ impl Default for ConditionInfo {
325325
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
326326
pub struct MCDCBranchSpan {
327327
pub span: Span,
328-
/// If `None`, this actually represents a normal branch span inserted for
329-
/// code that was too complex for MC/DC.
330-
pub condition_info: Option<ConditionInfo>,
331-
pub true_marker: BlockMarkerId,
332-
pub false_marker: BlockMarkerId,
333-
pub decision_depth: u16,
328+
pub condition_info: ConditionInfo,
329+
pub markers: MCDCBranchMarkers,
330+
}
331+
332+
#[derive(Clone, Debug)]
333+
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
334+
pub enum MCDCBranchMarkers {
335+
/// The first indicates true branch, the second indicates the false branch.
336+
Boolean(BlockMarkerId, BlockMarkerId),
337+
PatternMatching,
334338
}
335339

336340
#[derive(Copy, Clone, Debug)]
@@ -344,7 +348,6 @@ pub struct DecisionInfo {
344348
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
345349
pub struct MCDCDecisionSpan {
346350
pub span: Span,
347-
pub num_conditions: usize,
348351
pub end_markers: Vec<BlockMarkerId>,
349352
pub decision_depth: u16,
350353
}

‎compiler/rustc_middle/src/mir/pretty.rs

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -487,8 +487,7 @@ fn write_coverage_branch_info(
487487
branch_info: &coverage::BranchInfo,
488488
w: &mut dyn io::Write,
489489
) -> io::Result<()> {
490-
let coverage::BranchInfo { branch_spans, mcdc_branch_spans, mcdc_decision_spans, .. } =
491-
branch_info;
490+
let coverage::BranchInfo { branch_spans, mcdc_degraded_spans, mcdc_spans, .. } = branch_info;
492491

493492
for coverage::BranchSpan { span, true_marker, false_marker } in branch_spans {
494493
writeln!(
@@ -497,32 +496,30 @@ fn write_coverage_branch_info(
497496
)?;
498497
}
499498

500-
for coverage::MCDCBranchSpan {
501-
span,
502-
condition_info,
503-
true_marker,
504-
false_marker,
505-
decision_depth,
506-
} in mcdc_branch_spans
507-
{
499+
for coverage::MCDCBranchSpan { span, markers, .. } in mcdc_degraded_spans {
508500
writeln!(
509501
w,
510-
"{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?}, depth: {decision_depth:?} }} => {span:?}",
511-
condition_info.map(|info| info.condition_id)
502+
"{INDENT}coverage mcdc degraded branch {{ markers: {markers:?} }} => {span:?}",
512503
)?;
513504
}
514505

515-
for coverage::MCDCDecisionSpan { span, num_conditions, end_markers, decision_depth } in
516-
mcdc_decision_spans
506+
for (coverage::MCDCDecisionSpan { span, end_markers, decision_depth }, conditions) in mcdc_spans
517507
{
508+
let num_conditions = conditions.len();
518509
writeln!(
519510
w,
520511
"{INDENT}coverage mcdc decision {{ num_conditions: {num_conditions:?}, end: {end_markers:?}, depth: {decision_depth:?} }} => {span:?}"
521512
)?;
513+
for coverage::MCDCBranchSpan { span, condition_info, markers } in conditions {
514+
writeln!(
515+
w,
516+
"{INDENT}coverage mcdc branch {{ condition_id: {:?}, markers: {markers:?} }} => {span:?}",
517+
condition_info.condition_id
518+
)?;
519+
}
522520
}
523521

524-
if !branch_spans.is_empty() || !mcdc_branch_spans.is_empty() || !mcdc_decision_spans.is_empty()
525-
{
522+
if !branch_spans.is_empty() || !mcdc_degraded_spans.is_empty() || !mcdc_spans.is_empty() {
526523
writeln!(w)?;
527524
}
528525

‎compiler/rustc_mir_build/src/build/coverageinfo.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -162,18 +162,13 @@ impl BranchInfoBuilder {
162162
return None;
163163
}
164164

165-
let (mut mcdc_branch_spans, mcdc_spans) =
165+
let (mcdc_degraded_spans, mcdc_spans) =
166166
mcdc_info.map(MCDCInfoBuilder::into_done).unwrap_or_default();
167-
let mut mcdc_decision_spans = Vec::with_capacity(mcdc_spans.len());
168-
for (decision, conditions) in mcdc_spans {
169-
mcdc_branch_spans.extend(conditions);
170-
mcdc_decision_spans.push(decision);
171-
}
172167
Some(Box::new(mir::coverage::BranchInfo {
173168
num_block_markers,
174169
branch_spans,
175-
mcdc_branch_spans,
176-
mcdc_decision_spans,
170+
mcdc_degraded_spans,
171+
mcdc_spans,
177172
}))
178173
}
179174
}

‎compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use std::collections::VecDeque;
33
use rustc_data_structures::fx::FxIndexMap;
44
use rustc_middle::bug;
55
use rustc_middle::mir::coverage::{
6-
BlockMarkerId, ConditionId, ConditionInfo, DecisionId, MCDCBranchSpan, MCDCDecisionSpan,
6+
BlockMarkerId, ConditionId, ConditionInfo, DecisionId, MCDCBranchMarkers, MCDCBranchSpan,
7+
MCDCDecisionSpan,
78
};
89
use rustc_middle::mir::BasicBlock;
910
use rustc_middle::thir::LogicalOp;
@@ -29,6 +30,7 @@ struct BooleanDecisionCtx {
2930
/// To construct condition evaluation tree.
3031
decision_stack: VecDeque<ConditionInfo>,
3132
conditions: Vec<MCDCBranchSpan>,
33+
condition_id_counter: usize,
3234
}
3335

3436
impl BooleanDecisionCtx {
@@ -37,15 +39,20 @@ impl BooleanDecisionCtx {
3739
id,
3840
decision_info: MCDCDecisionSpan {
3941
span: Span::default(),
40-
num_conditions: 0,
4142
end_markers: vec![],
4243
decision_depth: 0,
4344
},
4445
decision_stack: VecDeque::new(),
4546
conditions: vec![],
47+
condition_id_counter: 0,
4648
}
4749
}
4850

51+
fn next_condition_id(&mut self) -> ConditionId {
52+
self.condition_id_counter += 1;
53+
ConditionId::from_usize(self.condition_id_counter)
54+
}
55+
4956
// At first we assign ConditionIds for each sub expression.
5057
// If the sub expression is composite, re-assign its ConditionId to its LHS and generate a new ConditionId for its RHS.
5158
//
@@ -89,14 +96,12 @@ impl BooleanDecisionCtx {
8996
fn record_conditions(&mut self, op: LogicalOp) {
9097
let parent_condition = self.decision_stack.pop_back().unwrap_or_default();
9198
let lhs_id = if parent_condition.condition_id == ConditionId::NONE {
92-
self.decision_info.num_conditions += 1;
93-
ConditionId::from(self.decision_info.num_conditions)
99+
ConditionId::from(self.next_condition_id())
94100
} else {
95101
parent_condition.condition_id
96102
};
97103

98-
self.decision_info.num_conditions += 1;
99-
let rhs_condition_id = ConditionId::from(self.decision_info.num_conditions);
104+
let rhs_condition_id = self.next_condition_id();
100105

101106
let (lhs, rhs) = match op {
102107
LogicalOp::And => {
@@ -147,13 +152,9 @@ impl BooleanDecisionCtx {
147152

148153
self.conditions.push(MCDCBranchSpan {
149154
span,
150-
condition_info: Some(condition_info),
151-
true_marker,
152-
false_marker,
153-
decision_depth: 0,
155+
condition_info,
156+
markers: MCDCBranchMarkers::Boolean(true_marker, false_marker),
154157
});
155-
// In case this decision had only one condition
156-
self.decision_info.num_conditions = self.decision_info.num_conditions.max(1);
157158
}
158159

159160
fn is_finished(&self) -> bool {
@@ -250,7 +251,6 @@ struct MCDCTargetInfo {
250251
impl MCDCTargetInfo {
251252
fn set_depth(&mut self, depth: u16) {
252253
self.decision.decision_depth = depth;
253-
self.conditions.iter_mut().for_each(|branch| branch.decision_depth = depth);
254254
}
255255
}
256256

@@ -298,7 +298,9 @@ impl MCDCInfoBuilder {
298298
}
299299

300300
fn append_normal_branches(&mut self, mut branches: Vec<MCDCBranchSpan>) {
301-
branches.iter_mut().for_each(|branch| branch.condition_info = None);
301+
branches
302+
.iter_mut()
303+
.for_each(|branch| branch.condition_info.condition_id = ConditionId::NONE);
302304
self.normal_branch_spans.extend(branches);
303305
}
304306

‎compiler/rustc_mir_transform/src/coverage/counters.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,12 @@ impl CoverageCounters {
101101
BcbCounter::Counter { id }
102102
}
103103

104-
fn make_expression(&mut self, lhs: BcbCounter, op: Op, rhs: BcbCounter) -> BcbCounter {
104+
pub(super) fn make_expression(
105+
&mut self,
106+
lhs: BcbCounter,
107+
op: Op,
108+
rhs: BcbCounter,
109+
) -> BcbCounter {
105110
let new_expr = BcbExpression { lhs, op, rhs };
106111
*self
107112
.expressions_memo
@@ -159,7 +164,11 @@ impl CoverageCounters {
159164
/// Variant of `make_expression` that makes `lhs` optional and assumes [`Op::Add`].
160165
///
161166
/// This is useful when using [`Iterator::fold`] to build an arbitrary-length sum.
162-
fn make_sum_expression(&mut self, lhs: Option<BcbCounter>, rhs: BcbCounter) -> BcbCounter {
167+
pub(super) fn make_sum_expression(
168+
&mut self,
169+
lhs: Option<BcbCounter>,
170+
rhs: BcbCounter,
171+
) -> BcbCounter {
163172
let Some(lhs) = lhs else { return rhs };
164173
self.make_expression(lhs, Op::Add, rhs)
165174
}

‎compiler/rustc_mir_transform/src/coverage/mappings.rs

Lines changed: 94 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ use std::collections::BTreeSet;
33
use rustc_data_structures::graph::DirectedGraph;
44
use rustc_index::bit_set::BitSet;
55
use rustc_index::IndexVec;
6-
use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, ConditionInfo, CoverageKind};
6+
use rustc_middle::mir::coverage::{
7+
BlockMarkerId, BranchSpan, ConditionInfo, CoverageKind, MCDCBranchMarkers,
8+
};
79
use rustc_middle::mir::{self, BasicBlock, StatementKind};
810
use rustc_middle::ty::TyCtxt;
911
use rustc_span::Span;
@@ -35,12 +37,15 @@ pub(super) struct BranchPair {
3537
#[derive(Debug)]
3638
pub(super) struct MCDCBranch {
3739
pub(super) span: Span,
38-
pub(super) true_bcb: BasicCoverageBlock,
39-
pub(super) false_bcb: BasicCoverageBlock,
40-
/// If `None`, this actually represents a normal branch mapping inserted
41-
/// for code that was too complex for MC/DC.
42-
pub(super) condition_info: Option<ConditionInfo>,
43-
pub(super) decision_depth: u16,
40+
pub(super) branch_bcbs: MCDCBranchBlocks,
41+
pub(super) condition_info: ConditionInfo,
42+
}
43+
44+
#[derive(Debug)]
45+
pub(super) enum MCDCBranchBlocks {
46+
Boolean(BasicCoverageBlock, BasicCoverageBlock),
47+
#[allow(dead_code)]
48+
PatternMatching,
4449
}
4550

4651
/// Associates an MC/DC decision with its join BCBs.
@@ -49,7 +54,6 @@ pub(super) struct MCDCDecision {
4954
pub(super) span: Span,
5055
pub(super) end_bcbs: BTreeSet<BasicCoverageBlock>,
5156
pub(super) bitmap_idx: u32,
52-
pub(super) num_conditions: u16,
5357
pub(super) decision_depth: u16,
5458
}
5559

@@ -58,8 +62,8 @@ pub(super) struct ExtractedMappings {
5862
pub(super) code_mappings: Vec<CodeMapping>,
5963
pub(super) branch_pairs: Vec<BranchPair>,
6064
pub(super) mcdc_bitmap_bytes: u32,
61-
pub(super) mcdc_branches: Vec<MCDCBranch>,
62-
pub(super) mcdc_decisions: Vec<MCDCDecision>,
65+
pub(super) mcdc_degraded_branches: Vec<MCDCBranch>,
66+
pub(super) mcdc_mappings: Vec<(MCDCDecision, Vec<MCDCBranch>)>,
6367
}
6468

6569
/// Extracts coverage-relevant spans from MIR, and associates them with
@@ -72,9 +76,8 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>(
7276
) -> ExtractedMappings {
7377
let mut code_mappings = vec![];
7478
let mut branch_pairs = vec![];
75-
let mut mcdc_bitmap_bytes = 0;
76-
let mut mcdc_branches = vec![];
77-
let mut mcdc_decisions = vec![];
79+
let mut mcdc_degraded_branches = vec![];
80+
let mut mcdc_mappings = vec![];
7881

7982
if hir_info.is_async_fn || tcx.sess.coverage_no_mir_spans() {
8083
// An async function desugars into a function that returns a future,
@@ -99,17 +102,16 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>(
99102
mir_body,
100103
hir_info.body_span,
101104
basic_coverage_blocks,
102-
&mut mcdc_bitmap_bytes,
103-
&mut mcdc_branches,
104-
&mut mcdc_decisions,
105+
&mut mcdc_degraded_branches,
106+
&mut mcdc_mappings,
105107
);
106108

107109
ExtractedMappings {
108110
code_mappings,
109111
branch_pairs,
110-
mcdc_bitmap_bytes,
111-
mcdc_branches,
112-
mcdc_decisions,
112+
mcdc_bitmap_bytes: 0, // Calculated in `coverage::create_mappings`
113+
mcdc_degraded_branches,
114+
mcdc_mappings,
113115
}
114116
}
115117

@@ -123,8 +125,8 @@ impl ExtractedMappings {
123125
code_mappings,
124126
branch_pairs,
125127
mcdc_bitmap_bytes: _,
126-
mcdc_branches,
127-
mcdc_decisions,
128+
mcdc_degraded_branches,
129+
mcdc_mappings,
128130
} = self;
129131

130132
// Identify which BCBs have one or more mappings.
@@ -140,16 +142,26 @@ impl ExtractedMappings {
140142
insert(true_bcb);
141143
insert(false_bcb);
142144
}
143-
for &MCDCBranch { true_bcb, false_bcb, .. } in mcdc_branches {
144-
insert(true_bcb);
145-
insert(false_bcb);
145+
for MCDCBranch { branch_bcbs, .. } in mcdc_degraded_branches
146+
.iter()
147+
.chain(mcdc_mappings.iter().map(|(_, branches)| branches.into_iter()).flatten())
148+
{
149+
match branch_bcbs {
150+
MCDCBranchBlocks::Boolean(true_bcb, false_bcb) => {
151+
insert(*true_bcb);
152+
insert(*false_bcb);
153+
}
154+
MCDCBranchBlocks::PatternMatching => {
155+
unimplemented!("mcdc for pattern matching is not implemented yet")
156+
}
157+
}
146158
}
147159

148160
// MC/DC decisions refer to BCBs, but don't require those BCBs to have counters.
149161
if bcbs_with_counter_mappings.is_empty() {
150162
debug_assert!(
151-
mcdc_decisions.is_empty(),
152-
"A function with no counter mappings shouldn't have any decisions: {mcdc_decisions:?}",
163+
mcdc_mappings.is_empty(),
164+
"A function with no counter mappings shouldn't have any decisions: {mcdc_mappings:?}",
153165
);
154166
}
155167

@@ -220,9 +232,8 @@ pub(super) fn extract_mcdc_mappings(
220232
mir_body: &mir::Body<'_>,
221233
body_span: Span,
222234
basic_coverage_blocks: &CoverageGraph,
223-
mcdc_bitmap_bytes: &mut u32,
224-
mcdc_branches: &mut impl Extend<MCDCBranch>,
225-
mcdc_decisions: &mut impl Extend<MCDCDecision>,
235+
mcdc_degraded_branches: &mut impl Extend<MCDCBranch>,
236+
mcdc_mappings: &mut impl Extend<(MCDCDecision, Vec<MCDCBranch>)>,
226237
) {
227238
let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { return };
228239

@@ -231,57 +242,63 @@ pub(super) fn extract_mcdc_mappings(
231242
let bcb_from_marker =
232243
|marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?);
233244

234-
let check_branch_bcb =
235-
|raw_span: Span, true_marker: BlockMarkerId, false_marker: BlockMarkerId| {
236-
// For now, ignore any branch span that was introduced by
237-
// expansion. This makes things like assert macros less noisy.
238-
if !raw_span.ctxt().outer_expn_data().is_root() {
239-
return None;
240-
}
241-
let (span, _) = unexpand_into_body_span_with_visible_macro(raw_span, body_span)?;
242-
243-
let true_bcb = bcb_from_marker(true_marker)?;
244-
let false_bcb = bcb_from_marker(false_marker)?;
245-
Some((span, true_bcb, false_bcb))
245+
let check_branch_bcb = |raw_span: Span,
246+
first_markers: &[BlockMarkerId],
247+
second_markers: &[BlockMarkerId]| {
248+
// For now, ignore any branch span that was introduced by
249+
// expansion. This makes things like assert macros less noisy.
250+
if !raw_span.ctxt().outer_expn_data().is_root() {
251+
return None;
252+
}
253+
let (span, _) = unexpand_into_body_span_with_visible_macro(raw_span, body_span)?;
254+
255+
let first_bcbs =
256+
first_markers.into_iter().copied().map(&bcb_from_marker).collect::<Option<Vec<_>>>()?;
257+
let second_bcbs = second_markers
258+
.into_iter()
259+
.copied()
260+
.map(&bcb_from_marker)
261+
.collect::<Option<Vec<_>>>()?;
262+
263+
Some((span, first_bcbs, second_bcbs))
264+
};
265+
266+
let extract_branch_mapping =
267+
|&mir::coverage::MCDCBranchSpan { span: raw_span, condition_info, ref markers }| {
268+
let (span, branch_bcbs) = match markers {
269+
MCDCBranchMarkers::Boolean(true_marker, false_marker) => {
270+
let (span, true_bcb, false_bcb) =
271+
check_branch_bcb(raw_span, &[*true_marker], &[*false_marker])?;
272+
(span, MCDCBranchBlocks::Boolean(true_bcb[0], false_bcb[0]))
273+
}
274+
MCDCBranchMarkers::PatternMatching => unimplemented!("not implemented yet"),
275+
};
276+
277+
Some(MCDCBranch { span, branch_bcbs, condition_info })
246278
};
247279

248-
mcdc_branches.extend(branch_info.mcdc_branch_spans.iter().filter_map(
249-
|&mir::coverage::MCDCBranchSpan {
250-
span: raw_span,
251-
condition_info,
252-
true_marker,
253-
false_marker,
254-
decision_depth,
255-
}| {
256-
let (span, true_bcb, false_bcb) =
257-
check_branch_bcb(raw_span, true_marker, false_marker)?;
258-
Some(MCDCBranch { span, true_bcb, false_bcb, condition_info, decision_depth })
259-
},
260-
));
261-
262-
mcdc_decisions.extend(branch_info.mcdc_decision_spans.iter().filter_map(
263-
|decision: &mir::coverage::MCDCDecisionSpan| {
264-
let (span, _) = unexpand_into_body_span_with_visible_macro(decision.span, body_span)?;
265-
266-
let end_bcbs = decision
267-
.end_markers
268-
.iter()
269-
.map(|&marker| bcb_from_marker(marker))
270-
.collect::<Option<_>>()?;
271-
272-
// Each decision containing N conditions needs 2^N bits of space in
273-
// the bitmap, rounded up to a whole number of bytes.
274-
// The decision's "bitmap index" points to its first byte in the bitmap.
275-
let bitmap_idx = *mcdc_bitmap_bytes;
276-
*mcdc_bitmap_bytes += (1_u32 << decision.num_conditions).div_ceil(8);
277-
278-
Some(MCDCDecision {
280+
mcdc_degraded_branches
281+
.extend(branch_info.mcdc_degraded_spans.iter().filter_map(extract_branch_mapping));
282+
283+
mcdc_mappings.extend(branch_info.mcdc_spans.iter().filter_map(|(decision, branches)| {
284+
let (span, _) = unexpand_into_body_span_with_visible_macro(decision.span, body_span)?;
285+
286+
let end_bcbs = decision
287+
.end_markers
288+
.iter()
289+
.map(|&marker| bcb_from_marker(marker))
290+
.collect::<Option<_>>()?;
291+
292+
let branch_mappings: Vec<_> =
293+
branches.into_iter().filter_map(extract_branch_mapping).collect();
294+
(branch_mappings.len() > 0).then_some((
295+
MCDCDecision {
279296
span,
280297
end_bcbs,
281-
bitmap_idx,
282-
num_conditions: decision.num_conditions as u16,
298+
bitmap_idx: 0, // Assigned in `coverage::create_mappings`
283299
decision_depth: decision.decision_depth,
284-
})
285-
},
286-
));
300+
},
301+
branch_mappings,
302+
))
303+
}));
287304
}

‎compiler/rustc_mir_transform/src/coverage/mod.rs

Lines changed: 127 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use rustc_span::{BytePos, Pos, RelativeBytePos, Span, Symbol};
2121

2222
use crate::coverage::counters::{CounterIncrementSite, CoverageCounters};
2323
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
24-
use crate::coverage::mappings::ExtractedMappings;
24+
use crate::coverage::mappings::{ExtractedMappings, MCDCBranchBlocks};
2525
use crate::MirPass;
2626

2727
/// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected
@@ -71,7 +71,7 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
7171

7272
////////////////////////////////////////////////////
7373
// Extract coverage spans and other mapping info from MIR.
74-
let extracted_mappings = mappings::extract_all_mapping_info_from_mir(
74+
let mut extracted_mappings = mappings::extract_all_mapping_info_from_mir(
7575
tcx,
7676
mir_body,
7777
&hir_info,
@@ -91,29 +91,29 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
9191
}
9292

9393
let bcb_has_counter_mappings = |bcb| bcbs_with_counter_mappings.contains(bcb);
94-
let coverage_counters =
94+
let mut coverage_counters =
9595
CoverageCounters::make_bcb_counters(&basic_coverage_blocks, bcb_has_counter_mappings);
9696

97-
let mappings = create_mappings(tcx, &hir_info, &extracted_mappings, &coverage_counters);
98-
if mappings.is_empty() {
99-
// No spans could be converted into valid mappings, so skip this function.
100-
debug!("no spans could be converted into valid mappings; skipping");
101-
return;
102-
}
103-
10497
inject_coverage_statements(
10598
mir_body,
10699
&basic_coverage_blocks,
107100
bcb_has_counter_mappings,
108-
&coverage_counters,
101+
&mut coverage_counters,
109102
);
110103

104+
let mappings = create_mappings(tcx, &hir_info, &mut extracted_mappings, &mut coverage_counters);
105+
if mappings.is_empty() {
106+
// No spans could be converted into valid mappings, so skip this function.
107+
debug!("no spans could be converted into valid mappings; skipping");
108+
return;
109+
}
110+
111111
inject_mcdc_statements(mir_body, &basic_coverage_blocks, &extracted_mappings);
112112

113113
let mcdc_num_condition_bitmaps = extracted_mappings
114-
.mcdc_decisions
114+
.mcdc_mappings
115115
.iter()
116-
.map(|&mappings::MCDCDecision { decision_depth, .. }| decision_depth)
116+
.map(|&(mappings::MCDCDecision { decision_depth, .. }, _)| decision_depth)
117117
.max()
118118
.map_or(0, |max| usize::from(max) + 1);
119119

@@ -135,8 +135,8 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
135135
fn create_mappings<'tcx>(
136136
tcx: TyCtxt<'tcx>,
137137
hir_info: &ExtractedHirInfo,
138-
extracted_mappings: &ExtractedMappings,
139-
coverage_counters: &CoverageCounters,
138+
extracted_mappings: &mut ExtractedMappings,
139+
coverage_counters: &mut CoverageCounters,
140140
) -> Vec<Mapping> {
141141
let source_map = tcx.sess.source_map();
142142
let body_span = hir_info.body_span;
@@ -160,9 +160,9 @@ fn create_mappings<'tcx>(
160160
let ExtractedMappings {
161161
code_mappings,
162162
branch_pairs,
163-
mcdc_bitmap_bytes: _,
164-
mcdc_branches,
165-
mcdc_decisions,
163+
mcdc_bitmap_bytes,
164+
mcdc_degraded_branches,
165+
mcdc_mappings,
166166
} = extracted_mappings;
167167
let mut mappings = Vec::new();
168168

@@ -184,27 +184,91 @@ fn create_mappings<'tcx>(
184184
Some(Mapping { kind, code_region })
185185
},
186186
));
187+
let mut counter_for_bcbs_group = |bcbs: &[BasicCoverageBlock]| {
188+
let counters = bcbs
189+
.into_iter()
190+
.copied()
191+
.map(|bcb| {
192+
coverage_counters.bcb_counter(bcb).expect("all BCBs with spans were given counters")
193+
})
194+
.collect::<Vec<_>>();
195+
counters
196+
.into_iter()
197+
.fold(None, |acc, val| Some(coverage_counters.make_sum_expression(acc, val)))
198+
.expect("counters must be non-empty")
199+
};
187200

188-
mappings.extend(mcdc_branches.iter().filter_map(
189-
|&mappings::MCDCBranch { span, true_bcb, false_bcb, condition_info, decision_depth: _ }| {
201+
let mut create_mcdc_branch_terms = |branch_bcbs: &MCDCBranchBlocks| match branch_bcbs {
202+
MCDCBranchBlocks::Boolean(true_bcb, false_bcb) => {
203+
let true_counter = counter_for_bcbs_group(&[*true_bcb]);
204+
let false_counter = counter_for_bcbs_group(&[*false_bcb]);
205+
(true_counter.as_term(), false_counter.as_term())
206+
}
207+
MCDCBranchBlocks::PatternMatching => {
208+
unimplemented!("mcdc for pattern matching is not implemented yet");
209+
}
210+
};
211+
// MCDC branch mappings are appended with their decisions in case decisions were ignored.
212+
mappings.extend(mcdc_degraded_branches.iter().filter_map(
213+
|&mappings::MCDCBranch { span, ref branch_bcbs, condition_info: _ }| {
190214
let code_region = region_for_span(span)?;
191-
let true_term = term_for_bcb(true_bcb);
192-
let false_term = term_for_bcb(false_bcb);
193-
let kind = match condition_info {
194-
Some(mcdc_params) => MappingKind::MCDCBranch { true_term, false_term, mcdc_params },
195-
None => MappingKind::Branch { true_term, false_term },
196-
};
197-
Some(Mapping { kind, code_region })
215+
let (true_term, false_term) = create_mcdc_branch_terms(branch_bcbs);
216+
Some(Mapping { kind: MappingKind::Branch { true_term, false_term }, code_region })
198217
},
199218
));
200219

201-
mappings.extend(mcdc_decisions.iter().filter_map(
202-
|&mappings::MCDCDecision { span, bitmap_idx, num_conditions, .. }| {
203-
let code_region = region_for_span(span)?;
204-
let kind = MappingKind::MCDCDecision(DecisionInfo { bitmap_idx, num_conditions });
205-
Some(Mapping { kind, code_region })
206-
},
207-
));
220+
let mut next_bitmap_idx = |num_conditions: u16| {
221+
let bitmap_idx = *mcdc_bitmap_bytes;
222+
// Each decision containing N conditions needs 2^N bits of space in
223+
// the bitmap, rounded up to a whole number of bytes.
224+
// The decision's "bitmap index" points to its first byte in the bitmap.
225+
*mcdc_bitmap_bytes += (1_u16 << num_conditions).div_ceil(8) as u32;
226+
bitmap_idx
227+
};
228+
229+
for (decision, branches) in mcdc_mappings {
230+
let num_conditions = branches.len() as u16;
231+
let conditions = branches
232+
.into_iter()
233+
.filter_map(|&mut mappings::MCDCBranch { span, ref branch_bcbs, condition_info }| {
234+
let code_region = region_for_span(span)?;
235+
let (true_term, false_term) = create_mcdc_branch_terms(branch_bcbs);
236+
Some(Mapping {
237+
kind: MappingKind::MCDCBranch {
238+
true_term,
239+
false_term,
240+
mcdc_params: condition_info,
241+
},
242+
code_region,
243+
})
244+
})
245+
.collect::<Vec<_>>();
246+
247+
if conditions.len() == num_conditions as usize
248+
&& let Some(code_region) = region_for_span(decision.span)
249+
{
250+
decision.bitmap_idx = next_bitmap_idx(num_conditions);
251+
let kind = MappingKind::MCDCDecision(DecisionInfo {
252+
bitmap_idx: decision.bitmap_idx,
253+
num_conditions,
254+
});
255+
mappings.extend(
256+
std::iter::once(Mapping { kind, code_region }).chain(conditions.into_iter()),
257+
);
258+
} else {
259+
mappings.extend(conditions.into_iter().map(|mapping| {
260+
let MappingKind::MCDCBranch { true_term, false_term, mcdc_params: _ } =
261+
mapping.kind
262+
else {
263+
unreachable!("all mappings here are MCDCBranch as shown above");
264+
};
265+
Mapping {
266+
kind: MappingKind::Branch { true_term, false_term },
267+
code_region: mapping.code_region,
268+
}
269+
}))
270+
}
271+
}
208272

209273
mappings
210274
}
@@ -268,43 +332,41 @@ fn inject_mcdc_statements<'tcx>(
268332
basic_coverage_blocks: &CoverageGraph,
269333
extracted_mappings: &ExtractedMappings,
270334
) {
271-
// Inject test vector update first because `inject_statement` always insert new statement at head.
272-
for &mappings::MCDCDecision {
273-
span: _,
274-
ref end_bcbs,
275-
bitmap_idx,
276-
num_conditions: _,
277-
decision_depth,
278-
} in &extracted_mappings.mcdc_decisions
279-
{
280-
for end in end_bcbs {
281-
let end_bb = basic_coverage_blocks[*end].leader_bb();
335+
for (decision, conditions) in &extracted_mappings.mcdc_mappings {
336+
// Inject test vector update first because `inject_statement` always insert new statement at head.
337+
for &end in &decision.end_bcbs {
338+
let end_bb = basic_coverage_blocks[end].leader_bb();
282339
inject_statement(
283340
mir_body,
284-
CoverageKind::TestVectorBitmapUpdate { bitmap_idx, decision_depth },
341+
CoverageKind::TestVectorBitmapUpdate {
342+
bitmap_idx: decision.bitmap_idx,
343+
decision_depth: decision.decision_depth,
344+
},
285345
end_bb,
286346
);
287347
}
288-
}
289-
290-
for &mappings::MCDCBranch { span: _, true_bcb, false_bcb, condition_info, decision_depth } in
291-
&extracted_mappings.mcdc_branches
292-
{
293-
let Some(condition_info) = condition_info else { continue };
294-
let id = condition_info.condition_id;
295348

296-
let true_bb = basic_coverage_blocks[true_bcb].leader_bb();
297-
inject_statement(
298-
mir_body,
299-
CoverageKind::CondBitmapUpdate { id, value: true, decision_depth },
300-
true_bb,
301-
);
302-
let false_bb = basic_coverage_blocks[false_bcb].leader_bb();
303-
inject_statement(
304-
mir_body,
305-
CoverageKind::CondBitmapUpdate { id, value: false, decision_depth },
306-
false_bb,
307-
);
349+
for &mappings::MCDCBranch { span: _, ref branch_bcbs, condition_info } in conditions {
350+
let id = condition_info.condition_id;
351+
let true_bcbs = match branch_bcbs {
352+
MCDCBranchBlocks::Boolean(true_bcb, _) => &[*true_bcb],
353+
MCDCBranchBlocks::PatternMatching => {
354+
unimplemented!("mcdc for pattern matching is not implemented yet")
355+
}
356+
};
357+
for &bcb in true_bcbs {
358+
let bb = basic_coverage_blocks[bcb].leader_bb();
359+
inject_statement(
360+
mir_body,
361+
CoverageKind::CondBitmapUpdate {
362+
id,
363+
value: true,
364+
decision_depth: decision.decision_depth,
365+
},
366+
bb,
367+
);
368+
}
369+
}
308370
}
309371
}
310372

0 commit comments

Comments
 (0)
Please sign in to comment.