rust-lang / rust-lang/rust

Suboptimal code generation with unnecessary stack spills around std::mem::take / alternative implementation suggested

Open
#154,546 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

C-bug needs-triage
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.