-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Missed optimizations for Vec::pop() followed by Vec::push() #61939
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
Comments
Similarly, both of these functions fail to optimize out the pub fn push_pop(v: &mut Vec<i32>) -> i32 {
v.push(1);
v.pop().unwrap()
} pub fn push_and_get_ref(v: &mut Vec<i32>) -> &i32 {
v.push(1);
v.last().unwrap()
} Unlike the example in the OP, this can technically cause The modifications to I think in the case of push-after-pop it should be possible for the compiler to optimize out |
FWIW, the workaround has been pessimized in Rust 1.38. The variant using example::noop:
mov rax, qword ptr [rdi + 16]
test rax, rax
je .LBB0_2
mov qword ptr [rdi + 16], rax
.LBB0_2:
ret In other words, there is now no possibility at all to get Rustc to optimize this piece of code correctly down to a no-op. |
The above behavior is still present in Rust 1.53 beta. The current 1.54.0-nightly (4de7572 2021-05-01) produces this assembly: example::noop:
cmp qword ptr [rdi + 16], 0
ret I'm not sure what has changed between these two versions. |
It looks like the |
@Diomendius it appears you were testing with a dated rustc 1.38 (2019-09-26). Try experimenting with nightly. |
@memoryruins I was using 1.38 as @troiganto originally reported that this version was the first to break the You're right that testing old versions is not generally helpful, but in this case both versions seem to exhibit the same bug, and the same "fix" (as brutal as it is) works for both. |
Starting with Rust 1.75 (currently in beta) all three examples given in the opening post properly optimize to a noop. The two examples given by @Diomendius in the second post still don't optimize out the |
Although just adding too many pub fn noop(v: &mut Vec<i32>) {
if let Some(last) = v.pop() {
v.push(last);
v.pop();
v.push(last);
v.pop();
v.push(last);
v.pop();
v.push(last);
v.pop();
v.push(last);
v.pop();
v.push(last);
v.pop();
v.push(last);
v.pop();
v.push(last);
v.pop();
v.push(last);
/*v.pop();
v.push(last);
v.pop();
v.push(last);
v.pop();
v.push(last);
v.pop();
v.push(last);
v.pop();
v.push(last);*/
}
} That still compiles to a noop, but uncomment one more pair and you get: example::noop:
mov rax, qword ptr [rdi + 16]
test rax, rax
je .LBB0_2
mov qword ptr [rdi + 16], rax
.LBB0_2:
ret Uncomment another one and you get the full Vec resizing logic. |
The following example should be get optimized to a no-op by the compiler:
Instead, it produces about 60 lines of assembly, which also contain calls to
__rust_alloc()
andalloc::raw_vec::capacity_overflow()
.The following code is better:
producing:
And even better:
producing:
Compiler explorer
I feel like the compiler should be able to handle this situation better, considering that all the information is readily available.
The text was updated successfully, but these errors were encountered: