Uh oh!
There was an error while loading. Please reload this page.
rustc_mir: implement an "lvalue reuse" optimization (aka destination propagation aka NRVO). - #46321
rustc_mir: implement an "lvalue reuse" optimization (aka destination propagation aka NRVO).#46321eddyb wants to merge 1 commit into
Conversation
rust-highfive
commented
Nov 28, 2017
r? @pnkfelix (rust_highfive has picked a reviewer for you, use r? to override) |
eddyb
commented
Nov 28, 2017
@bors try |
bors
commented
Nov 28, 2017
rustc_mir: implement an "lvalue reuse" optimization (aka destination propagation aka NRVO). Replaces a chain of moves, such as `a = ...; ... b = move a; ... f(&mut b) ... c = move b`, with the final destination, i.e. only `c = ...; ... f(&mut c); ...` remains (note that borrowing is allowed). **DO NOT MERGE** only for testing atm. Based on #46142.
eddyb
commented
Nov 28, 2017
r? @nikomatsakis |
Is placement new doing the same? Will the only reason to use it then be that you like the syntax? |
@est31 No, this doesn't change execution order, placement has to allocate before evaluating the value into the allocation, while OTOH, this optimizations only reuses stack local destinations when doing so can preserve behavior. |
bors
commented
Nov 28, 2017
☀️ Test successful - status-travis |
Mark-Simulacrum
commented
Nov 28, 2017
Try build added to queue. Will be complete soon (probably a couple hours). |
Assuming the perf results are accurate, this regresses compile times. It'd be a shame if we'd have to disable this, especially the first iteration which seems straight-forward (2 visits over the MIR). Also, do we have any runtime benchmarks yet? |
Mark-Simulacrum
commented
Nov 29, 2017
Hold up, those perf results are invalid. If you rerun try with Line 25 in 827cb0d |
eddyb
commented
Nov 29, 2017
@bors try |
bors
commented
Nov 29, 2017
rustc_mir: implement an "lvalue reuse" optimization (aka destination propagation aka NRVO). Replaces a chain of moves, such as `a = ...; ... b = move a; ... f(&mut b) ... c = move b`, with the final destination, i.e. only `c = ...; ... f(&mut c); ...` remains (note that borrowing works).
bors
commented
Nov 29, 2017
☀️ Test successful - status-travis |
eddyb
commented
Nov 29, 2017
@Mark-Simulacrum I'm not sure my hack worked, I'm probably missing the changes to actually upload the results from #46354. |
Mark-Simulacrum
commented
Nov 29, 2017
I think you need to change this condition to Line 325 in 9de9a00 |
Mark-Simulacrum
commented
Nov 29, 2017
#46354 actually just merged so I think you should be able to just rebase. |
…propagation aka NRVO).
eddyb
commented
Nov 29, 2017
@bors try |
bors
commented
Nov 29, 2017
⌛ Trying commit e87ab56 with merge 9afa5ec24b58d6d3f271a9d655e1b98c6b550ea7... |
bors
commented
Nov 29, 2017
☀️ Test successful - status-travis |
Mark-Simulacrum
commented
Nov 29, 2017
Perf started. |
eddyb
commented
Nov 29, 2017
Perf results are better, although most wins are minor and there's a regression on |
As a random lurker, I wonder how this PR and this LLVM patch relate. AFAICT, they do very similar optimizations but with different code representations (MIR and LLVM IR). |
pcwalton
commented
Nov 29, 2017
@stjepang For one, the patch in this PR can handle borrows across functions, while that LLVM patch cannot. |
eddyb
commented
Nov 29, 2017
@stjepang I think @pcwalton abandoned that patch in favor of MIR optimizations, which had a lower priority and very little happened since. @pcwalton's own (forward, unlike this PR) "copy propagation" MIR pass is disabled by default because it was too slow to run (missing some caching). Also, this PR does more than optimize across basic blocks, allowing borrows without needing to track them. OTOH, LLVM could likely handle field accesses, while this PR doesn't, by itself. Not sure how we could encode the semantics of |
arielb1
commented
Nov 30, 2017
I talked with @eddyb on IRC and this appears this optimization is unjustified, especially with NLL.. |
eddyb
commented
Nov 30, 2017
On IRC @arielb1 pointed out that with NLL this might become valid: let(a, b, x);
x = None;loop{
a = init;use(x,&mut a);endregion('α);
b = move a;
x = Some(&'α b);}
endregion('α);This PR, in its current state, would incorrectly reuse |
nikomatsakis
left a comment
There was a problem hiding this comment.
OK um so I never really finished this review. I was hoping to write up a kind of description of what the code is doing. At this point I'm not sure of current status so I'll just post these incomplete and not that interesting comments for now.
@eddyb -- what is your latest thinking for how to proceed here?
There was a problem hiding this comment.
I'm missing some kind of meta comment here somewhere in this file. In my ideal world, the explanation would include the "before" and "after" MIR, in such a way that we can refer back to the example in the comments below. I'll take a stab at writing comments as my review to see if I understand what's going on. =)
There was a problem hiding this comment.
Can we get some tests specific to this optimization? Ideally a directory like mir-opt/reuse_lvalues/ with various corner cases, showing before/after --- but anyway at least one? =)
There was a problem hiding this comment.
Example:
L1 = ...
...
L2 = move L1
Here, local is L1 and dest is L2. We will set the reused flag on L2 to true.
There was a problem hiding this comment.
Nit: you can now do let (def, move) = &mut self.defs_moves[local];
There was a problem hiding this comment.
Note that Store corresponds also to things like SetDiscriminant and InlineAsm -- is that a problem here? I guess not.
carols10cents
commented
Dec 12, 2017
eddyb
commented
Dec 12, 2017
@carols10cents I have discussed a new set of analyses with @nikomatsakis which should make the optimization sound, and I hope to wrap up bug hunting and get back to this PR soon. |
scottmcm
commented
Dec 20, 2017
Looks like this will fix #32966 |
eddyb
commented
Dec 20, 2017
I'll close this PR as a reimplementation is required anyway, for soundness wrt loops. |
eddyb
commented
Dec 25, 2017
Note to self, I've tried this: #![feature(nll)]structFoo(u8);fnwithout_nll(){let(mut a,mut b);let r;letmut first = true;loop{
a = Foo(0);if first {
first = false;
b = a;}else{
r = &a;break;}}// `a`, `b`, and `*r` are the same location.// However, I don't see a way to abuse it.}fnwith_nll(){let(mut a,mut b);letmut r = None;loop{
a = Foo(0);// `*r` and `a` are the same location.drop((r,&mut a));
b = a;
r = Some(&b);}}fnmain(){without_nll();with_nll();}
As it stands, all I can do is keep this example for future mir-opt tests, and make sure it's not optimized, preferably without loop-specific special cases, just a generalized dataflow algorithm. |
jrmuizel
commented
Nov 22, 2018
Any chance we can see something like this resurrected? |
Replaces a chain of moves, such as
a = ...; ... b = move a; ... f(&mut b) ... c = move b, with the final destination, i.e. onlyc = ...; ... f(&mut c); ...remains (note that borrowing works).