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

Do not remove trivial SwitchInt in analysis MIR #139042

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

compiler-errors
Copy link
Member

@compiler-errors compiler-errors commented Mar 28, 2025

This PR ensures that we don't prematurely remove trivial SwitchInt terminators which affects both the borrow-checking and runtime semantics (i.e. UB) of the code. Previously the SimplifyCfg optimization was removing SwitchInt terminators when they was "trivial", i.e. when all arms branched to the same basic block, even if that SwitchInt terminator had the side-effect of reading an operand which (for example) may not be initialized or may point to an invalid place in memory.

This behavior is unlike all other optimizations, which are only applied after "analysis" (i.e. borrow-checking) is finished, and which Miri disables to make sure the compiler doesn't silently remove UB.

Fixing this code "breaks" (i.e. unmasks) code that used to borrow-check but no longer does, like:

fn foo() {
    let x;
    let (0 | _) = x;
}

This match expression should perform a read because _ does not shadow the 0 literal pattern, and the compiler should have to read the match scrutinee to compare it to 0. I've checked that this behavior does not actually manifest in practice via a crater run which came back clean: #139042 (comment)

As a side-note, it may be tempting to suggest that this is actually a good thing or that we should preserve this behavior. If we wanted to make this work (i.e. trivially optimize out reads from matches that are redundant like 0 | _), then we should be enabling this behavior after fixing this. However, I think it's kinda unprincipled, and for example other variations of the code don't even work today, e.g.:

fn foo() {
    let x;
    let (0.. | _) = x;
}

@rustbot
Copy link
Collaborator

rustbot commented Mar 28, 2025

r? @saethlin

rustbot has assigned @saethlin.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@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 Mar 28, 2025
@rustbot
Copy link
Collaborator

rustbot commented Mar 28, 2025

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

The Miri subtree was changed

cc @rust-lang/miri

_ => return false,
// Removing a `SwitchInt` terminator may remove reads that result in UB,
// so we must not apply this optimization when mir-opt-level = 0.
if self.mir_opt_level == 0 {
Copy link
Member Author

Choose a reason for hiding this comment

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

We could perhaps optimize trivial operands... Like plain locals. Maybe consts?

Copy link
Member Author

Choose a reason for hiding this comment

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

Well I guess even plain locals could have UB if they're uninitialized. But Rust would never produce that 🤔

Copy link
Contributor

Choose a reason for hiding this comment

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

SimplifyCfg runs before borrowck, so the following compiles:

fn main() {
    let bad_ref: &i32;
    let &(0 | _) = bad_ref;
}

Copy link
Member Author

Choose a reason for hiding this comment

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

oh wonderful, so this is a load bearing optimization then

Copy link
Member

Choose a reason for hiding this comment

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

Ah, not just a bug for Miri then but a bigger problem. Certainly we shouldn't optimize before borrowck.

So instead of checking the mir_opt_level, this should be told which "phase" it is in (or it can maybe even get that info from the MIR body, not sure), and then not do this transformation in the "analysis" phase.

@rust-log-analyzer

This comment has been minimized.

@compiler-errors
Copy link
Member Author

well i do wonder if anything relies on this today

@bors try

bors added a commit to rust-lang-ci/rust that referenced this pull request Mar 28, 2025
…chint, r=<try>

Do not remove trivial `SwitchInt` with mir-opt-level=0

When mir-opt-level=0, do not optimize out `SwitchInt` terminators that all have the same terminator since it may remove a read which affects miri's ability to detect UB on that operand.

cc `@RalfJung`

Fixes rust-lang/miri#4237

This affects some tests... I guess I could mark them as `mir-opt-level=1`? Not sure.
@bors
Copy link
Collaborator

bors commented Mar 28, 2025

⌛ Trying commit 6bad178 with merge 4dba42b...

@compiler-errors
Copy link
Member Author

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 28, 2025
@@ -0,0 +1,8 @@
use std::mem::MaybeUninit;
Copy link
Member

Choose a reason for hiding this comment

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

Please add a top-level doc comment explaining what this is testing and referencing the issue.

Copy link
Member

Choose a reason for hiding this comment

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

This comment is still unresolved.

@bors
Copy link
Collaborator

bors commented Mar 28, 2025

☀️ Try build successful - checks-actions
Build commit: 4dba42b (4dba42b29bacedb63812cd6f781e31ba4b782a95)

@oli-obk
Copy link
Contributor

oli-obk commented Mar 28, 2025

@craterbot check

@craterbot
Copy link
Collaborator

👌 Experiment pr-139042 created and queued.
🤖 Automatically detected try build 4dba42b
🔍 You can check out the queue and this experiment's details.

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot craterbot added S-waiting-on-crater Status: Waiting on a crater run to be completed. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Mar 28, 2025
@tmiasko
Copy link
Contributor

tmiasko commented Mar 28, 2025

Pull request doesn't resolve #139042 (comment) yet and mir-opt-level=0 is never used by default. Are we getting ahead of ourselves with crater?

@RalfJung
Copy link
Member

Yeah I think we do.
@craterbot cancel

The PR first needs to be changed to never do this SwitchInt removal in SimplifyCfg::Initial, SimplifyCfg::PromoteConsts, SimplifyCfg::RemoveFalseEdges, SimplifyCfg::PostAnalysis. And for SimplifyCfg::PreOptimizations it should likely be gated on mir_opt_level. Maintaining that list seems fragile so hopefully there is a better way... maybe it is checking for the phase and the opt_level.

Alternatively, instead of checking the opt level, we could have a separate flag. We already have a similar problem with RemovePlaceMention, which is now gated on the mir_keep_place_mention Z-flag. We could instead have a mir_preserve_ub Z-flag that controls both RemovePlaceMention and SimplifyCfg.

@craterbot
Copy link
Collaborator

🗑️ Experiment pr-139042 deleted!

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot craterbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-crater Status: Waiting on a crater run to be completed. labels Mar 28, 2025
@compiler-errors compiler-errors force-pushed the do-not-optimize-switchint branch from 6bad178 to 01ee75d Compare March 30, 2025 19:01
@compiler-errors
Copy link
Member Author

@bors try

@bors
Copy link
Collaborator

bors commented Mar 30, 2025

⌛ Trying commit 01ee75d with merge 5fee451...

bors added a commit to rust-lang-ci/rust that referenced this pull request Mar 30, 2025
…chint, r=<try>

Do not remove trivial `SwitchInt` with mir-opt-level=0

When mir-opt-level=0, do not optimize out `SwitchInt` terminators that all have the same terminator since it may remove a read which affects miri's ability to detect UB on that operand.

cc `@RalfJung`

Fixes rust-lang/miri#4237

This affects some tests... I guess I could mark them as `mir-opt-level=1`? Not sure.
@rust-log-analyzer

This comment has been minimized.

@compiler-errors
Copy link
Member Author

Ah, on vacation 🌴
r? RalfJung or reassign

@compiler-errors compiler-errors changed the title Do not remove trivial SwitchInt with mir-opt-level=0 Do not remove trivial SwitchInt in analysis MIR Apr 8, 2025
@compiler-errors compiler-errors force-pushed the do-not-optimize-switchint branch from e5531b3 to 3ee62a9 Compare April 8, 2025 21:09
@traviscross
Copy link
Contributor

I buy it.

@rfcbot fcp merge

@rfcbot
Copy link
Collaborator

rfcbot commented Apr 9, 2025

Team member @traviscross has proposed to merge this. The next step is review by the rest of the tagged team members:

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns.
See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Apr 9, 2025
@tmandry
Copy link
Member

tmandry commented Apr 9, 2025

@rfcbot reviewed

@tmandry
Copy link
Member

tmandry commented Apr 9, 2025

@bors rollup=never due to potential perf impacts

@rfcbot rfcbot added final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. and removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. labels Apr 9, 2025
@rfcbot
Copy link
Collaborator

rfcbot commented Apr 9, 2025

🔔 This is now entering its final comment period, as per the review above. 🔔

@compiler-errors
Copy link
Member Author

There should be no perf impacts here, but we can just test that rather than guessing 😄

@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Apr 9, 2025
bors added a commit to rust-lang-ci/rust that referenced this pull request Apr 9, 2025
…chint, r=<try>

Do not remove trivial `SwitchInt` in analysis MIR

This PR ensures that we don't prematurely remove trivial `SwitchInt` terminators which affects both the borrow-checking and runtime semantics (i.e. UB) of the code. Previously the `SimplifyCfg` optimization was removing `SwitchInt` terminators when they was "trivial", i.e. when all arms branched to the same basic block, even if that `SwitchInt` terminator had the side-effect of reading an operand which (for example) may not be initialized or may point to an invalid place in memory.

This behavior is unlike all other optimizations, which are only applied after "analysis" (i.e. borrow-checking) is finished, and which Miri disables to make sure the compiler doesn't silently remove UB.

Fixing this code "breaks" (i.e. unmasks) code that used to borrow-check but no longer does, like:

```rust
fn foo() {
    let x;
    let (0 | _) = x;
}
```

This match expression should perform a read because `_` does not shadow the `0` literal pattern, and the compiler should have to read the match scrutinee to compare it to 0. I've checked that this behavior does not actually manifest in practice via a crater run which came back clean: rust-lang#139042 (comment)

As a side-note, it may be tempting to suggest that this is actually a good thing or that we should preserve this behavior. If we wanted to make this work (i.e. trivially optimize out reads from matches that are redundant like `0 | _`), then we should be enabling this behavior *after* fixing this. However, I think it's kinda unprincipled, and for example other variations of the code don't even work today, e.g.:

```rust
fn foo() {
    let x;
    let (0.. | _) = x;
}
```
@bors
Copy link
Collaborator

bors commented Apr 9, 2025

⌛ Trying commit 3ee62a9 with merge 2f722cba6738caa1d971f6ef4229d7d86891b5af...

@scottmcm
Copy link
Member

scottmcm commented Apr 9, 2025

Agreed this is something that should never have worked, and it's just a bug in mir transform.

@bors
Copy link
Collaborator

bors commented Apr 9, 2025

☀️ Try build successful - checks-actions
Build commit: 2f722cb (2f722cba6738caa1d971f6ef4229d7d86891b5af)

@rust-timer

This comment has been minimized.

@rust-timer
Copy link
Collaborator

Finished benchmarking commit (2f722cb): comparison URL.

Overall result: no relevant changes - no action needed

Benchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. While you can manually mark this PR as fit for rollup, we strongly recommend not doing so since this PR may lead to changes in compiler perf.

@bors rollup=never
@rustbot label: -S-waiting-on-perf -perf-regression

Instruction count

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

Max RSS (memory usage)

Results (primary 0.7%, secondary -8.1%)

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)
6.3% [6.3%, 6.3%] 1
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-4.8% [-4.8%, -4.8%] 1
Improvements ✅
(secondary)
-8.1% [-8.1%, -8.1%] 1
All ❌✅ (primary) 0.7% [-4.8%, 6.3%] 2

Cycles

Results (secondary 2.6%)

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)
2.6% [2.2%, 3.0%] 2
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

Binary size

Results (primary -0.1%)

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.1% [-0.1%, -0.1%] 1
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) -0.1% [-0.1%, -0.1%] 1

Bootstrap: 779.721s -> 780.808s (0.14%)
Artifact size: 366.13 MiB -> 366.15 MiB (0.01%)

@rustbot rustbot removed the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Apr 9, 2025
@compiler-errors
Copy link
Member Author

@bors rollup-

@saethlin saethlin assigned saethlin and unassigned RalfJung Apr 9, 2025
@saethlin
Copy link
Member

saethlin commented Apr 9, 2025

My return from vacation hasn't processed yet but I'm quite interested in this PR so I've changed the assignment manually.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. I-lang-easy-decision Issue: The decision needed by the team is conjectured to be easy; this does not imply nomination I-lang-nominated Nominated for discussion during a lang team meeting. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.