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 optimization pass to reduce branches on match of tuples of enums #75119

Merged
merged 9 commits into from
Sep 20, 2020

Conversation

simonvandel
Copy link
Contributor

Fixes #68867 by adding a new pass that turns something like

let x: Option<()>;
let y: Option<()>;
match (x,y) {
    (Some(_), Some(_)) => {0},
    _ => {1}
}

into something like

let x: Option<()>;
let y: Option<()>;
let discriminant_x = // get discriminant of x
let discriminant_y = // get discriminant of x
if discriminant_x != discriminant_y {1} else {0}

The opt-diffs still have the old basic blocks like

bb3: {
          _8 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early-otherwise-branch-68867.rs:21:21: 21:30
          switchInt(move _8) -> [1_isize: bb7, otherwise: bb2]; // scope 0 at $DIR/early-otherwise-branch-68867.rs:21:21: 21:30
      }

      bb4: {
          _9 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early-otherwise-branch-68867.rs:22:23: 22:34
          switchInt(move _9) -> [2_isize: bb8, otherwise: bb2]; // scope 0 at $DIR/early-otherwise-branch-68867.rs:22:23: 22:34
      }

      bb5: {
          _10 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early-otherwise-branch-68867.rs:23:23: 23:34
          switchInt(move _10) -> [3_isize: bb9, otherwise: bb2]; // scope 0 at $DIR/early-otherwise-branch-68867.rs:23:23: 23:34
      }

These do get removed on later passes. I'm not sure if I should include those passes in the test to make it clear?

@rust-highfive
Copy link
Collaborator

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @varkor (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Aug 3, 2020
@simonvandel simonvandel changed the title New MIR optimization pass to reduce branches on tuples of enums New MIR optimization pass to reduce branches on match of tuples of enums Aug 3, 2020
@varkor
Copy link
Member

varkor commented Aug 3, 2020

This looks great, but unfortunately I don't have time to review something of this size at the moment.

r? @eddyb

@bors try @rust-timer queue

@rust-timer
Copy link
Collaborator

Awaiting bors try build completion

@rust-highfive rust-highfive assigned eddyb and unassigned varkor Aug 3, 2020
@bors
Copy link
Contributor

bors commented Aug 3, 2020

⌛ Trying commit c6982e36a893aeb5647d1c7773bff287acc43a38 with merge e42444c82f1bdc51f3bd1abadcf3f7b2a9e87159...

@bors
Copy link
Contributor

bors commented Aug 3, 2020

☀️ Try build successful - checks-actions, checks-azure
Build commit: e42444c82f1bdc51f3bd1abadcf3f7b2a9e87159 (e42444c82f1bdc51f3bd1abadcf3f7b2a9e87159)

@rust-timer
Copy link
Collaborator

Queued e42444c82f1bdc51f3bd1abadcf3f7b2a9e87159 with parent 829d69b, future comparison URL.

@rust-timer
Copy link
Collaborator

Finished benchmarking try commit (e42444c82f1bdc51f3bd1abadcf3f7b2a9e87159): comparison url.

Benchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. Please note that if the perf results are neutral, you should likely undo the rollup=never given below by specifying rollup- to bors.

Importantly, though, if the results of this run are non-neutral do not roll this PR up -- it will mask other regressions or improvements in the roll up.

@bors rollup=never

@simonvandel
Copy link
Contributor Author

The pass is gated on Mir opt level 3 right now, so I don't think a perf run makes sense without removing that check. @eddyb do you want me to remove it so we can check the impact of perf?

@simonvandel
Copy link
Contributor Author

I don't quite understand how to solve the tests failing.

-	                                           // + val: Value(Scalar(0x0000000000000002))
+	                                           // + val: Value(Scalar(0x00000002))

Running bless locally does not change anything

@Mark-Simulacrum
Copy link
Member

Mark-Simulacrum commented Aug 4, 2020

You likely need to edit the 32-bit test outputs (*.32bit) (which are not run on 64-bit machines) -- I would personally just do so manually. I wish we had a better way to support developers in doing this.

@mati865
Copy link
Contributor

mati865 commented Aug 4, 2020

You also have to add // EMIT_MIR_FOR_EACH_BIT_WIDTH just like in

// EMIT_MIR_FOR_EACH_BIT_WIDTH

@bors
Copy link
Contributor

bors commented Aug 11, 2020

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

@wesleywiser
Copy link
Member

cc @rust-lang/wg-mir-opt

@simonvandel
Copy link
Contributor Author

Rebased on master and squashed

@oli-obk
Copy link
Contributor

oli-obk commented Aug 12, 2020

r? @oli-obk

@rust-highfive rust-highfive assigned oli-obk and unassigned eddyb Aug 12, 2020
+ StorageLive(_11); // scope 0 at $DIR/early_otherwise_branch.rs:7:10: 7:17
+ _11 = Ne(_10, _7); // scope 0 at $DIR/early_otherwise_branch.rs:7:10: 7:17
+ StorageDead(_10); // scope 0 at $DIR/early_otherwise_branch.rs:7:10: 7:17
+ switchInt(move _11) -> [false: bb6, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:7:10: 7:17
Copy link
Contributor

Choose a reason for hiding this comment

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

instead of doing a switch and going to a block that does another switch, you can also compute another comparison:

_42 = Eq(_7, 1_isize)
_43 = BitAnd(_42, _11)

and then switch on _43 to directly go to bb3

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@oli-obk Hmm that works when we have only have 2 targets in the switch, but what if we have 3 targets in the switch like match (x,y,z)? See the 3_element_tuple test. Correct me if I am wrong though

Copy link
Contributor

Choose a reason for hiding this comment

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

That test makes bb2 dead code, and doesn't affect the match part on z at all, so it all seems to work out to me? You aren't optimizing chains longer than 2 elements with your optimization, and even if the optimization is extended to support that, we'd just end up with more of these BitAnds I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's been a while, but I finally looked at it again. I don't think it is that easy to use BitAnd to avoid the extra bb. See https://github.com/rust-lang/rust/pull/75119/files#diff-37f80fe71480f2b22fb8fd1fc103fda4R88 where we need to check both 0 and 1.

@bors
Copy link
Contributor

bors commented Aug 29, 2020

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

@oli-obk
Copy link
Contributor

oli-obk commented Sep 20, 2020

@bors r+

@bors
Copy link
Contributor

bors commented Sep 20, 2020

📌 Commit 13e8e20df3307d8eaf8b6f86e73f313fcaab39b2 has been approved by oli-obk

@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 20, 2020
@oli-obk
Copy link
Contributor

oli-obk commented Sep 20, 2020

@bors r-

@bors bors 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-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Sep 20, 2020
@oli-obk
Copy link
Contributor

oli-obk commented Sep 20, 2020

@bors r+

@bors
Copy link
Contributor

bors commented Sep 20, 2020

📌 Commit 0363694 has been approved by oli-obk

@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-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Sep 20, 2020
@bors
Copy link
Contributor

bors commented Sep 20, 2020

⌛ Testing commit 0363694 with merge 2e0edc0...

@bors
Copy link
Contributor

bors commented Sep 20, 2020

☀️ Test successful - checks-actions, checks-azure
Approved by: oli-obk
Pushing 2e0edc0 to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Sep 20, 2020
@bors bors merged commit 2e0edc0 into rust-lang:master Sep 20, 2020
@rustbot rustbot added this to the 1.48.0 milestone Sep 20, 2020
@ecstatic-morse
Copy link
Contributor

Hi! This PR showed up in the weekly perf triage report. It showed mixed results in instruction counts. While style-servo-opt seems to have sped up, all other benchmarks run slightly slower. Was this expected?

It's possible that there's some improvement in the generated code, although I'd be a bit surprised if LLVM couldn't manage this. If there's no runtime speedup, I think we should consider disabling this by default.

@simonvandel
Copy link
Contributor Author

Hi @ecstatic-morse
This optimization pass was created in response to #68867 which demonstrated a case where LLVM would not make this optimization.
I did a small comparison (see #68867 (comment)) which shows that the final asm that LLVM generates looks a lot cleaner. I have not benchmarked what the actualy speedup is in terms of running the function though.

@wesleywiser
Copy link
Member

It sounds like this might be a good fit for mir-opt-level=2 then as (eventually) we want to make that the default for optimized builds while mir-opt-level=1 will remain the default for non optimized builds.

@ecstatic-morse
Copy link
Contributor

@wesleywiser @simonvandel Was this ever moved to mir-opt-level=2? My personal opinion is that new optimizations should go to mir-opt-level=2 initially unless a) they result in a meaningful performance improvement for most users and b) we are very confident that they don't introduce miscompilations. I don't think this PR fits either of those criteria.

@simonvandel
Copy link
Contributor Author

Hi @ecstatic-morse, no this pass is still running at opt-level=1. Should I create a pr to gate it at 2?

Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Oct 6, 2020
…ise-branch, r=wesleywiser

Move `EarlyOtherwiseBranch` to mir-opt-level 2

cc rust-lang#75119

This didn't have an [effect in most cases](https://perf.rust-lang.org/compare.html?start=81e02708f1f4760244756548981277d5199baa9a&end=2e0edc0f28c5647141bedba02e7a222d3a5dc9c3&stat=instructions:u), and is not trivially sound. Let it bake at `mir-opt-level=2` for a while.

Also, this missed the cutoff for beta, so we'll have to backport this.
r? @wesleywiser
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Bad codegen with simple match statement