Skip to content

Commit

Permalink
coverage: Store BCB node IDs in mappings, and resolve them in codegen
Browse files Browse the repository at this point in the history
Even though the coverage graph itself is no longer available during codegen,
its nodes can still be used as opaque IDs.
  • Loading branch information
Zalathar committed Feb 6, 2025
1 parent 5958825 commit ee7dc06
Show file tree
Hide file tree
Showing 12 changed files with 91 additions and 103 deletions.
29 changes: 17 additions & 12 deletions compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ use rustc_codegen_ssa::traits::{
BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods,
};
use rustc_middle::mir::coverage::{
CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping, MappingKind, Op,
BasicCoverageBlock, CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping,
MappingKind, Op,
};
use rustc_middle::ty::{Instance, TyCtxt};
use rustc_span::Span;
Expand Down Expand Up @@ -140,7 +141,12 @@ fn fill_region_tables<'tcx>(
for &Mapping { ref kind, span } in &fn_cov_info.mappings {
// If the mapping refers to counters/expressions that were removed by
// MIR opts, replace those occurrences with zero.
let kind = kind.map_terms(|term| if is_zero_term(term) { CovTerm::Zero } else { term });
let counter_for_bcb = |bcb: BasicCoverageBlock| -> ffi::Counter {
let term =
fn_cov_info.term_for_bcb[bcb].expect("every BCB in a mapping was given a term");
let term = if is_zero_term(term) { CovTerm::Zero } else { term };
ffi::Counter::from_term(term)
};

// Convert the `Span` into coordinates that we can pass to LLVM, or
// discard the span if conversion fails. In rare, cases _all_ of a
Expand All @@ -154,23 +160,22 @@ fn fill_region_tables<'tcx>(
continue;
}

match kind {
MappingKind::Code(term) => {
code_regions
.push(ffi::CodeRegion { cov_span, counter: ffi::Counter::from_term(term) });
match *kind {
MappingKind::Code { bcb } => {
code_regions.push(ffi::CodeRegion { cov_span, counter: counter_for_bcb(bcb) });
}
MappingKind::Branch { true_term, false_term } => {
MappingKind::Branch { true_bcb, false_bcb } => {
branch_regions.push(ffi::BranchRegion {
cov_span,
true_counter: ffi::Counter::from_term(true_term),
false_counter: ffi::Counter::from_term(false_term),
true_counter: counter_for_bcb(true_bcb),
false_counter: counter_for_bcb(false_bcb),
});
}
MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => {
MappingKind::MCDCBranch { true_bcb, false_bcb, mcdc_params } => {
mcdc_branch_regions.push(ffi::MCDCBranchRegion {
cov_span,
true_counter: ffi::Counter::from_term(true_term),
false_counter: ffi::Counter::from_term(false_term),
true_counter: counter_for_bcb(true_bcb),
false_counter: counter_for_bcb(false_bcb),
mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params),
});
}
Expand Down
51 changes: 28 additions & 23 deletions compiler/rustc_middle/src/mir/coverage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,34 +179,19 @@ pub struct Expression {
#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
pub enum MappingKind {
/// Associates a normal region of code with a counter/expression/zero.
Code(CovTerm),
Code { bcb: BasicCoverageBlock },
/// Associates a branch region with separate counters for true and false.
Branch { true_term: CovTerm, false_term: CovTerm },
Branch { true_bcb: BasicCoverageBlock, false_bcb: BasicCoverageBlock },
/// Associates a branch region with separate counters for true and false.
MCDCBranch { true_term: CovTerm, false_term: CovTerm, mcdc_params: ConditionInfo },
MCDCBranch {
true_bcb: BasicCoverageBlock,
false_bcb: BasicCoverageBlock,
mcdc_params: ConditionInfo,
},
/// Associates a decision region with a bitmap and number of conditions.
MCDCDecision(DecisionInfo),
}

impl MappingKind {
/// Returns a copy of this mapping kind, in which all coverage terms have
/// been replaced with ones returned by the given function.
pub fn map_terms(&self, map_fn: impl Fn(CovTerm) -> CovTerm) -> Self {
match *self {
Self::Code(term) => Self::Code(map_fn(term)),
Self::Branch { true_term, false_term } => {
Self::Branch { true_term: map_fn(true_term), false_term: map_fn(false_term) }
}
Self::MCDCBranch { true_term, false_term, mcdc_params } => Self::MCDCBranch {
true_term: map_fn(true_term),
false_term: map_fn(false_term),
mcdc_params,
},
Self::MCDCDecision(param) => Self::MCDCDecision(param),
}
}
}

#[derive(Clone, Debug)]
#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
pub struct Mapping {
Expand All @@ -222,10 +207,14 @@ pub struct Mapping {
pub struct FunctionCoverageInfo {
pub function_source_hash: u64,
pub body_span: Span,

pub num_counters: usize,
pub mcdc_bitmap_bits: usize,
pub expressions: IndexVec<ExpressionId, Expression>,

pub mappings: Vec<Mapping>,
pub term_for_bcb: IndexVec<BasicCoverageBlock, Option<CovTerm>>,

pub mcdc_bitmap_bits: usize,
/// The depth of the deepest decision is used to know how many
/// temp condbitmaps should be allocated for the function.
pub mcdc_num_condition_bitmaps: usize,
Expand Down Expand Up @@ -329,3 +318,19 @@ impl CoverageIdsInfo {
}
}
}

rustc_index::newtype_index! {
/// During the `InstrumentCoverage` MIR pass, a BCB is a node in the
/// "coverage graph", which is a refinement of the MIR control-flow graph
/// that merges or omits some blocks that aren't relevant to coverage.
///
/// After that pass is complete, the coverage graph no longer exists, so a
/// BCB is effectively an opaque ID.
#[derive(HashStable)]
#[encodable]
#[orderable]
#[debug_format = "bcb{}"]
pub struct BasicCoverageBlock {
const START_BCB = 0;
}
}
6 changes: 1 addition & 5 deletions compiler/rustc_mir_transform/src/coverage/counters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ pub(super) struct CoverageCounters {
next_counter_id: CounterId,

/// Coverage counters/expressions that are associated with individual BCBs.
node_counters: IndexVec<BasicCoverageBlock, Option<CovTerm>>,
pub(crate) node_counters: IndexVec<BasicCoverageBlock, Option<CovTerm>>,

/// Table of expression data, associating each expression ID with its
/// corresponding operator (+ or -) and its LHS/RHS operands.
Expand Down Expand Up @@ -203,10 +203,6 @@ impl CoverageCounters {
counter
}

pub(super) fn term_for_bcb(&self, bcb: BasicCoverageBlock) -> Option<CovTerm> {
self.node_counters[bcb]
}

/// Returns an iterator over all the nodes in the coverage graph that
/// should have a counter-increment statement injected into MIR, along with
/// each site's corresponding counter ID.
Expand Down
10 changes: 1 addition & 9 deletions compiler/rustc_mir_transform/src/coverage/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use rustc_data_structures::graph::dominators::Dominators;
use rustc_data_structures::graph::{self, DirectedGraph, StartNode};
use rustc_index::IndexVec;
use rustc_index::bit_set::DenseBitSet;
pub(crate) use rustc_middle::mir::coverage::{BasicCoverageBlock, START_BCB};
use rustc_middle::mir::{self, BasicBlock, Terminator, TerminatorKind};
use tracing::debug;

Expand Down Expand Up @@ -269,15 +270,6 @@ impl graph::Predecessors for CoverageGraph {
}
}

rustc_index::newtype_index! {
/// A node in the control-flow graph of CoverageGraph.
#[orderable]
#[debug_format = "bcb{}"]
pub(crate) struct BasicCoverageBlock {
const START_BCB = 0;
}
}

/// `BasicCoverageBlockData` holds the data indexed by a `BasicCoverageBlock`.
///
/// A `BasicCoverageBlock` (BCB) represents the maximal-length sequence of MIR `BasicBlock`s without
Expand Down
46 changes: 17 additions & 29 deletions compiler/rustc_mir_transform/src/coverage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,13 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:

let coverage_counters = counters::make_bcb_counters(&graph, &bcbs_with_counter_mappings);

let mappings = create_mappings(&extracted_mappings, &coverage_counters);
let mappings = create_mappings(&extracted_mappings);
if mappings.is_empty() {
// No spans could be converted into valid mappings, so skip this function.
debug!("no spans could be converted into valid mappings; skipping");
return;
}
let term_for_bcb = coverage_counters.node_counters.clone();

inject_coverage_statements(mir_body, &graph, &extracted_mappings, &coverage_counters);

Expand All @@ -116,26 +117,24 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
function_source_hash: hir_info.function_source_hash,
body_span: hir_info.body_span,

num_counters: coverage_counters.num_counters(),
mcdc_bitmap_bits: extracted_mappings.mcdc_bitmap_bits,
expressions: coverage_counters.into_expressions(),

mappings,
term_for_bcb,

mcdc_bitmap_bits: extracted_mappings.mcdc_bitmap_bits,
mcdc_num_condition_bitmaps,
}));
}

/// For each coverage span extracted from MIR, create a corresponding
/// mapping.
/// For each coverage span extracted from MIR, create a corresponding mapping.
///
/// Precondition: All BCBs corresponding to those spans have been given
/// coverage counters.
fn create_mappings(
extracted_mappings: &ExtractedMappings,
coverage_counters: &CoverageCounters,
) -> Vec<Mapping> {
let term_for_bcb =
|bcb| coverage_counters.term_for_bcb(bcb).expect("all BCBs with spans were given counters");

/// FIXME(Zalathar): This used to be where BCBs in the extracted mappings were
/// resolved to a `CovTerm`. But that is now handled elsewhere, so this
/// function can potentially be simplified even further.
fn create_mappings(extracted_mappings: &ExtractedMappings) -> Vec<Mapping> {
// Fully destructure the mappings struct to make sure we don't miss any kinds.
let ExtractedMappings {
num_bcbs: _,
Expand All @@ -150,23 +149,18 @@ fn create_mappings(
mappings.extend(code_mappings.iter().map(
// Ordinary code mappings are the simplest kind.
|&mappings::CodeMapping { span, bcb }| {
let kind = MappingKind::Code(term_for_bcb(bcb));
let kind = MappingKind::Code { bcb };
Mapping { kind, span }
},
));

mappings.extend(branch_pairs.iter().map(
|&mappings::BranchPair { span, true_bcb, false_bcb }| {
let true_term = term_for_bcb(true_bcb);
let false_term = term_for_bcb(false_bcb);
let kind = MappingKind::Branch { true_term, false_term };
let kind = MappingKind::Branch { true_bcb, false_bcb };
Mapping { kind, span }
},
));

let term_for_bcb =
|bcb| coverage_counters.term_for_bcb(bcb).expect("all BCBs with spans were given counters");

// MCDC branch mappings are appended with their decisions in case decisions were ignored.
mappings.extend(mcdc_degraded_branches.iter().map(
|&mappings::MCDCBranch {
Expand All @@ -176,11 +170,7 @@ fn create_mappings(
condition_info: _,
true_index: _,
false_index: _,
}| {
let true_term = term_for_bcb(true_bcb);
let false_term = term_for_bcb(false_bcb);
Mapping { kind: MappingKind::Branch { true_term, false_term }, span }
},
}| { Mapping { kind: MappingKind::Branch { true_bcb, false_bcb }, span } },
));

for (decision, branches) in mcdc_mappings {
Expand All @@ -201,12 +191,10 @@ fn create_mappings(
true_index: _,
false_index: _,
}| {
let true_term = term_for_bcb(true_bcb);
let false_term = term_for_bcb(false_bcb);
Mapping {
kind: MappingKind::MCDCBranch {
true_term,
false_term,
true_bcb,
false_bcb,
mcdc_params: condition_info,
},
span,
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_mir_transform/src/coverage/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ fn coverage_ids_info<'tcx>(
// to any particular point in the control-flow graph.
// (Keep this in sync with the injection of `ExpressionUsed`
// statements in the `InstrumentCoverage` MIR pass.)
if let MappingKind::Code(CovTerm::Expression(id)) = mapping.kind {
if let MappingKind::Code { bcb } = mapping.kind
&& let Some(CovTerm::Expression(id)) = fn_cov_info.term_for_bcb[bcb]
{
expressions_seen.remove(id);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@
+ coverage ExpressionId(0) => Expression { lhs: Counter(1), op: Add, rhs: Counter(2) };
+ coverage ExpressionId(1) => Expression { lhs: Expression(0), op: Add, rhs: Counter(3) };
+ coverage ExpressionId(2) => Expression { lhs: Counter(0), op: Subtract, rhs: Expression(1) };
+ coverage Code(Counter(0)) => $DIR/branch_match_arms.rs:14:1: 15:21 (#0);
+ coverage Code(Counter(1)) => $DIR/branch_match_arms.rs:16:17: 16:33 (#0);
+ coverage Code(Counter(2)) => $DIR/branch_match_arms.rs:17:17: 17:33 (#0);
+ coverage Code(Counter(3)) => $DIR/branch_match_arms.rs:18:17: 18:33 (#0);
+ coverage Code(Expression(2)) => $DIR/branch_match_arms.rs:19:17: 19:33 (#0);
+ coverage Code(Counter(0)) => $DIR/branch_match_arms.rs:21:2: 21:2 (#0);
+ coverage Code { bcb: bcb0 } => $DIR/branch_match_arms.rs:14:1: 15:21 (#0);
+ coverage Code { bcb: bcb1 } => $DIR/branch_match_arms.rs:16:17: 16:33 (#0);
+ coverage Code { bcb: bcb3 } => $DIR/branch_match_arms.rs:17:17: 17:33 (#0);
+ coverage Code { bcb: bcb4 } => $DIR/branch_match_arms.rs:18:17: 18:33 (#0);
+ coverage Code { bcb: bcb5 } => $DIR/branch_match_arms.rs:19:17: 19:33 (#0);
+ coverage Code { bcb: bcb2 } => $DIR/branch_match_arms.rs:21:2: 21:2 (#0);
+
bb0: {
+ Coverage::CounterIncrement(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
let mut _0: bool;

+ coverage body span: $DIR/instrument_coverage.rs:29:18: 31:2 (#0)
+ coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:29:1: 31:2 (#0);
+ coverage Code { bcb: bcb0 } => $DIR/instrument_coverage.rs:29:1: 31:2 (#0);
+
bb0: {
+ Coverage::CounterIncrement(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@

+ coverage body span: $DIR/instrument_coverage.rs:14:11: 20:2 (#0)
+ coverage ExpressionId(0) => Expression { lhs: Counter(1), op: Subtract, rhs: Counter(0) };
+ coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:14:1: 14:11 (#0);
+ coverage Code(Counter(1)) => $DIR/instrument_coverage.rs:16:12: 16:17 (#0);
+ coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:17:13: 17:18 (#0);
+ coverage Code(Expression(0)) => $DIR/instrument_coverage.rs:18:10: 18:10 (#0);
+ coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:20:2: 20:2 (#0);
+ coverage Code { bcb: bcb0 } => $DIR/instrument_coverage.rs:14:1: 14:11 (#0);
+ coverage Code { bcb: bcb1 } => $DIR/instrument_coverage.rs:16:12: 16:17 (#0);
+ coverage Code { bcb: bcb2 } => $DIR/instrument_coverage.rs:17:13: 17:18 (#0);
+ coverage Code { bcb: bcb3 } => $DIR/instrument_coverage.rs:18:10: 18:10 (#0);
+ coverage Code { bcb: bcb2 } => $DIR/instrument_coverage.rs:20:2: 20:2 (#0);
+
bb0: {
+ Coverage::CounterIncrement(0);
Expand Down
4 changes: 2 additions & 2 deletions tests/mir-opt/coverage/instrument_coverage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// EMIT_MIR instrument_coverage.main.InstrumentCoverage.diff
// CHECK-LABEL: fn main()
// CHECK: coverage body span:
// CHECK: coverage Code(Counter({{[0-9]+}})) =>
// CHECK: coverage Code { bcb: bcb{{[0-9]+}} } =>
// CHECK: bb0:
// CHECK: Coverage::CounterIncrement
fn main() {
Expand All @@ -22,7 +22,7 @@ fn main() {
// EMIT_MIR instrument_coverage.bar.InstrumentCoverage.diff
// CHECK-LABEL: fn bar()
// CHECK: coverage body span:
// CHECK: coverage Code(Counter({{[0-9]+}})) =>
// CHECK: coverage Code { bcb: bcb{{[0-9]+}} } =>
// CHECK: bb0:
// CHECK: Coverage::CounterIncrement
#[inline(never)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@

coverage body span: $DIR/instrument_coverage_cleanup.rs:13:11: 15:2 (#0)
coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Subtract, rhs: Counter(1) };
coverage Code(Counter(0)) => $DIR/instrument_coverage_cleanup.rs:13:1: 14:36 (#0);
coverage Code(Expression(0)) => $DIR/instrument_coverage_cleanup.rs:14:37: 14:39 (#0);
coverage Code(Counter(1)) => $DIR/instrument_coverage_cleanup.rs:14:39: 14:39 (#0);
coverage Code(Counter(0)) => $DIR/instrument_coverage_cleanup.rs:15:2: 15:2 (#0);
coverage Branch { true_term: Expression(0), false_term: Counter(1) } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0);
coverage Code { bcb: bcb0 } => $DIR/instrument_coverage_cleanup.rs:13:1: 14:36 (#0);
coverage Code { bcb: bcb3 } => $DIR/instrument_coverage_cleanup.rs:14:37: 14:39 (#0);
coverage Code { bcb: bcb1 } => $DIR/instrument_coverage_cleanup.rs:14:39: 14:39 (#0);
coverage Code { bcb: bcb2 } => $DIR/instrument_coverage_cleanup.rs:15:2: 15:2 (#0);
coverage Branch { true_bcb: bcb3, false_bcb: bcb1 } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0);

bb0: {
Coverage::CounterIncrement(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@

+ coverage body span: $DIR/instrument_coverage_cleanup.rs:13:11: 15:2 (#0)
+ coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Subtract, rhs: Counter(1) };
+ coverage Code(Counter(0)) => $DIR/instrument_coverage_cleanup.rs:13:1: 14:36 (#0);
+ coverage Code(Expression(0)) => $DIR/instrument_coverage_cleanup.rs:14:37: 14:39 (#0);
+ coverage Code(Counter(1)) => $DIR/instrument_coverage_cleanup.rs:14:39: 14:39 (#0);
+ coverage Code(Counter(0)) => $DIR/instrument_coverage_cleanup.rs:15:2: 15:2 (#0);
+ coverage Branch { true_term: Expression(0), false_term: Counter(1) } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0);
+ coverage Code { bcb: bcb0 } => $DIR/instrument_coverage_cleanup.rs:13:1: 14:36 (#0);
+ coverage Code { bcb: bcb3 } => $DIR/instrument_coverage_cleanup.rs:14:37: 14:39 (#0);
+ coverage Code { bcb: bcb1 } => $DIR/instrument_coverage_cleanup.rs:14:39: 14:39 (#0);
+ coverage Code { bcb: bcb2 } => $DIR/instrument_coverage_cleanup.rs:15:2: 15:2 (#0);
+ coverage Branch { true_bcb: bcb3, false_bcb: bcb1 } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0);
+
bb0: {
+ Coverage::CounterIncrement(0);
Expand Down

0 comments on commit ee7dc06

Please sign in to comment.