Remove reachable coverage without counters

Remove reachable coverage without counters to maintain invariant that
either there is no coverage at all or there is a live coverage counter
left that provides the function source hash.

The motivating example would be a following closure:

```rust
    let f = |x: bool| {
        debug_assert!(x);
    };
```

Which, with span changes from #93967, with disabled debug assertions,
after the final CFG simplifications but before removal of dead blocks,
gives rise to MIR:

```rust
fn main::{closure#0}(_1: &[closure@a.rs:2:13: 2:22], _2: bool) -> () {
    debug x => _2;
    let mut _0: ();

    bb0: {
        Coverage::Expression(4294967295) = 1 - 2;
        return;
    }

    ...
}
```
This commit is contained in:
Tomasz Miąsko 2022-07-25 00:00:00 +00:00
parent 2f320a224e
commit 5f40a4f7a0
3 changed files with 42 additions and 18 deletions

View file

@ -315,7 +315,7 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
/// with `0` executions. /// with `0` executions.
/// ///
/// If there are no live `Counter` `Coverage` statements remaining, we remove /// If there are no live `Counter` `Coverage` statements remaining, we remove
/// dead `Coverage` statements along with the dead blocks. Since at least one /// `Coverage` statements along with the dead blocks. Since at least one
/// counter per function is required by LLVM (and necessary, to add the /// counter per function is required by LLVM (and necessary, to add the
/// `function_hash` to the counter's call to the LLVM intrinsic /// `function_hash` to the counter's call to the LLVM intrinsic
/// `instrprof.increment()`). /// `instrprof.increment()`).
@ -342,6 +342,16 @@ fn save_unreachable_coverage(
} }
} }
for block in &mut basic_blocks.raw[..first_dead_block] {
for statement in &mut block.statements {
let StatementKind::Coverage(_) = &statement.kind else { continue };
let instance = statement.source_info.scope.inlined_instance(source_scopes);
if !live.contains(&instance) {
statement.make_nop();
}
}
}
if live.is_empty() { if live.is_empty() {
return; return;
} }

View file

@ -1,21 +1,28 @@
1| |// Regression test for issue #98833. 1| |// Regression test for issue #98833.
2| |// compile-flags: -Zinline-mir 2| |// compile-flags: -Zinline-mir -Cdebug-assertions=off
3| | 3| |
4| 1|fn main() { 4| 1|fn main() {
5| 1| println!("{}", live::<false>()); 5| 1| println!("{}", live::<false>());
6| 1|} 6| 1|
7| | 7| 1| let f = |x: bool| {
8| |#[inline] 8| | debug_assert!(
9| 1|fn live<const B: bool>() -> u32 { 9| | x
10| 1| if B { 10| | );
11| 0| dead() 11| 1| };
12| | } else { 12| 1| f(false);
13| 1| 0 13| 1|}
14| | } 14| |
15| 1|} 15| |#[inline]
16| | 16| 1|fn live<const B: bool>() -> u32 {
17| |#[inline] 17| 1| if B {
18| 0|fn dead() -> u32 { 18| 0| dead()
19| 0| 42 19| | } else {
20| 0|} 20| 1| 0
21| | }
22| 1|}
23| |
24| |#[inline]
25| 0|fn dead() -> u32 {
26| 0| 42
27| 0|}

View file

@ -1,8 +1,15 @@
// Regression test for issue #98833. // Regression test for issue #98833.
// compile-flags: -Zinline-mir // compile-flags: -Zinline-mir -Cdebug-assertions=off
fn main() { fn main() {
println!("{}", live::<false>()); println!("{}", live::<false>());
let f = |x: bool| {
debug_assert!(
x
);
};
f(false);
} }
#[inline] #[inline]