Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Skip MIR pass UnreachablePropagation when coverage is enabled #116166

Merged
merged 1 commit into from
Sep 27, 2023

Conversation

Zalathar
Copy link
Contributor

When coverage instrumentation and MIR opts are both enabled, coverage relies on two assumptions:

  • MIR opts that would delete StatementKind::Coverage statements instead move them into bb0 and change them to CoverageKind::Unreachable.

  • MIR opts won't delete all CoverageKind::Counter statements from an instrumented function.

Most MIR opts naturally satisfy the second assumption, because they won't remove coverage statements from bb0, but UnreachablePropagation can do so if it finds that bb0 is unreachable. If this happens, LLVM thinks the function isn't instrumented, and it vanishes from coverage reports.

A proper solution won't be possible until after per-function coverage info lands in #116046, but for now we can avoid the problem by turning off this particular pass when coverage instrumentation is enabled.


cc @cjgillot since I found this while investigating coverage problems encountered by #113970
@rustbot label +A-code-coverage +A-mir-opt

@rustbot
Copy link
Collaborator

rustbot commented Sep 26, 2023

r? @petrochenkov

(rustbot has picked a reviewer for you, use r? to override)

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Sep 26, 2023
@rustbot
Copy link
Collaborator

rustbot commented Sep 26, 2023

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

@rustbot rustbot added A-code-coverage Area: Source-based code coverage (-Cinstrument-coverage) A-mir-opt Area: MIR optimizations labels Sep 26, 2023
@Zalathar
Copy link
Contributor Author

I had also written a more complicated patch that leaves this pass enabled, but takes care not to erase coverage statements from unreachable blocks.

But for now I think it's simpler to just turn off the pass when coverage is enabled, and try to fix it entirely on the coverage side (without having to modify other MIR passes) after some other coverage-related changes land.

@@ -13,7 +13,9 @@ pub struct UnreachablePropagation;
impl MirPass<'_> for UnreachablePropagation {
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
// Enable only under -Zmir-opt-level=2 as this can make programs less debuggable.
sess.mir_opt_level() >= 2
// Coverage gets confused by MIR passes that can remove all coverage statements
// from an instrumented function.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a FIXME with a issue/PR number, so a future reader will know when to revert that PR?
r=me once done

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a FIXME that refers to a new issue, #116171.

When coverage instrumentation and MIR opts are both enabled, coverage relies on
two assumptions:

- MIR opts that would delete `StatementKind::Coverage` statements instead move
  them into bb0 and change them to `CoverageKind::Unreachable`.

- MIR opts won't delete all `CoverageKind::Counter` statements from an
  instrumented function.

Most MIR opts naturally satisfy the second assumption, because they won't
remove coverage statements from bb0, but `UnreachablePropagation` can do so if
it finds that bb0 is unreachable. If this happens, LLVM thinks the function
isn't instrumented, and it vanishes from coverage reports.

A proper solution won't be possible until after per-function coverage info
lands in rust-lang#116046, but for now we can avoid the problem by turning off this
particular pass when coverage instrumentation is enabled.
@oli-obk
Copy link
Contributor

oli-obk commented Sep 26, 2023

@bors r=cjgillot

@bors
Copy link
Contributor

bors commented Sep 26, 2023

📌 Commit 64df5a8 has been approved by cjgillot

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 26, 2023
@bors
Copy link
Contributor

bors commented Sep 27, 2023

⌛ Testing commit 64df5a8 with merge 376f3f0...

@bors
Copy link
Contributor

bors commented Sep 27, 2023

☀️ Test successful - checks-actions
Approved by: cjgillot
Pushing 376f3f0 to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Sep 27, 2023
@bors bors merged commit 376f3f0 into rust-lang:master Sep 27, 2023
12 checks passed
@rustbot rustbot added this to the 1.74.0 milestone Sep 27, 2023
@Zalathar Zalathar deleted the unreachable branch September 27, 2023 07:38
@rust-timer
Copy link
Collaborator

Finished benchmarking commit (376f3f0): comparison URL.

Overall result: ❌ regressions - no action needed

@rustbot label: -perf-regression

Instruction count

This is a highly reliable metric that was used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
0.3% [0.2%, 0.4%] 5
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

Max RSS (memory usage)

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
3.7% [3.6%, 3.9%] 2
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

Cycles

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-1.4% [-1.4%, -1.4%] 1
All ❌✅ (primary) - - 0

Binary size

This benchmark run did not return any relevant results for this metric.

Bootstrap: 630.064s -> 629.749s (-0.05%)
Artifact size: 317.19 MiB -> 317.26 MiB (0.02%)

bors added a commit to rust-lang-ci/rust that referenced this pull request Oct 1, 2023
coverage: Regression test for functions with unreachable bodies

This is a regression test for the coverage issue that was addressed temporarily by rust-lang#116166, and is tracked by rust-lang#116171.

---

If we instrument a function for coverage, but all of its counter-increment statements are removed by MIR optimizations, LLVM will think it isn't instrumented and it will disappear from coverage maps and coverage reports.

Most MIR opts won't cause this because they tend not to remove statements from bb0, but `UnreachablePropagation` can do so if it sees that bb0 ends with `TerminatorKind::Unreachable`.

Currently we have worked around this by turning off `UnreachablePropagation` when coverage instrumentation is enabled, which is why this test is able to pass.

---

`@rustbot` label +A-code-coverage
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-code-coverage Area: Source-based code coverage (-Cinstrument-coverage) A-mir-opt Area: MIR optimizations merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants