2024-04-29 12:25:09 +00:00
|
|
|
use std::collections::BTreeSet;
|
|
|
|
|
|
|
|
use rustc_data_structures::graph::DirectedGraph;
|
|
|
|
use rustc_index::bit_set::BitSet;
|
2024-05-02 02:30:07 +00:00
|
|
|
use rustc_index::IndexVec;
|
2024-07-04 13:52:49 +00:00
|
|
|
use rustc_middle::mir::coverage::{
|
|
|
|
BlockMarkerId, BranchSpan, ConditionInfo, CoverageInfoHi, CoverageKind,
|
|
|
|
};
|
2024-04-29 12:25:09 +00:00
|
|
|
use rustc_middle::mir::{self, BasicBlock, StatementKind};
|
2024-06-17 10:09:45 +00:00
|
|
|
use rustc_middle::ty::TyCtxt;
|
2024-04-29 12:25:09 +00:00
|
|
|
use rustc_span::Span;
|
|
|
|
|
|
|
|
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
|
2024-06-30 07:36:16 +00:00
|
|
|
use crate::coverage::spans::extract_refined_covspans;
|
2024-06-30 08:20:45 +00:00
|
|
|
use crate::coverage::unexpand::unexpand_into_body_span;
|
2024-04-29 12:25:09 +00:00
|
|
|
use crate::coverage::ExtractedHirInfo;
|
|
|
|
|
2024-05-02 04:18:22 +00:00
|
|
|
/// Associates an ordinary executable code span with its corresponding BCB.
|
2024-04-29 12:25:09 +00:00
|
|
|
#[derive(Debug)]
|
2024-05-02 04:18:22 +00:00
|
|
|
pub(super) struct CodeMapping {
|
2024-04-29 12:25:09 +00:00
|
|
|
pub(super) span: Span,
|
2024-05-02 04:18:22 +00:00
|
|
|
pub(super) bcb: BasicCoverageBlock,
|
2024-04-29 12:25:09 +00:00
|
|
|
}
|
|
|
|
|
2024-05-02 04:18:22 +00:00
|
|
|
/// This is separate from [`MCDCBranch`] to help prepare for larger changes
|
2024-04-29 12:25:09 +00:00
|
|
|
/// that will be needed for improved branch coverage in the future.
|
|
|
|
/// (See <https://github.com/rust-lang/rust/pull/124217>.)
|
|
|
|
#[derive(Debug)]
|
2024-05-02 04:39:24 +00:00
|
|
|
pub(super) struct BranchPair {
|
2024-04-29 12:25:09 +00:00
|
|
|
pub(super) span: Span,
|
|
|
|
pub(super) true_bcb: BasicCoverageBlock,
|
|
|
|
pub(super) false_bcb: BasicCoverageBlock,
|
|
|
|
}
|
|
|
|
|
2024-05-02 02:46:23 +00:00
|
|
|
/// Associates an MC/DC branch span with condition info besides fields for normal branch.
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub(super) struct MCDCBranch {
|
|
|
|
pub(super) span: Span,
|
|
|
|
pub(super) true_bcb: BasicCoverageBlock,
|
|
|
|
pub(super) false_bcb: BasicCoverageBlock,
|
|
|
|
/// If `None`, this actually represents a normal branch mapping inserted
|
|
|
|
/// for code that was too complex for MC/DC.
|
|
|
|
pub(super) condition_info: Option<ConditionInfo>,
|
|
|
|
pub(super) decision_depth: u16,
|
|
|
|
}
|
|
|
|
|
2024-05-02 02:30:07 +00:00
|
|
|
/// Associates an MC/DC decision with its join BCBs.
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub(super) struct MCDCDecision {
|
|
|
|
pub(super) span: Span,
|
|
|
|
pub(super) end_bcbs: BTreeSet<BasicCoverageBlock>,
|
|
|
|
pub(super) bitmap_idx: u32,
|
2024-05-30 03:16:07 +00:00
|
|
|
pub(super) num_conditions: u16,
|
2024-05-02 02:30:07 +00:00
|
|
|
pub(super) decision_depth: u16,
|
|
|
|
}
|
|
|
|
|
2024-05-02 10:24:23 +00:00
|
|
|
#[derive(Default)]
|
2024-05-02 07:05:32 +00:00
|
|
|
pub(super) struct ExtractedMappings {
|
2024-05-02 04:18:22 +00:00
|
|
|
pub(super) code_mappings: Vec<CodeMapping>,
|
2024-05-02 04:39:24 +00:00
|
|
|
pub(super) branch_pairs: Vec<BranchPair>,
|
2024-05-02 09:53:03 +00:00
|
|
|
pub(super) mcdc_bitmap_bytes: u32,
|
2024-05-02 02:46:23 +00:00
|
|
|
pub(super) mcdc_branches: Vec<MCDCBranch>,
|
2024-05-02 02:30:07 +00:00
|
|
|
pub(super) mcdc_decisions: Vec<MCDCDecision>,
|
2024-04-29 12:25:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Extracts coverage-relevant spans from MIR, and associates them with
|
|
|
|
/// their corresponding BCBs.
|
2024-06-17 10:09:45 +00:00
|
|
|
pub(super) fn extract_all_mapping_info_from_mir<'tcx>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
mir_body: &mir::Body<'tcx>,
|
2024-04-29 12:25:09 +00:00
|
|
|
hir_info: &ExtractedHirInfo,
|
|
|
|
basic_coverage_blocks: &CoverageGraph,
|
2024-05-02 07:05:32 +00:00
|
|
|
) -> ExtractedMappings {
|
2024-06-17 10:09:45 +00:00
|
|
|
let mut code_mappings = vec![];
|
|
|
|
let mut branch_pairs = vec![];
|
|
|
|
let mut mcdc_bitmap_bytes = 0;
|
|
|
|
let mut mcdc_branches = vec![];
|
|
|
|
let mut mcdc_decisions = vec![];
|
|
|
|
|
|
|
|
if hir_info.is_async_fn || tcx.sess.coverage_no_mir_spans() {
|
2024-04-29 12:25:09 +00:00
|
|
|
// An async function desugars into a function that returns a future,
|
|
|
|
// with the user code wrapped in a closure. Any spans in the desugared
|
|
|
|
// outer function will be unhelpful, so just keep the signature span
|
|
|
|
// and ignore all of the spans in the MIR body.
|
2024-06-17 10:09:45 +00:00
|
|
|
//
|
|
|
|
// When debugging flag `-Zcoverage-options=no-mir-spans` is set, we need
|
|
|
|
// to give the same treatment to _all_ functions, because `llvm-cov`
|
|
|
|
// seems to ignore functions that don't have any ordinary code spans.
|
2024-04-29 12:25:09 +00:00
|
|
|
if let Some(span) = hir_info.fn_sig_span_extended {
|
2024-06-17 10:09:45 +00:00
|
|
|
code_mappings.push(CodeMapping { span, bcb: START_BCB });
|
2024-04-29 12:25:09 +00:00
|
|
|
}
|
2024-06-17 10:09:45 +00:00
|
|
|
} else {
|
|
|
|
// Extract coverage spans from MIR statements/terminators as normal.
|
|
|
|
extract_refined_covspans(mir_body, hir_info, basic_coverage_blocks, &mut code_mappings);
|
2024-04-29 12:25:09 +00:00
|
|
|
}
|
|
|
|
|
2024-05-02 10:24:23 +00:00
|
|
|
branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, basic_coverage_blocks));
|
|
|
|
|
|
|
|
extract_mcdc_mappings(
|
|
|
|
mir_body,
|
|
|
|
hir_info.body_span,
|
|
|
|
basic_coverage_blocks,
|
2024-05-02 09:53:03 +00:00
|
|
|
&mut mcdc_bitmap_bytes,
|
2024-05-02 10:24:23 +00:00
|
|
|
&mut mcdc_branches,
|
|
|
|
&mut mcdc_decisions,
|
|
|
|
);
|
|
|
|
|
2024-05-02 07:05:32 +00:00
|
|
|
ExtractedMappings {
|
|
|
|
code_mappings,
|
|
|
|
branch_pairs,
|
|
|
|
mcdc_bitmap_bytes,
|
|
|
|
mcdc_branches,
|
|
|
|
mcdc_decisions,
|
|
|
|
}
|
2024-05-02 06:28:49 +00:00
|
|
|
}
|
|
|
|
|
2024-05-02 07:05:32 +00:00
|
|
|
impl ExtractedMappings {
|
2024-05-02 06:28:49 +00:00
|
|
|
pub(super) fn all_bcbs_with_counter_mappings(
|
|
|
|
&self,
|
|
|
|
basic_coverage_blocks: &CoverageGraph, // Only used for allocating a correctly-sized set
|
|
|
|
) -> BitSet<BasicCoverageBlock> {
|
|
|
|
// Fully destructure self to make sure we don't miss any fields that have mappings.
|
|
|
|
let Self {
|
|
|
|
code_mappings,
|
|
|
|
branch_pairs,
|
2024-05-02 09:53:03 +00:00
|
|
|
mcdc_bitmap_bytes: _,
|
2024-05-02 06:28:49 +00:00
|
|
|
mcdc_branches,
|
|
|
|
mcdc_decisions,
|
|
|
|
} = self;
|
|
|
|
|
|
|
|
// Identify which BCBs have one or more mappings.
|
|
|
|
let mut bcbs_with_counter_mappings = BitSet::new_empty(basic_coverage_blocks.num_nodes());
|
|
|
|
let mut insert = |bcb| {
|
|
|
|
bcbs_with_counter_mappings.insert(bcb);
|
|
|
|
};
|
|
|
|
|
|
|
|
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 &MCDCBranch { true_bcb, false_bcb, .. } in mcdc_branches {
|
|
|
|
insert(true_bcb);
|
|
|
|
insert(false_bcb);
|
|
|
|
}
|
|
|
|
|
|
|
|
// MC/DC decisions refer to BCBs, but don't require those BCBs to have counters.
|
|
|
|
if bcbs_with_counter_mappings.is_empty() {
|
|
|
|
debug_assert!(
|
|
|
|
mcdc_decisions.is_empty(),
|
|
|
|
"A function with no counter mappings shouldn't have any decisions: {mcdc_decisions:?}",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
bcbs_with_counter_mappings
|
|
|
|
}
|
2024-04-29 12:25:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn resolve_block_markers(
|
2024-07-04 13:52:49 +00:00
|
|
|
coverage_info_hi: &CoverageInfoHi,
|
2024-04-29 12:25:09 +00:00
|
|
|
mir_body: &mir::Body<'_>,
|
|
|
|
) -> IndexVec<BlockMarkerId, Option<BasicBlock>> {
|
|
|
|
let mut block_markers = IndexVec::<BlockMarkerId, Option<BasicBlock>>::from_elem_n(
|
|
|
|
None,
|
2024-07-04 13:52:49 +00:00
|
|
|
coverage_info_hi.num_block_markers,
|
2024-04-29 12:25:09 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// Fill out the mapping from block marker IDs to their enclosing blocks.
|
|
|
|
for (bb, data) in mir_body.basic_blocks.iter_enumerated() {
|
|
|
|
for statement in &data.statements {
|
|
|
|
if let StatementKind::Coverage(CoverageKind::BlockMarker { id }) = statement.kind {
|
|
|
|
block_markers[id] = Some(bb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
block_markers
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: There is currently a lot of redundancy between
|
|
|
|
// `extract_branch_pairs` 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(
|
|
|
|
mir_body: &mir::Body<'_>,
|
|
|
|
hir_info: &ExtractedHirInfo,
|
|
|
|
basic_coverage_blocks: &CoverageGraph,
|
2024-05-02 04:39:24 +00:00
|
|
|
) -> Vec<BranchPair> {
|
2024-07-04 13:52:49 +00:00
|
|
|
let Some(coverage_info_hi) = mir_body.coverage_info_hi.as_deref() else { return vec![] };
|
2024-04-29 12:25:09 +00:00
|
|
|
|
2024-07-04 13:52:49 +00:00
|
|
|
let block_markers = resolve_block_markers(coverage_info_hi, mir_body);
|
2024-04-29 12:25:09 +00:00
|
|
|
|
2024-07-04 13:52:49 +00:00
|
|
|
coverage_info_hi
|
2024-04-29 12:25:09 +00:00
|
|
|
.branch_spans
|
|
|
|
.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;
|
|
|
|
}
|
2024-06-30 08:20:45 +00:00
|
|
|
let span = unexpand_into_body_span(raw_span, hir_info.body_span)?;
|
2024-04-29 12:25:09 +00:00
|
|
|
|
|
|
|
let bcb_from_marker =
|
|
|
|
|marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?);
|
|
|
|
|
|
|
|
let true_bcb = bcb_from_marker(true_marker)?;
|
|
|
|
let false_bcb = bcb_from_marker(false_marker)?;
|
|
|
|
|
2024-05-02 04:39:24 +00:00
|
|
|
Some(BranchPair { span, true_bcb, false_bcb })
|
2024-04-29 12:25:09 +00:00
|
|
|
})
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn extract_mcdc_mappings(
|
|
|
|
mir_body: &mir::Body<'_>,
|
|
|
|
body_span: Span,
|
|
|
|
basic_coverage_blocks: &CoverageGraph,
|
2024-05-02 09:53:03 +00:00
|
|
|
mcdc_bitmap_bytes: &mut u32,
|
2024-05-02 02:46:23 +00:00
|
|
|
mcdc_branches: &mut impl Extend<MCDCBranch>,
|
2024-05-02 02:30:07 +00:00
|
|
|
mcdc_decisions: &mut impl Extend<MCDCDecision>,
|
2024-05-02 02:46:23 +00:00
|
|
|
) {
|
2024-07-04 13:52:49 +00:00
|
|
|
let Some(coverage_info_hi) = mir_body.coverage_info_hi.as_deref() else { return };
|
2024-04-29 12:25:09 +00:00
|
|
|
|
2024-07-04 13:52:49 +00:00
|
|
|
let block_markers = resolve_block_markers(coverage_info_hi, mir_body);
|
2024-04-29 12:25:09 +00:00
|
|
|
|
|
|
|
let bcb_from_marker =
|
|
|
|
|marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?);
|
|
|
|
|
|
|
|
let check_branch_bcb =
|
|
|
|
|raw_span: Span, true_marker: BlockMarkerId, false_marker: BlockMarkerId| {
|
|
|
|
// 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;
|
|
|
|
}
|
2024-06-30 08:20:45 +00:00
|
|
|
let span = unexpand_into_body_span(raw_span, body_span)?;
|
2024-04-29 12:25:09 +00:00
|
|
|
|
|
|
|
let true_bcb = bcb_from_marker(true_marker)?;
|
|
|
|
let false_bcb = bcb_from_marker(false_marker)?;
|
|
|
|
Some((span, true_bcb, false_bcb))
|
|
|
|
};
|
|
|
|
|
2024-07-04 13:52:49 +00:00
|
|
|
mcdc_branches.extend(coverage_info_hi.mcdc_branch_spans.iter().filter_map(
|
2024-05-02 02:46:23 +00:00
|
|
|
|&mir::coverage::MCDCBranchSpan {
|
|
|
|
span: raw_span,
|
|
|
|
condition_info,
|
|
|
|
true_marker,
|
|
|
|
false_marker,
|
|
|
|
decision_depth,
|
|
|
|
}| {
|
|
|
|
let (span, true_bcb, false_bcb) =
|
|
|
|
check_branch_bcb(raw_span, true_marker, false_marker)?;
|
|
|
|
Some(MCDCBranch { span, true_bcb, false_bcb, condition_info, decision_depth })
|
|
|
|
},
|
|
|
|
));
|
2024-04-29 12:25:09 +00:00
|
|
|
|
2024-07-04 13:52:49 +00:00
|
|
|
mcdc_decisions.extend(coverage_info_hi.mcdc_decision_spans.iter().filter_map(
|
2024-05-02 02:30:07 +00:00
|
|
|
|decision: &mir::coverage::MCDCDecisionSpan| {
|
2024-06-30 08:20:45 +00:00
|
|
|
let span = unexpand_into_body_span(decision.span, body_span)?;
|
2024-04-29 12:25:09 +00:00
|
|
|
|
2024-05-02 02:30:07 +00:00
|
|
|
let end_bcbs = decision
|
|
|
|
.end_markers
|
|
|
|
.iter()
|
|
|
|
.map(|&marker| bcb_from_marker(marker))
|
|
|
|
.collect::<Option<_>>()?;
|
2024-04-29 12:25:09 +00:00
|
|
|
|
2024-05-02 09:53:03 +00:00
|
|
|
// Each decision containing N conditions needs 2^N bits of space in
|
|
|
|
// the bitmap, rounded up to a whole number of bytes.
|
|
|
|
// The decision's "bitmap index" points to its first byte in the bitmap.
|
|
|
|
let bitmap_idx = *mcdc_bitmap_bytes;
|
2024-05-30 03:16:07 +00:00
|
|
|
*mcdc_bitmap_bytes += (1_u32 << decision.num_conditions).div_ceil(8);
|
2024-04-29 12:25:09 +00:00
|
|
|
|
2024-05-02 02:30:07 +00:00
|
|
|
Some(MCDCDecision {
|
|
|
|
span,
|
2024-04-29 12:25:09 +00:00
|
|
|
end_bcbs,
|
|
|
|
bitmap_idx,
|
2024-05-30 03:16:07 +00:00
|
|
|
num_conditions: decision.num_conditions as u16,
|
2024-04-29 12:25:09 +00:00
|
|
|
decision_depth: decision.decision_depth,
|
2024-05-02 02:30:07 +00:00
|
|
|
})
|
|
|
|
},
|
|
|
|
));
|
2024-04-29 12:25:09 +00:00
|
|
|
}
|