Skip to content

Commit 334bbbe

Browse files
committed
Fixed four bugs found while compiling a larger set of real world crates
Compiling with coverage, with the expression optimization, now works on the json5format crate and its dependencies.
1 parent 1296fe3 commit 334bbbe

File tree

94 files changed

+6770
-377
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+6770
-377
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

+14-2
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,15 @@ const COVMAP_VAR_ALIGN_BYTES: usize = 8;
2828
/// A context object for maintaining all state needed by the coverageinfo module.
2929
pub struct CrateCoverageContext<'tcx> {
3030
// Coverage data for each instrumented function identified by DefId.
31-
pub(crate) function_coverage_map: RefCell<FxHashMap<Instance<'tcx>, FunctionCoverage>>,
31+
pub(crate) function_coverage_map: RefCell<FxHashMap<Instance<'tcx>, FunctionCoverage<'tcx>>>,
3232
}
3333

3434
impl<'tcx> CrateCoverageContext<'tcx> {
3535
pub fn new() -> Self {
3636
Self { function_coverage_map: Default::default() }
3737
}
3838

39-
pub fn take_function_coverage_map(&self) -> FxHashMap<Instance<'tcx>, FunctionCoverage> {
39+
pub fn take_function_coverage_map(&self) -> FxHashMap<Instance<'tcx>, FunctionCoverage<'tcx>> {
4040
self.function_coverage_map.replace(FxHashMap::default())
4141
}
4242
}
@@ -58,6 +58,18 @@ impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
5858
unsafe { llvm::LLVMRustCoverageCreatePGOFuncNameVar(llfn, mangled_fn_name.as_ptr()) }
5959
}
6060

61+
fn set_function_source_hash(&mut self, instance: Instance<'tcx>, function_source_hash: u64) {
62+
debug!(
63+
"ensuring function source hash is set for instance={:?}; function_source_hash={}",
64+
instance, function_source_hash,
65+
);
66+
let mut coverage_map = self.coverage_context().function_coverage_map.borrow_mut();
67+
coverage_map
68+
.entry(instance)
69+
.or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
70+
.set_function_source_hash(function_source_hash);
71+
}
72+
6173
fn add_coverage_counter(
6274
&mut self,
6375
instance: Instance<'tcx>,

compiler/rustc_codegen_ssa/src/coverageinfo/map.rs

+23-4
Original file line numberDiff line numberDiff line change
@@ -28,28 +28,43 @@ pub struct Expression {
2828
/// only whitespace or comments). According to LLVM Code Coverage Mapping documentation, "A count
2929
/// for a gap area is only used as the line execution count if there are no other regions on a
3030
/// line."
31-
pub struct FunctionCoverage {
31+
pub struct FunctionCoverage<'tcx> {
32+
instance: Instance<'tcx>,
3233
source_hash: u64,
3334
counters: IndexVec<CounterValueReference, Option<CodeRegion>>,
3435
expressions: IndexVec<InjectedExpressionIndex, Option<Expression>>,
3536
unreachable_regions: Vec<CodeRegion>,
3637
}
3738

38-
impl FunctionCoverage {
39-
pub fn new<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self {
39+
impl<'tcx> FunctionCoverage<'tcx> {
40+
pub fn new(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self {
4041
let coverageinfo = tcx.coverageinfo(instance.def_id());
4142
debug!(
4243
"FunctionCoverage::new(instance={:?}) has coverageinfo={:?}",
4344
instance, coverageinfo
4445
);
4546
Self {
47+
instance,
4648
source_hash: 0, // will be set with the first `add_counter()`
4749
counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize),
4850
expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize),
4951
unreachable_regions: Vec::new(),
5052
}
5153
}
5254

55+
/// Although every function should have at least one `Counter`, the `Counter` isn't required to
56+
/// have a `CodeRegion`. (The `CodeRegion` may be associated only with `Expressions`.) This
57+
/// method supports the ability to ensure the `function_source_hash` is set from `Counters` that
58+
/// do not trigger the call to `add_counter()` because they don't have an associated
59+
/// `CodeRegion` to add.
60+
pub fn set_function_source_hash(&mut self, source_hash: u64) {
61+
if self.source_hash == 0 {
62+
self.source_hash = source_hash;
63+
} else {
64+
debug_assert_eq!(source_hash, self.source_hash);
65+
}
66+
}
67+
5368
/// Adds a code region to be counted by an injected counter intrinsic.
5469
/// The source_hash (computed during coverage instrumentation) should also be provided, and
5570
/// should be the same for all counters in a given function.
@@ -111,7 +126,11 @@ impl FunctionCoverage {
111126
pub fn get_expressions_and_counter_regions<'a>(
112127
&'a self,
113128
) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &'a CodeRegion)>) {
114-
assert!(self.source_hash != 0);
129+
assert!(
130+
self.source_hash != 0,
131+
"No counters provided the source_hash for function: {:?}",
132+
self.instance
133+
);
115134

116135
let counter_regions = self.counter_regions();
117136
let (counter_expressions, expression_regions) = self.expressions_with_regions();

compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
2424

2525
if let Some(code_region) = code_region {
2626
bx.add_coverage_counter(self.instance, function_source_hash, id, code_region);
27+
} else {
28+
bx.set_function_source_hash(self.instance, function_source_hash);
2729
}
2830
// Note: Some counters do not have code regions, but may still be referenced from
2931
// expressions.

compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ pub trait CoverageInfoMethods: BackendTypes {
99
pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes {
1010
fn create_pgo_func_name_var(&self, instance: Instance<'tcx>) -> Self::Value;
1111

12+
fn set_function_source_hash(&mut self, instance: Instance<'tcx>, function_source_hash: u64);
13+
1214
fn add_coverage_counter(
1315
&mut self,
1416
instance: Instance<'tcx>,

compiler/rustc_mir/src/transform/coverage/debug.rs

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use rustc_middle::mir::{BasicBlock, TerminatorKind};
77

88
use std::lazy::SyncOnceCell;
99

10+
pub const NESTED_INDENT: &str = " ";
11+
1012
const RUSTC_COVERAGE_DEBUG_OPTIONS: &str = "RUSTC_COVERAGE_DEBUG_OPTIONS";
1113

1214
pub(crate) fn debug_options<'a>() -> &'a DebugOptions {

compiler/rustc_mir/src/transform/coverage/graph.rs

+116-24
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use super::Error;
2+
13
use rustc_data_structures::fx::FxHashMap;
24
use rustc_data_structures::graph::dominators::{self, Dominators};
35
use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode};
@@ -313,16 +315,28 @@ impl BasicCoverageBlockData {
313315
}
314316

315317
#[inline(always)]
316-
pub fn set_counter(&mut self, counter_kind: CoverageKind) -> ExpressionOperandId {
318+
pub fn set_counter(
319+
&mut self,
320+
counter_kind: CoverageKind,
321+
) -> Result<ExpressionOperandId, Error> {
317322
debug_assert!(
323+
// If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also
324+
// have an expression (to be injected into an existing `BasicBlock` represented by this
325+
// `BasicCoverageBlock`).
318326
self.edge_from_bcbs.is_none() || counter_kind.is_expression(),
319327
"attempt to add a `Counter` to a BCB target with existing incoming edge counters"
320328
);
321329
let operand = counter_kind.as_operand_id();
322-
self.counter_kind
323-
.replace(counter_kind)
324-
.expect_none("attempt to set a BasicCoverageBlock coverage counter more than once");
325-
operand
330+
let expect_none = self.counter_kind.replace(counter_kind);
331+
if expect_none.is_some() {
332+
return Error::from_string(format!(
333+
"attempt to set a BasicCoverageBlock coverage counter more than once; \
334+
{:?} already had counter {:?}",
335+
self,
336+
expect_none.unwrap(),
337+
));
338+
}
339+
Ok(operand)
326340
}
327341

328342
#[inline(always)]
@@ -340,19 +354,33 @@ impl BasicCoverageBlockData {
340354
&mut self,
341355
from_bcb: BasicCoverageBlock,
342356
counter_kind: CoverageKind,
343-
) -> ExpressionOperandId {
344-
debug_assert!(
345-
self.counter_kind.as_ref().map_or(true, |c| c.is_expression()),
346-
"attempt to add an incoming edge counter from {:?} when the target BCB already has a \
347-
`Counter`",
348-
from_bcb
349-
);
357+
) -> Result<ExpressionOperandId, Error> {
358+
if level_enabled!(tracing::Level::DEBUG) {
359+
// If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also
360+
// have an expression (to be injected into an existing `BasicBlock` represented by this
361+
// `BasicCoverageBlock`).
362+
if !self.counter_kind.as_ref().map_or(true, |c| c.is_expression()) {
363+
return Error::from_string(format!(
364+
"attempt to add an incoming edge counter from {:?} when the target BCB already \
365+
has a `Counter`",
366+
from_bcb
367+
));
368+
}
369+
}
350370
let operand = counter_kind.as_operand_id();
351-
self.edge_from_bcbs
371+
let expect_none = self
372+
.edge_from_bcbs
352373
.get_or_insert_with(|| FxHashMap::default())
353-
.insert(from_bcb, counter_kind)
354-
.expect_none("attempt to set an edge counter more than once");
355-
operand
374+
.insert(from_bcb, counter_kind);
375+
if expect_none.is_some() {
376+
return Error::from_string(format!(
377+
"attempt to set an edge counter more than once; from_bcb: \
378+
{:?} already had counter {:?}",
379+
from_bcb,
380+
expect_none.unwrap(),
381+
));
382+
}
383+
Ok(operand)
356384
}
357385

358386
#[inline(always)]
@@ -383,6 +411,56 @@ impl BasicCoverageBlockData {
383411
}
384412
}
385413

414+
/// Represents a successor from a branching BasicCoverageBlock (such as the arms of a `SwitchInt`)
415+
/// as either the successor BCB itself, if it has only one incoming edge, or the successor _plus_
416+
/// the specific branching BCB, representing the edge between the two. The latter case
417+
/// distinguishes this incoming edge from other incoming edges to the same `target_bcb`.
418+
#[derive(Clone, Copy, PartialEq, Eq)]
419+
pub(crate) struct BcbBranch {
420+
pub edge_from_bcb: Option<BasicCoverageBlock>,
421+
pub target_bcb: BasicCoverageBlock,
422+
}
423+
424+
impl BcbBranch {
425+
pub fn from_to(
426+
from_bcb: BasicCoverageBlock,
427+
to_bcb: BasicCoverageBlock,
428+
basic_coverage_blocks: &CoverageGraph,
429+
) -> Self {
430+
let edge_from_bcb = if basic_coverage_blocks.predecessors[to_bcb].len() > 1 {
431+
Some(from_bcb)
432+
} else {
433+
None
434+
};
435+
Self { edge_from_bcb, target_bcb: to_bcb }
436+
}
437+
438+
pub fn counter<'a>(
439+
&self,
440+
basic_coverage_blocks: &'a CoverageGraph,
441+
) -> Option<&'a CoverageKind> {
442+
if let Some(from_bcb) = self.edge_from_bcb {
443+
basic_coverage_blocks[self.target_bcb].edge_counter_from(from_bcb)
444+
} else {
445+
basic_coverage_blocks[self.target_bcb].counter()
446+
}
447+
}
448+
449+
pub fn is_only_path_to_target(&self) -> bool {
450+
self.edge_from_bcb.is_none()
451+
}
452+
}
453+
454+
impl std::fmt::Debug for BcbBranch {
455+
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
456+
if let Some(from_bcb) = self.edge_from_bcb {
457+
write!(fmt, "{:?}->{:?}", from_bcb, self.target_bcb)
458+
} else {
459+
write!(fmt, "{:?}", self.target_bcb)
460+
}
461+
}
462+
}
463+
386464
fn bcb_filtered_successors<'a, 'tcx>(
387465
body: &'tcx &'a mir::Body<'tcx>,
388466
term_kind: &'tcx TerminatorKind<'tcx>,
@@ -437,14 +515,18 @@ impl TraverseCoverageGraphWithLoops {
437515
}
438516

439517
pub fn next(&mut self) -> Option<BasicCoverageBlock> {
440-
// Strip contexts with empty worklists from the top of the stack
441-
while self.context_stack.last().map_or(false, |context| context.worklist.is_empty()) {
442-
self.context_stack.pop();
443-
}
444-
// Pop the next bcb off of the current context_stack. If none, all BCBs were visited.
445-
while let Some(next_bcb) =
518+
debug!(
519+
"TraverseCoverageGraphWithLoops::next - context_stack: {:?}",
520+
self.context_stack.iter().rev().collect::<Vec<_>>()
521+
);
522+
while let Some(next_bcb) = {
523+
// Strip contexts with empty worklists from the top of the stack
524+
while self.context_stack.last().map_or(false, |context| context.worklist.is_empty()) {
525+
self.context_stack.pop();
526+
}
527+
// Pop the next bcb off of the current context_stack. If none, all BCBs were visited.
446528
self.context_stack.last_mut().map_or(None, |context| context.worklist.pop())
447-
{
529+
} {
448530
if !self.visited.insert(next_bcb) {
449531
debug!("Already visited: {:?}", next_bcb);
450532
continue;
@@ -459,9 +541,19 @@ impl TraverseCoverageGraphWithLoops {
459541
}
460542
return Some(next_bcb);
461543
}
462-
debug_assert_eq!(self.visited.count(), self.visited.domain_size());
463544
None
464545
}
546+
547+
pub fn is_complete(&self) -> bool {
548+
self.visited.count() == self.visited.domain_size()
549+
}
550+
551+
pub fn unvisited(&self) -> Vec<BasicCoverageBlock> {
552+
let mut unvisited_set: BitSet<BasicCoverageBlock> =
553+
BitSet::new_filled(self.visited.domain_size());
554+
unvisited_set.subtract(&self.visited);
555+
unvisited_set.iter().collect::<Vec<_>>()
556+
}
465557
}
466558

467559
fn find_loop_backedges(

0 commit comments

Comments
 (0)