Skip to content

Commit 00aa76a

Browse files
committed
!! (WIP) Only CoverageKind::Mappings can have code regions
1 parent e56269d commit 00aa76a

File tree

10 files changed

+50
-170
lines changed

10 files changed

+50
-170
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -72,39 +72,34 @@ impl<'tcx> FunctionCoverage<'tcx> {
7272
self.is_used
7373
}
7474

75-
/// Sets the function source hash value. If called multiple times for the same function, all
76-
/// calls should have the same hash value.
77-
pub fn set_function_source_hash(&mut self, source_hash: u64) {
75+
/// Notes that a counter increment has been encountered with the given
76+
/// counter ID and source hash.
77+
///
78+
/// If called multiple times for the same function, all calls should have
79+
/// the same hash value.
80+
#[instrument(level = "debug", skip(self))]
81+
pub(crate) fn counter_seen(&mut self, id: CounterId, source_hash: u64) {
82+
self.counters_seen.insert(id);
83+
7884
if self.source_hash == 0 {
7985
self.source_hash = source_hash;
8086
} else {
8187
debug_assert_eq!(source_hash, self.source_hash);
8288
}
8389
}
8490

85-
/// Adds code regions to be counted by an injected counter intrinsic.
86-
#[instrument(level = "debug", skip(self))]
87-
pub(crate) fn add_counter(&mut self, id: CounterId, code_regions: &[CodeRegion]) {
88-
if !self.counters_seen.insert(id) {
89-
return;
90-
}
91-
self.add_mappings(CovTerm::Counter(id), code_regions);
92-
}
93-
94-
/// Adds information about a coverage expression, along with zero or more
95-
/// code regions mapped to that expression.
91+
/// Adds information about a coverage expression.
9692
///
9793
/// Both counters and "counter expressions" (or simply, "expressions") can be operands in other
9894
/// expressions. These are tracked as separate variants of `CovTerm`, so there is no ambiguity
9995
/// between operands that are counter IDs and operands that are expression IDs.
10096
#[instrument(level = "debug", skip(self))]
101-
pub(crate) fn add_counter_expression(
97+
pub(crate) fn add_expression_data(
10298
&mut self,
10399
expression_id: ExpressionId,
104100
lhs: CovTerm,
105101
op: Op,
106102
rhs: CovTerm,
107-
code_regions: &[CodeRegion],
108103
) {
109104
debug_assert!(
110105
expression_id.as_usize() < self.expressions.len(),
@@ -121,23 +116,17 @@ impl<'tcx> FunctionCoverage<'tcx> {
121116
None => *slot = Some(expression),
122117
// If this expression ID slot has already been filled, it should
123118
// contain identical information.
124-
Some(ref previous_expression) => {
125-
assert_eq!(
126-
previous_expression, &expression,
127-
"add_counter_expression: expression for id changed"
128-
);
129-
return;
130-
}
119+
Some(ref previous_expression) => assert_eq!(
120+
previous_expression, &expression,
121+
"add_counter_expression: expression for id changed"
122+
),
131123
}
132-
133-
self.add_mappings(CovTerm::Expression(expression_id), code_regions);
134124
}
135125

136-
/// Adds regions that will be marked as "unreachable", with a constant "zero counter".
126+
/// Adds a region that will be marked as "unreachable", with a constant "zero counter".
137127
#[instrument(level = "debug", skip(self))]
138-
pub(crate) fn add_unreachable_regions(&mut self, code_regions: &[CodeRegion]) {
139-
assert!(!code_regions.is_empty(), "unreachable regions always have code regions");
140-
self.add_mappings(CovTerm::Zero, code_regions);
128+
pub(crate) fn add_unreachable_region(&mut self, code_region: &CodeRegion) {
129+
self.add_mappings(CovTerm::Zero, std::slice::from_ref(code_region));
141130
}
142131

143132
pub(crate) fn add_mappings(&mut self, kind: CovTerm, code_regions: &[CodeRegion]) {

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,14 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
110110
.entry(instance)
111111
.or_insert_with(|| FunctionCoverage::new(bx.tcx(), instance));
112112

113-
let Coverage { kind, code_regions } = coverage;
113+
let Coverage { kind } = coverage;
114114
match *kind {
115115
CoverageKind::Counter { function_source_hash, id } => {
116116
debug!(
117117
"ensuring function source hash is set for instance={:?}; function_source_hash={}",
118118
instance, function_source_hash,
119119
);
120-
func_coverage.set_function_source_hash(function_source_hash);
121-
func_coverage.add_counter(id, code_regions);
120+
func_coverage.counter_seen(id, function_source_hash);
122121
// We need to explicitly drop the `RefMut` before calling into `instrprof_increment`,
123122
// as that needs an exclusive borrow.
124123
drop(coverage_map);
@@ -136,12 +135,9 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
136135
bx.instrprof_increment(fn_name, hash, num_counters, index);
137136
}
138137
CoverageKind::Expression { id, lhs, op, rhs } => {
139-
func_coverage.add_counter_expression(id, lhs, op, rhs, code_regions);
138+
func_coverage.add_expression_data(id, lhs, op, rhs);
140139
}
141-
CoverageKind::Unreachable => {
142-
func_coverage.add_unreachable_regions(code_regions);
143-
}
144-
CoverageKind::Mappings { kind } => {
140+
CoverageKind::Mappings { kind, ref code_regions } => {
145141
func_coverage.add_mappings(kind, code_regions);
146142
}
147143
}
@@ -211,8 +207,7 @@ fn add_unused_function_coverage<'tcx>(
211207

212208
let mut function_coverage = FunctionCoverage::unused(tcx, instance);
213209
for &code_region in tcx.covered_code_regions(def_id) {
214-
let code_region = std::slice::from_ref(code_region);
215-
function_coverage.add_unreachable_regions(code_region);
210+
function_coverage.add_unreachable_region(code_region);
216211
}
217212

218213
if let Some(coverage_context) = cx.coverage_context() {

compiler/rustc_middle/src/mir/coverage.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ pub enum CoverageKind {
7575
op: Op,
7676
rhs: CovTerm,
7777
},
78-
Unreachable,
7978
Mappings {
8079
kind: CovTerm,
80+
code_regions: Vec<CodeRegion>,
8181
},
8282
}
8383

@@ -97,8 +97,11 @@ impl Debug for CoverageKind {
9797
},
9898
rhs,
9999
),
100-
Unreachable => write!(fmt, "Unreachable"),
101-
Mappings { kind } => fmt.debug_struct("Mappings").field("kind", kind).finish(),
100+
Mappings { kind, code_regions } => fmt
101+
.debug_struct("Mappings")
102+
.field("kind", kind)
103+
.field("code_regions", code_regions)
104+
.finish(),
102105
}
103106
}
104107
}

compiler/rustc_middle/src/mir/mod.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,12 +1473,8 @@ impl Debug for Statement<'_> {
14731473
AscribeUserType(box (ref place, ref c_ty), ref variance) => {
14741474
write!(fmt, "AscribeUserType({place:?}, {variance:?}, {c_ty:?})")
14751475
}
1476-
Coverage(box self::Coverage { ref kind, ref code_regions }) => {
1477-
if code_regions.is_empty() {
1478-
write!(fmt, "Coverage::{kind:?}")
1479-
} else {
1480-
write!(fmt, "Coverage::{kind:?} for {code_regions:?}")
1481-
}
1476+
Coverage(box self::Coverage { ref kind }) => {
1477+
write!(fmt, "Coverage::{kind:?}")
14821478
}
14831479
Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"),
14841480
ConstEvalCounter => write!(fmt, "ConstEvalCounter"),

compiler/rustc_middle/src/mir/syntax.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
66
use super::{BasicBlock, Constant, Local, SwitchTargets, UserTypeProjection};
77

8-
use crate::mir::coverage::{CodeRegion, CoverageKind};
8+
use crate::mir::coverage::CoverageKind;
99
use crate::traits::Reveal;
1010
use crate::ty::adjustment::PointerCoercion;
1111
use crate::ty::GenericArgsRef;
@@ -523,7 +523,6 @@ pub enum FakeReadCause {
523523
#[derive(TypeFoldable, TypeVisitable)]
524524
pub struct Coverage {
525525
pub kind: CoverageKind,
526-
pub code_regions: Vec<CodeRegion>,
527526
}
528527

529528
#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]

compiler/rustc_mir_transform/src/coverage/mod.rs

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -316,15 +316,13 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
316316

317317
inject_statement(
318318
self.mir_body,
319-
CoverageKind::Mappings { kind: counter_kind.as_term() },
319+
CoverageKind::Mappings { kind: counter_kind.as_term(), code_regions },
320320
mir::START_BLOCK,
321-
code_regions,
322321
);
323322
inject_statement(
324323
self.mir_body,
325324
self.make_mir_coverage_kind(&counter_kind),
326325
self.bcb_leader_bb(bcb),
327-
Vec::new(),
328326
);
329327
}
330328
}
@@ -407,7 +405,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
407405
self.mir_body,
408406
self.make_mir_coverage_kind(&counter_kind),
409407
inject_to_bb,
410-
Vec::new(),
411408
);
412409
}
413410
BcbCounter::Expression { .. } => inject_intermediate_expression(
@@ -473,18 +470,13 @@ fn inject_edge_counter_basic_block(
473470
new_bb
474471
}
475472

476-
fn inject_statement(
477-
mir_body: &mut mir::Body<'_>,
478-
counter_kind: CoverageKind,
479-
bb: BasicBlock,
480-
code_regions: Vec<CodeRegion>,
481-
) {
482-
debug!(" injecting statement {counter_kind:?} for {bb:?} at code regions: {code_regions:?}");
473+
fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb: BasicBlock) {
474+
debug!(" injecting statement {counter_kind:?} for {bb:?}");
483475
let data = &mut mir_body[bb];
484476
let source_info = data.terminator().source_info;
485477
let statement = Statement {
486478
source_info,
487-
kind: StatementKind::Coverage(Box::new(Coverage { kind: counter_kind, code_regions })),
479+
kind: StatementKind::Coverage(Box::new(Coverage { kind: counter_kind })),
488480
};
489481
data.statements.insert(0, statement);
490482
}
@@ -498,10 +490,7 @@ fn inject_intermediate_expression(mir_body: &mut mir::Body<'_>, expression: Cove
498490
let source_info = data.terminator().source_info;
499491
let statement = Statement {
500492
source_info,
501-
kind: StatementKind::Coverage(Box::new(Coverage {
502-
kind: expression,
503-
code_regions: Vec::new(),
504-
})),
493+
kind: StatementKind::Coverage(Box::new(Coverage { kind: expression })),
505494
};
506495
data.statements.push(statement);
507496
}

compiler/rustc_mir_transform/src/coverage/query.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,7 @@ impl CoverageVisitor {
6868
self.update_from_term(lhs);
6969
self.update_from_term(rhs);
7070
}
71-
CoverageKind::Unreachable => {}
72-
CoverageKind::Mappings { kind } => self.update_from_term(kind),
71+
CoverageKind::Mappings { kind, .. } => self.update_from_term(kind),
7372
}
7473
}
7574
}
@@ -94,8 +93,11 @@ fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, instance_def: ty::InstanceDef<'tcx>) ->
9493
fn covered_code_regions(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<&CodeRegion> {
9594
let body = mir_body(tcx, def_id);
9695
all_coverage_in_mir_body(body)
97-
// Coverage statements have a list of code regions (possibly empty).
98-
.flat_map(|coverage| coverage.code_regions.as_slice())
96+
// Only `Mappings` statements have a list of code regions.
97+
.flat_map(|coverage| match coverage.kind {
98+
CoverageKind::Mappings { ref code_regions, .. } => code_regions.as_slice(),
99+
_ => &[],
100+
})
99101
.collect()
100102
}
101103

compiler/rustc_mir_transform/src/simplify.rs

Lines changed: 2 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,8 @@
2828
//! return.
2929
3030
use crate::MirPass;
31-
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
31+
use rustc_data_structures::fx::FxIndexSet;
3232
use rustc_index::{Idx, IndexSlice, IndexVec};
33-
use rustc_middle::mir::coverage::*;
3433
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
3534
use rustc_middle::mir::*;
3635
use rustc_middle::ty::TyCtxt;
@@ -336,15 +335,14 @@ pub fn remove_duplicate_unreachable_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut B
336335
}
337336
}
338337

339-
pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
338+
pub fn remove_dead_blocks<'tcx>(_tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
340339
let reachable = traversal::reachable_as_bitset(body);
341340
let num_blocks = body.basic_blocks.len();
342341
if num_blocks == reachable.count() {
343342
return;
344343
}
345344

346345
let basic_blocks = body.basic_blocks.as_mut();
347-
let source_scopes = &body.source_scopes;
348346
let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect();
349347
let mut used_blocks = 0;
350348
for alive_index in reachable.iter() {
@@ -358,10 +356,6 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
358356
used_blocks += 1;
359357
}
360358

361-
if tcx.sess.instrument_coverage() {
362-
save_unreachable_coverage(basic_blocks, source_scopes, used_blocks);
363-
}
364-
365359
basic_blocks.raw.truncate(used_blocks);
366360

367361
for block in basic_blocks {
@@ -371,93 +365,6 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
371365
}
372366
}
373367

374-
/// Some MIR transforms can determine at compile time that a sequences of
375-
/// statements will never be executed, so they can be dropped from the MIR.
376-
/// For example, an `if` or `else` block that is guaranteed to never be executed
377-
/// because its condition can be evaluated at compile time, such as by const
378-
/// evaluation: `if false { ... }`.
379-
///
380-
/// Those statements are bypassed by redirecting paths in the CFG around the
381-
/// `dead blocks`; but with `-C instrument-coverage`, the dead blocks usually
382-
/// include `Coverage` statements representing the Rust source code regions to
383-
/// be counted at runtime. Without these `Coverage` statements, the regions are
384-
/// lost, and the Rust source code will show no coverage information.
385-
///
386-
/// What we want to show in a coverage report is the dead code with coverage
387-
/// counts of `0`. To do this, we need to save the code regions, by injecting
388-
/// `Unreachable` coverage statements. These are non-executable statements whose
389-
/// code regions are still recorded in the coverage map, representing regions
390-
/// with `0` executions.
391-
///
392-
/// If there are no live `Counter` `Coverage` statements remaining, we remove
393-
/// `Coverage` statements along with the dead blocks. Since at least one
394-
/// counter per function is required by LLVM (and necessary, to add the
395-
/// `function_hash` to the counter's call to the LLVM intrinsic
396-
/// `instrprof.increment()`).
397-
///
398-
/// The `generator::StateTransform` MIR pass and MIR inlining can create
399-
/// atypical conditions, where all live `Counter`s are dropped from the MIR.
400-
///
401-
/// With MIR inlining we can have coverage counters belonging to different
402-
/// instances in a single body, so the strategy described above is applied to
403-
/// coverage counters from each instance individually.
404-
fn save_unreachable_coverage(
405-
basic_blocks: &mut IndexSlice<BasicBlock, BasicBlockData<'_>>,
406-
source_scopes: &IndexSlice<SourceScope, SourceScopeData<'_>>,
407-
first_dead_block: usize,
408-
) {
409-
// Identify instances that still have some live coverage counters left.
410-
let mut live = FxHashSet::default();
411-
for basic_block in &basic_blocks.raw[0..first_dead_block] {
412-
for statement in &basic_block.statements {
413-
let StatementKind::Coverage(coverage) = &statement.kind else { continue };
414-
let CoverageKind::Counter { .. } = coverage.kind else { continue };
415-
let instance = statement.source_info.scope.inlined_instance(source_scopes);
416-
live.insert(instance);
417-
}
418-
}
419-
420-
for block in &mut basic_blocks.raw[..first_dead_block] {
421-
for statement in &mut block.statements {
422-
let StatementKind::Coverage(_) = &statement.kind else { continue };
423-
let instance = statement.source_info.scope.inlined_instance(source_scopes);
424-
if !live.contains(&instance) {
425-
statement.make_nop();
426-
}
427-
}
428-
}
429-
430-
if live.is_empty() {
431-
return;
432-
}
433-
434-
// Retain coverage for instances that still have some live counters left.
435-
let mut retained_coverage = Vec::new();
436-
for dead_block in &basic_blocks.raw[first_dead_block..] {
437-
for statement in &dead_block.statements {
438-
let StatementKind::Coverage(coverage) = &statement.kind else { continue };
439-
if coverage.code_regions.is_empty() {
440-
continue;
441-
};
442-
let instance = statement.source_info.scope.inlined_instance(source_scopes);
443-
if live.contains(&instance) {
444-
retained_coverage.push((statement.source_info, coverage.code_regions.clone()));
445-
}
446-
}
447-
}
448-
449-
let start_block = &mut basic_blocks[START_BLOCK];
450-
start_block.statements.extend(retained_coverage.into_iter().map(
451-
|(source_info, code_regions)| Statement {
452-
source_info,
453-
kind: StatementKind::Coverage(Box::new(Coverage {
454-
kind: CoverageKind::Unreachable,
455-
code_regions,
456-
})),
457-
},
458-
));
459-
}
460-
461368
pub enum SimplifyLocals {
462369
BeforeConstProp,
463370
Final,

0 commit comments

Comments
 (0)