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

New mir-opt pass to simplify gotos with const values #77486

Closed
wants to merge 12 commits into from

Conversation

simonvandel
Copy link
Contributor

Fixes #77355

This pass optimizes the following sequence

bb2: {
    _2 = const true;
    goto -> bb3;
}

bb3: {
    switchInt(_2) -> [false: bb4, otherwise: bb5];
}

into

bb2: {
    _2 = const true;
    goto -> bb5;
}

Note that from the diff it seems to invalidate some of the other passes - not sure how best to handle that.

@rust-highfive
Copy link
Collaborator

r? @lcnr

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

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Oct 3, 2020
@simonvandel
Copy link
Contributor Author

simonvandel commented Oct 3, 2020

I can't seem to reproduce the CI failing. ./x.py test passes all tests locally.

@lcnr
Copy link
Contributor

lcnr commented Oct 3, 2020

This optimization looks good to me, it should be quite fast and seems to get triggered suprisingly frequently from what I can tell

r? @oli-obk though, won't review mir opts myself

@rust-highfive rust-highfive assigned oli-obk and unassigned lcnr Oct 3, 2020
@tmiasko
Copy link
Contributor

tmiasko commented Oct 3, 2020

The CI failure looks like miscompilation of rustc, you might need to test the second stage: ./x.py test --stage 2 to reproduce.

@tmiasko
Copy link
Contributor

tmiasko commented Oct 3, 2020

The report_inline_asm function is being miscompiled (it unconditionally sets cookie to zero). The SimplifyBranchSame seems to be the optimization responsible, in particular the use of zip without checking that the number of statements is the same looks very suspicious:

bb_l.statements.iter().zip(&bb_r.statements).try_fold(StatementEquality::TrivialEqual, |acc,(l,r)| {

--- rustc_codegen_llvm.back-write-report_inline_asm.005-010.SimplifyBranchSame.before.mir
+++ rustc_codegen_llvm.back-write-report_inline_asm.005-010.SimplifyBranchSame.after.mir
@@ -1,11 +1,11 @@
-// MIR for `report_inline_asm` before SimplifyBranchSame
+// MIR for `report_inline_asm` after SimplifyBranchSame
 
 fn report_inline_asm(_1: &CodegenContext<LlvmCodegenBackend>, _2: std::string::String, _3: llvm_::ffi::DiagnosticLevel, _4: u32, _5: Option<(std::string::String, Vec<InnerSpan>)>) -> () {
     debug cgcx => _1;                    // in scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:244:5: 244:9
     debug msg => _2;                     // in scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:245:5: 245:8
     debug level => _3;                   // in scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:246:5: 246:10
     debug cookie => _4;                  // in scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:247:5: 247:15
     debug source => _5;                  // in scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:248:5: 248:11
     let mut _0: ();                      // return place in scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:249:3: 249:3
     let mut _6: isize;                   // in scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:253:27: 253:35
     let _7: rustc_errors::Level;         // in scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:256:9: 256:14
@@ -15,80 +15,76 @@
     let mut _11: u32;                    // in scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:261:40: 261:53
     let mut _12: std::string::String;    // in scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:261:55: 261:58
     let mut _13: rustc_errors::Level;    // in scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:261:60: 261:65
     let mut _14: std::option::Option<(std::string::String, std::vec::Vec<rustc_span::InnerSpan>)>; // in scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:261:67: 261:73
     scope 1 {
         debug level => _7;               // in scope 1 at compiler/rustc_codegen_llvm/src/back/write.rs:256:9: 256:14
     }
 
     bb0: {
         _6 = discriminant(((*_1).2: rustc_session::config::Lto)); // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:253:27: 253:35
-        switchInt(move _6) -> [1_isize: bb3, 3_isize: bb3, otherwise: bb2]; // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:253:27: 253:35
+        goto -> bb2;                     // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:253:27: 253:35
     }
 
     bb1 (cleanup): {
         resume;                          // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:243:1: 262:2
     }
 
     bb2: {
-        goto -> bb4;                     // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:253:5: 255:6
-    }
-
-    bb3: {
         _4 = const 0_u32;                // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:254:9: 254:19
-        goto -> bb4;                     // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:253:5: 255:6
+        goto -> bb3;                     // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:253:5: 255:6
     }
 
-    bb4: {
+    bb3: {
         StorageLive(_7);                 // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:256:9: 256:14
         _8 = discriminant(_3);           // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:257:9: 257:37
-        switchInt(move _8) -> [0_isize: bb6, 1_isize: bb7, 2_isize: bb8, 3_isize: bb8, otherwise: bb5]; // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:257:9: 257:37
+        switchInt(move _8) -> [0_isize: bb5, 1_isize: bb6, 2_isize: bb7, 3_isize: bb7, otherwise: bb4]; // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:257:9: 257:37
     }
 
-    bb5: {
+    bb4: {
         unreachable;                     // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:256:23: 256:28
     }
 
-    bb6: {
+    bb5: {
         discriminant(_7) = 2;            // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:257:41: 257:53
-        goto -> bb9;                     // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:256:17: 260:6
+        goto -> bb8;                     // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:256:17: 260:6
     }
 
-    bb7: {
+    bb6: {
         discriminant(_7) = 3;            // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:258:43: 258:57
-        goto -> bb9;                     // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:256:17: 260:6
+        goto -> bb8;                     // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:256:17: 260:6
     }
 
-    bb8: {
+    bb7: {
         discriminant(_7) = 4;            // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:259:72: 259:83
-        goto -> bb9;                     // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:256:17: 260:6
+        goto -> bb8;                     // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:256:17: 260:6
     }
 
-    bb9: {
+    bb8: {
         StorageLive(_9);                 // scope 1 at compiler/rustc_codegen_llvm/src/back/write.rs:261:5: 261:74
         StorageLive(_10);                // scope 1 at compiler/rustc_codegen_llvm/src/back/write.rs:261:5: 261:22
         _10 = &((*_1).20: rustc_codegen_ssa::back::write::SharedEmitter); // scope 1 at compiler/rustc_codegen_llvm/src/back/write.rs:261:5: 261:22
         StorageLive(_11);                // scope 1 at compiler/rustc_codegen_llvm/src/back/write.rs:261:40: 261:53
         _11 = _4;                        // scope 1 at compiler/rustc_codegen_llvm/src/back/write.rs:261:40: 261:46
         StorageLive(_12);                // scope 1 at compiler/rustc_codegen_llvm/src/back/write.rs:261:55: 261:58
         _12 = move _2;                   // scope 1 at compiler/rustc_codegen_llvm/src/back/write.rs:261:55: 261:58
         StorageLive(_13);                // scope 1 at compiler/rustc_codegen_llvm/src/back/write.rs:261:60: 261:65
         _13 = _7;                        // scope 1 at compiler/rustc_codegen_llvm/src/back/write.rs:261:60: 261:65
         StorageLive(_14);                // scope 1 at compiler/rustc_codegen_llvm/src/back/write.rs:261:67: 261:73
         _14 = move _5;                   // scope 1 at compiler/rustc_codegen_llvm/src/back/write.rs:261:67: 261:73
-        _9 = SharedEmitter::inline_asm_error(move _10, move _11, move _12, move _13, move _14) -> [return: bb10, unwind: bb1]; // scope 1 at compiler/rustc_codegen_llvm/src/back/write.rs:261:5: 261:74
+        _9 = SharedEmitter::inline_asm_error(move _10, move _11, move _12, move _13, move _14) -> [return: bb9, unwind: bb1]; // scope 1 at compiler/rustc_codegen_llvm/src/back/write.rs:261:5: 261:74
                                          // mir::Constant
                                          // + span: compiler/rustc_codegen_llvm/src/back/write.rs:261:23: 261:39
                                          // + literal: Const { ty: for<'r> fn(&'r rustc_codegen_ssa::back::write::SharedEmitter, u32, std::string::String, rustc_errors::Level, std::option::Option<(std::string::String, std::vec::Vec<rustc_span::InnerSpan>)>) {rustc_codegen_ssa::back::write::SharedEmitter::inline_asm_error}, val: Value(Scalar(<ZST>)) }
     }
 
-    bb10: {
+    bb9: {
         StorageDead(_14);                // scope 1 at compiler/rustc_codegen_llvm/src/back/write.rs:261:73: 261:74
         StorageDead(_13);                // scope 1 at compiler/rustc_codegen_llvm/src/back/write.rs:261:73: 261:74
         StorageDead(_12);                // scope 1 at compiler/rustc_codegen_llvm/src/back/write.rs:261:73: 261:74
         StorageDead(_11);                // scope 1 at compiler/rustc_codegen_llvm/src/back/write.rs:261:73: 261:74
         StorageDead(_10);                // scope 1 at compiler/rustc_codegen_llvm/src/back/write.rs:261:73: 261:74
         StorageDead(_9);                 // scope 1 at compiler/rustc_codegen_llvm/src/back/write.rs:261:74: 261:75
         _0 = const ();                   // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:249:3: 262:2
         StorageDead(_7);                 // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:262:1: 262:2
         return;                          // scope 0 at compiler/rustc_codegen_llvm/src/back/write.rs:262:2: 262:2
     }

@simonvandel
Copy link
Contributor Author

The SimplifyBranchSame seems to be the optimization responsible, in particular the use of zip without checking that the number of statements is the same looks very suspicious:

Yeah, that does look sketchy. I'll open a separate PR for that.

@simonvandel
Copy link
Contributor Author

CI is green now with a miscompile fix for SimplifyBranchSame included.

@tmiasko
Copy link
Contributor

tmiasko commented Oct 4, 2020

@Mark-Simulacrum I would suggest backporting fix to SimplifyBranchSame from this PR to beta.

@Mark-Simulacrum
Copy link
Member

I think this is just a new optimization? IIRC we recently disabled some passes on beta, I would rather not backport mir opt changes, rather disabling them if they're buggy.

@tmiasko
Copy link
Contributor

tmiasko commented Oct 4, 2020

This is a bug in an existing optimization, affecting beta but not stable. Fix is in 2da1c13. Disabling the optimization altogether also sounds fine to me.

@Mark-Simulacrum
Copy link
Member

Could you extract that to a separate PR so we can land it (more) quickly? cc @rust-lang/wg-mir-opt -- it sounds like there's potentially another soundness bug with the SimplifyBranchSame pass? I've not looked at the test in detail though

@bors
Copy link
Contributor

bors commented Oct 4, 2020

☔ The latest upstream changes (presumably #77430) made this pull request unmergeable. Please resolve the merge conflicts.

Note that reviewers usually do not review pull requests until merge conflicts are resolved! Once you resolve the conflicts, you should change the labels applied by bors to indicate that your PR is ready for review. Post this as a comment to change the labels:

@rustbot modify labels: +S-waiting-on-review -S-waiting-on-author

@tmiasko
Copy link
Contributor

tmiasko commented Oct 4, 2020

I opened #77549.

bors added a commit to rust-lang-ci/rust that referenced this pull request Oct 5, 2020
…li-obk

 Fix miscompile in SimplifyBranchSame

Cherry-picked from rust-lang#77486, but with a different test case that used to be compiled incorrectly on both master & beta branches.
@simonvandel
Copy link
Contributor Author

Rebased on master. Ready for review/merge.

As the test shows, this seems to fix #77355. However, running RUSTC_LOG=rustc_mir::transform::const_goto ./x.py build --stage 2 > stage2.log yields very few instances of "SUCCESS:", which was a bit surprising to me.

// if we applied optimizations, we potentially have some cfg to cleanup to
// make it easier for further passes
if should_simplify {
simplify_cfg(body);
Copy link
Contributor

Choose a reason for hiding this comment

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

cc @rust-lang/wg-mir-opt we need to figure out a way to dump the mir of an optimizations both before its internal cleanup and after. The test diffs are very noisy if we do the cleanup together with the optimization.

Maybe we could not do these immediate cleanups, and instead leave a flag in the mir body that states that it needs a simplification and the following cleanup optimizations just skip if the flag is not set?

The previous test's diff was changed by the const_goto pass such that the Ne(_1, false) pattern did not show anymore
@simonvandel
Copy link
Contributor Author

Rebased and resolved review comments.
@rustbot modify labels: +S-waiting-on-review -S-waiting-on-author

@bors
Copy link
Contributor

bors commented Oct 27, 2020

☔ The latest upstream changes (presumably #77876) made this pull request unmergeable. Please resolve the merge conflicts.

Note that reviewers usually do not review pull requests until merge conflicts are resolved! Once you resolve the conflicts, you should change the labels applied by bors to indicate that your PR is ready for review. Post this as a comment to change the labels:

@rustbot modify labels: +S-waiting-on-review -S-waiting-on-author

@oli-obk oli-obk 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 Nov 3, 2020
@camelid camelid added A-mir-opt Area: MIR optimizations 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-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Nov 20, 2020
@camelid
Copy link
Member

camelid commented Nov 20, 2020

@simonvandel Could you resolve the conflicts or would you like a new review first?

@simonvandel
Copy link
Contributor Author

@camelid it probably makes most sense to fix the conflicts first. I haven't looked at this for a while, so I'll need to see what actually remains to do.

@JohnCSimon
Copy link
Member

ping from triage - @simonvandel can you post your status on this PR and fix the merge conflicts?

@Dylan-DPC-zz
Copy link

@simonvandel thanks for taking the time to contribute. I have to close this due to inactivity. If you wish and you have the time you can open a new PR with these changes and we'll take it from there. Thanks

@Dylan-DPC-zz Dylan-DPC-zz added S-inactive Status: Inactive and waiting on the author. This is often applied to closed PRs. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Dec 8, 2020
@simonvandel
Copy link
Contributor Author

Hi @Dylan-DPC
I fixed the merge conflicts and the code on my branch is ready for feedback again. Can this PR be reopened or should I create a new PR? Reusing the PR could make sense to continue discussions etc.

@bjorn3
Copy link
Member

bjorn3 commented Dec 29, 2020

You can re-open it, but you must first force-push the commit at which the branch was when you closed it. This is a github limitation.

@Dylan-DPC-zz
Copy link

@simonvandel better to create a new pr

bors added a commit to rust-lang-ci/rust that referenced this pull request Feb 24, 2021
New mir-opt pass to simplify gotos with const values (reopening rust-lang#77486)

Reopening PR rust-lang#77486

Fixes rust-lang#77355

This pass optimizes the following sequence
```rust
bb2: {
    _2 = const true;
    goto -> bb3;
}

bb3: {
    switchInt(_2) -> [false: bb4, otherwise: bb5];
}
```
into
```rust
bb2: {
    _2 = const true;
    goto -> bb5;
}
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-mir-opt Area: MIR optimizations S-inactive Status: Inactive and waiting on the author. This is often applied to closed PRs.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

if matches!(...) results in worse mir than if let ...