Suboptimal code generation with unnecessary stack spills around std::mem::take / alternative implementation suggested
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
I tried this code:
pub enum State {
Active(Vec<i64>),
Pending(Vec<i64>),
}
pub fn update_state_orig(state: &mut State) {
if let State::Pending(vector) = state {
*state = State::Active(std::mem::take(vector));
}
}
I expected to see this happen:
The method update_state_orig() just flips the discriminant byte of the enum, as the Vec moved over from "Pending" to "Active" state is the same in the same position in the enum.
Instead, this happened:
The Vec is loaded into registers, than spilled to the stack, then re-loaded from the stack into the very same registers, than written back into the same memory locations, where it came from. This happens even with -C opt-level=3. Tested on rustc 1.94.0 and rustc nightly.
Possible fix:
Interestingly enough, the optimization inefficiency disappears, when std::mem::replace and std::mem::take are reimplemented on top of std::mem::swap:
pub fn replace_via_swap<R>(location: &mut R, mut new_value: R) -> R {
std::mem::swap(location, &mut new_value);
new_value
}
pub fn take_via_swap<R: Default>(location: &mut R) -> R {
replace_via_swap(location, Default::default())
}
pub fn update_state(state: &mut State) {
if let State::Pending(vector) = state {
*state = State::Active(take_via_swap(vector));
}
}
As can be seen on Godbolt, https://godbolt.org/z/1eGf3anPq , the code generation for update_state(), which uses take_via_swap(), is optimal without any unnecessary stack spills.
Meta
Tested with rustc 1.94.0 and rustc nightly on Godbolt. As this is about code optimization (the code generated is formally correct, just much less optimal, than it could be), no backtrace can be included.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the Rust snippets from the issue with rustc 1.94.0 and nightly, using the linked Godbolt comparison as the reference. Inspect optimized assembly for update_state_orig and the swap-based update_state, then trace the relevant compiler optimization path. Done means the unnecessary stack spills are removed without changing correctness.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100