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

Fixed const propagation not working in a particular scenario #71298

Closed
wants to merge 7 commits into from
Closed

Fixed const propagation not working in a particular scenario #71298

wants to merge 7 commits into from

Conversation

felix91gr
Copy link
Contributor

As shown here, constant propagation is missing at least the following scenario:

let x = 0;
let y = f(x);

It is currently being const propagated like this:

let x = 0;
let y = f(x);

And it should behave like this:

let x = 0;
let y = f(0);

This PR addresses this scenario, and changes the result of two other mir-opt tests. I will need help figuring out if those tests cases are being further optimized, or if I ran into a regression.

(CC @oli-obk)

@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 @matthewjasper (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 Apr 18, 2020
@felix91gr
Copy link
Contributor Author

felix91gr commented Apr 18, 2020

Some notes on this PR for whoever is reading and wants some explanations about the changes:

  1. https://github.com/rust-lang/rust/pull/71298/files#diff-9e103702275cbef342c2decd3395bf3bR759

This was added in order to have LocalKind information outside of the context of CanConstProp::check, which was important to this commit.

  1. https://github.com/rust-lang/rust/pull/71298/files#diff-9e103702275cbef342c2decd3395bf3bR765

I originally made LocalKind implement Clone with a derive macro, but for some reason that broke rustc. I reverted those changes, and implemented a cloning function alongside the code that needed it, and that unbroke the compiler.

No idea why the Clone derive made it break, though.

  1. https://github.com/rust-lang/rust/pull/71298/files#diff-9e103702275cbef342c2decd3395bf3bL775

Not being able to const-prop over Vars was too much of a restriction. Lifting it was necessary to make this test case pass.

@felix91gr
Copy link
Contributor Author

Sorry, I'm super rusty with rustc commands (pun not intended). Now all my changes are up.

  1. https://github.com/rust-lang/rust/pull/71298/files#diff-1542539b0bf77ee645d84f829c7d8605L25

This test changed its output. It seems to me that it's now more performant (basically, it's being optimized better now). Please help me here though, because I'm not 100% sure.

  1. https://github.com/rust-lang/rust/pull/71298/files#diff-c34947eb53c76084c6a717a62550884f

I definitely need help here. These MIR changes are way too complicated for my current understanding of MIR >.<

Did I cause a regression, or is this test also optimizing better now?

@felix91gr
Copy link
Contributor Author

Missed rustfmt. Brb with correct format

@rust-highfive

This comment has been minimized.

@felix91gr
Copy link
Contributor Author

I'm working on a fix for the errors shown in CI. Will be back as soon as my computer finishes running the test suite again!

Copy link
Member

@wesleywiser wesleywiser left a comment

Choose a reason for hiding this comment

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

Thanks for the PR! I left a few comments. Let me know if you have any questions.

src/librustc_mir/transform/const_prop.rs Outdated Show resolved Hide resolved
src/librustc_mir/transform/const_prop.rs Outdated Show resolved Hide resolved
@wesleywiser
Copy link
Member

r? @wesleywiser

@felix91gr
Copy link
Contributor Author

Kay, uploaded the changes that let the old tests pass again (const-prop was now detecting UB at compile time in 3 of them <3)

@felix91gr
Copy link
Contributor Author

What IS that CI failure? Its output is so weird. It somehow expects ints with 8 0's instead of 16 0's. Is this a type inference problem, or is it perhaps 32 vs 64 bit thing? 🤔

@rust-highfive

This comment has been minimized.

@wesleywiser
Copy link
Member

Yeah, it looks like the tests are failing on 32-bit ARM. For the src/test/mir-opt/const_prop/usize_literal_propagation.rs and src/test/mir-opt/simplify-arm-identity.rs, I think you just need to add an annotation to tell the test harness to use separate outputs on 32 and 64 bit:

// EMIT_MIR_FOR_EACH_BIT_WIDTH

@wesleywiser
Copy link
Member

Sorry, I forgot, you'll also need to --bless the tests to get the files to be generated. You'll need to do that for both 64-bit and 32-bit architectures. Something like this should work:

For 64-bit:

./x.py test --stage 1 -i --bless src/test/mir-opt

For 32-bit:

./x.py test --stage 1 -i --bless --target i686-unknown-linux-gnu src/test/mir-opt

(Assuming you're on Linux. You may also need to install some 32-bit compatibility libraries like this mentions.)

@felix91gr
Copy link
Contributor Author

Sorry, I forgot, you'll also need to --bless the tests to get the files to be generated. You'll need to do that for both 64-bit and 32-bit architectures. Something like this should work:

For 64-bit:

./x.py test --stage 1 -i --bless src/test/mir-opt

For 32-bit:

./x.py test --stage 1 -i --bless --target i686-unknown-linux-gnu src/test/mir-opt

(Assuming you're on Linux. You may also need to install some 32-bit compatibility libraries like this mentions.)

Got it! (I think, that seems fairly straightforward). And yes, I'm on Linux :3

@bors

This comment has been minimized.

src/librustc_mir/transform/const_prop.rs Outdated Show resolved Hide resolved
src/librustc_mir/transform/const_prop.rs Outdated Show resolved Hide resolved
src/librustc_mir/transform/const_prop.rs Outdated Show resolved Hide resolved
src/librustc_mir/transform/const_prop.rs Outdated Show resolved Hide resolved
@wesleywiser
Copy link
Member

You might want to rebase when you get a chance so that CI will run and we can see if this improved the failing UI tests (it should have).

@rust-highfive

This comment has been minimized.

@felix91gr
Copy link
Contributor Author

felix91gr commented Apr 21, 2020

(I'm recompiling everthing because the repo got into some weird state and I decided to reset git's memory, in a way, by cloning again at these commits) (therefore, I'm basically offline until I manage to recompile everything :v)

@felix91gr
Copy link
Contributor Author

Kay, re-blessed those two tests. I checked the output manually and whatever they're doing, I think those programs are gonna have the same output as their predecessors. Now on to checking if the rest of the test suite is a-ok

@felix91gr
Copy link
Contributor Author

Oh wow, those failures are weird. What do they even mean?

@felix91gr
Copy link
Contributor Author

Okay, I'll have to generate the MIR for those snippets, or I'll have no idea of what to do. They look very arcane to me (like read_unaligned and stuff, or like let a = a as *const isize as usize, what does that line even mean?)

But hey, on the good side: only 2 tests failed.

@felix91gr
Copy link
Contributor Author

Oh, okay, that's not entirely accurate. Only 2 fail, but on the --stage 1 command. It seems that the full ./x.py test run has a lot more failing tests. That's great, I can probably debug some of those :D

@felix91gr
Copy link
Contributor Author

Okay, wait... I don't get it. There are errors for which the test complains that the stderr changed, but it hasn't really changed? What is happening. This is so weird.

For example, the first failing test gives this output. And for good measure, these are the contents of the .stderr file that was saved after running it.

They're all the same? What's going on? 🤔

@felix91gr
Copy link
Contributor Author

(@wesleywiser?)

@wesleywiser
Copy link
Member

I also am very confused. What may be happening is that there's a miscompilation introduced by these changes which is affecting the test harness itself and causing strange issues like this.

@wesleywiser
Copy link
Member

@felix91gr Would you mind opening another PR with just commits 1, 2 and 3 in it? We can test there if the issue is propagating vars or the function args change.

@felix91gr
Copy link
Contributor Author

Sure! Lemme make a couple of new branches, will be back with two testing PRs :3

@felix91gr
Copy link
Contributor Author

The two changes are up!

What I did to dodge the aliasing of self in the call to remove_const was basically to convert remove_const into an associated function. That way, we give it only the borrows it actually needs - and can safely iterate over the BitSet without having to clone it.

@wesleywiser
Copy link
Member

@bors try @rust-timer queue

@rust-timer
Copy link
Collaborator

Awaiting bors try build completion

@bors
Copy link
Contributor

bors commented Apr 26, 2020

⌛ Trying commit 6e586b8 with merge 075d9ec4e3198749f4bb6e6f3bb2b29b8eceb815...

@bors
Copy link
Contributor

bors commented Apr 26, 2020

☀️ Try build successful - checks-azure
Build commit: 075d9ec4e3198749f4bb6e6f3bb2b29b8eceb815 (075d9ec4e3198749f4bb6e6f3bb2b29b8eceb815)

@rust-timer
Copy link
Collaborator

Queued 075d9ec4e3198749f4bb6e6f3bb2b29b8eceb815 with parent 0862458, future comparison URL.

@rust-timer
Copy link
Collaborator

Finished benchmarking try commit 075d9ec4e3198749f4bb6e6f3bb2b29b8eceb815, comparison URL.

@oli-obk
Copy link
Contributor

oli-obk commented Apr 26, 2020

All of that regression time is spent in LTO, because we go from 16 codegen units to 44!

So technically this PR is now an improvement, because it allows rustc to split the crate into more codegen units?

Idk... is optimizing for LTO compilation speed actually something we care about?

@felix91gr
Copy link
Contributor Author

felix91gr commented Apr 26, 2020

All of that regression time is spent in LTO, because we go from 16 codegen units to 44!

Yay!

So technically this PR is now an improvement, because it allows rustc to split the crate into more codegen units?

Wait, I'm not sure I'm understanding you correctly. You mean that:

  • We are now generating sort of an excess of codegen units (which need to be re-tuned in the codegen step because it's a local maxima as Wesley pointed out),
  • Or that generating extra codegen units allows the compiler to optimize the code better due to LTO?

Idk... is optimizing for LTO compilation speed actually something we care about?

Probably, but not at this stage. Specially if we are to fully decouple the compiler into libraries in the future. My guess is that we want to optimize LTO compilation speed, but only starting at the codegen stage and not before.

Because MIR is at the end of the day going to have to do what MIR's going to have to do, right? Be a place for optimizations that benefit all Rust backends, a place for static analysis and a place for compile-time computation, right?

I think we are doing the right thing here :)

@wesleywiser
Copy link
Member

So technically this PR is now an improvement, because it allows rustc to split the crate into more codegen units?

I think what's actually happening is that before, the patched incremental test had 16 codegen units get regenerated and now 44 are being regenerated. I believe the total number of codgen units is directly tied to the number of modules in the crate which for our perf tests, is always the same. So this is a clear regression.


I think unfortunately at this time we can't merge this exactly as is because of the large incremental regression and no correspondingly large improvements. However, this PR definitely moves us closer to our end goal of high-quality MIR optimizations, it's just currently hampered by other parts of the compiler. I'm also very sympathetic to the clippy use case the original issue there completed.

At this time, we can do this:

  1. Merge Const-prop bugfix: only add propagation inside own block for user variables #71518 as-is after doing one last perf run to make sure it's still performance neutral.

  2. Merge the function call propagation under mir-opt-level=2. Rust code normally compiles under mir-opt-level=1 which is the default but nightly compilers can do "unstable" MIR optimizations which are enabled at levels 2 & 3. Since this code works fine but causes performance regressions, we'll gate it under mir-opt-level=2 being requested.

Clippy unfortunately won't see the effects of point 2 because it doesn't use unstable optimizations. However, it should be relatively easy to add some logic to clippy that does a very simple version of const-prop and would resolve your issue. After point 1 is merged, I think all you will need to do is look for any locals in a Body which are assigned to once and remember the right-hand side value of their assignment. Then when inspecting function calls, substitute known values for those locals. I'm not really familiar with clippy internals so @oli-obk will have to guide you there but I think that is very do-able.

Let me know if you have any questions and I'll be happy to try to answer them 🙂

@felix91gr
Copy link
Contributor Author

However, this PR definitely moves us closer to our end goal of high-quality MIR optimizations, it's just currently hampered by other parts of the compiler. I'm also very sympathetic to the clippy use case the original issue there completed.

💓

Merge the function call propagation under mir-opt-level=2. Rust code normally compiles under mir-opt-level=1 which is the default but nightly compilers can do "unstable" MIR optimizations which are enabled at levels 2 & 3. Since this code works fine but causes performance regressions, we'll gate it under mir-opt-level=2 being requested.

How does that work? I'm guessing clippy can't call upon a miri instance to run opt-level-2 optimizations either, right? Like, basically that opt. is 100% blocked from stable venues until upgraded to opt-level-1.

Another idea is that maybe this can be gated in Clippy as a nightly-only feature, as well. So that we don't write new code just because we want the feature to appear on stable right now.

But if you say it's actually easy, maybe we can give it a try. @oli-obk how straightforward do you think it'll be to implement the lint if we have (1) but not (2) on hand?

@felix91gr
Copy link
Contributor Author

felix91gr commented Apr 26, 2020

Let me know if you have any questions and I'll be happy to try to answer them 🙂

@wesleywiser yes!

  1. How can I help with the codegen issue? Is there a working group or something like that?

  2. How long do you reckon it will take us to sort this out? Like, if we're pessimistic and multiply the time x8 or so just to be sure to go over- and not under-.

  3. Oli mentioned that there will be a meeting about this. Is it going to be public? Can I attend? Even to just listen to the conversation is enough, although I'd love to be able to ask questions if they arise.

  4. Do we have a list of MIR-opts we want to implement, somewhere? I wanna keep working on this. Either in docs (which as I understand stuff, I plan on writing them down), in book keeping or in implementation.

@wesleywiser
Copy link
Member

  1. How can I help with the codegen issue? Is there a working group or something like that?

We're not actually sure yet. We've noticed more performance issues cropping up because of the current algorithm so it's something we need to look into soon. Our next step is to hold a planning meeting with the rest of the compiler team. I'll be sure to ping you when I fill out the meeting issue template so you know when that happens!

  1. How long do you reckon it will take us to sort this out?

I'm not really sure. I suspect there's some improvements to the current algorithm we could find and land in the next few months. Finding a significantly better algorithm will probably take much longer.

  1. Oli mentioned that there will be a meeting about this. Is it going to be public? Can I attend? Even to just listen to the conversation is enough, although I'd love to be able to ask questions if they arise.

Yes, absolutely! All of the compiler team triage and design meetings happen on the Rust Zulip instance. Triage meetings are Thursday mornings at 10am US Eastern time. Design meetings are Friday mornings at 10am US Eastern time. Anyone is welcome to attend either.

  1. Do we have a list of MIR-opts we want to implement, somewhere? I wanna keep working on this. Either in docs (which as I understand stuff, I plan on writing them down), in book keeping or in implementation.

There's a few places I'd look:

bors added a commit to rust-lang-ci/rust that referenced this pull request Apr 29, 2020
…k_prop, r=wesleywiser

Const-prop bugfix: only add propagation inside own block for user variables

A testing spinoff of rust-lang#71298. This one only adds the const-prop for locals that are user variables.
@felix91gr
Copy link
Contributor Author

Closed since #71697 takes the baton on the conversation and remaining points to implement ✨

@felix91gr felix91gr closed this Apr 30, 2020
@felix91gr felix91gr deleted the const_prop_bugfix branch April 30, 2020 17:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants