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 with mir-opt-level=0 #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.

@bors
Copy link
Collaborator

bors commented Mar 30, 2025

☀️ Try build successful - checks-actions
Build commit: 5fee451 (5fee451ebd94296ea05214e819c311e164b30af6)

@compiler-errors
Copy link
Member Author

@craterbot check

@craterbot
Copy link
Collaborator

👌 Experiment pr-139042 created and queued.
🤖 Automatically detected try build 5fee451
🔍 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-review Status: Awaiting review from the assignee but also interested parties. labels Mar 30, 2025
@craterbot
Copy link
Collaborator

🚧 Experiment pr-139042 is now running

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

@compiler-errors compiler-errors force-pushed the do-not-optimize-switchint branch from 01ee75d to 6fa280b Compare March 30, 2025 23:21
@craterbot
Copy link
Collaborator

🎉 Experiment pr-139042 is completed!
📊 5 regressed and 5 fixed (606182 total)
📰 Open the full report.

⚠️ If you notice any spurious failure please add them to the denylist!
ℹ️ 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 31, 2025
@compiler-errors
Copy link
Member Author

Oh good. Looks like nobody relies on this behavior.

@RalfJung: I took your suggested approach and generalized the flag into -Zmir-preserve-ub and then disabled SwitchInt optimization in the Built phase, since that's the pre-Analysis(Initial) state that we use for borrowck.

I can confirm that causes the example that @tmiasko shared to fail to compile (tho I'll add a test).

I'll write up a better description and then probably FCP this with lang?

@compiler-errors compiler-errors force-pushed the do-not-optimize-switchint branch from 1fed5da to e5531b3 Compare April 7, 2025 19:09
@compiler-errors
Copy link
Member Author

@rustbot label: -T-compiler +T-lang +I-lang-nominated +I-lang-easy-decision

Hi T-lang, this needs an FCP because it's technically breakage. See the above PR description for details; this isn't something we should've ever accepted, IMO.

@rustbot rustbot added 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. T-lang Relevant to the language team, which will review and decide on the PR/issue. and removed T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Apr 7, 2025
@compiler-errors
Copy link
Member Author

r? @saethlin (or perhaps @RalfJung) or reassign, just choosing a name out of a hat.

@rustbot
Copy link
Collaborator

rustbot commented Apr 7, 2025

Could not assign reviewer from: saethlin.
User(s) saethlin are either the PR author, already assigned, or on vacation. Please use r? to specify someone else to assign.

@compiler-errors
Copy link
Member Author

Ah, on vacation 🌴
r? RalfJung or reassign

Comment on lines +32 to +33
//! We must be extremely careful to only apply optimizations that preserve the semantics of the
//! code, since changes here can affect which programs compile in an insta-stable way.
Copy link
Member

Choose a reason for hiding this comment

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

Removing UB is generally considered "preserving the semantics", since UB programs have "all behaviors". So I think the wording here is potentially confusing.

Suggested change
//! We must be extremely careful to only apply optimizations that preserve the semantics of the
//! code, since changes here can affect which programs compile in an insta-stable way.
//! We must be extremely careful to only apply optimizations that preserve UB and
//! all non-determinism, since changes here can affect which programs compile in
//! an insta-stable way. The normal logic that a program with UB can be changed
//! to do anything does not apply to pre-"runtime" MIR!

Copy link
Member

Choose a reason for hiding this comment

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

This could also use a comment explaining the purpose of the test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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.

10 participants