rust-lang / rust-lang/rust

Call to `needs_drop` doesn't optimize properly in combination with unrelated mutation of static variable

Open
#143,320 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-LLVM C-optimization T-compiler
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

I tried this code (on Godbolt):

use std::sync::atomic::{AtomicBool, Ordering};

#[no_mangle]
pub fn f1() -> u32 {
    let slot = Slot::new(|| 6);
    slot.get() + 12
}

#[no_mangle]
pub fn f2() -> u32 {
    static S: Slot<u32> = Slot::new(|| 5);
    S.get() + 10
}

struct Slot<T>(fn() -> T, AtomicBool);

impl<T> Slot<T> {
    const fn new(f: fn() -> T) -> Self {
        Self(f, AtomicBool::new(false))
    }

    fn get(&self) -> T {
        if std::mem::needs_drop::<T>() {
            self.1.store(true, Ordering::SeqCst);
        }

        (self.0)()
    }
}

I expected to see this happen: With opt-level 3, both f1 and f2 produce optimized assembly that simply returns 18 and 15, respectively. For u32, the whole branch with std::mem::needs_drop::<T>() should completely optimize away.

Instead, this happened: f1 optimizes perfectly while f2 contains an unnecessary call which is not inlined. This only seems to happen if the branch guarded by the drop check could have a side effect on a global variable. The side effect is optimized away, but for some reason the call is not inlined anymore:

core::ops::function::FnOnce::call_once::h572ede77f557e91f:
        mov     eax, 5
        ret

f1:
        mov     eax, 18
        ret

f2:
        push    rax
        call    core::ops::function::FnOnce::call_once::h572ede77f557e91f
        add     eax, 10
        pop     rcx
        ret
Meta

rustc --version --verbose:

rustc 1.88.0 (6b00bc388 2025-06-23)
binary: rustc
commit-hash: 6b00bc3880198600130e1cf62b8f8a93494488cc
commit-date: 2025-06-23
host: x86_64-unknown-linux-gnu
release: 1.88.0
LLVM version: 20.1.5

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

Reproduce the issue with the provided Slot example using rustc 1.88.0 and -C opt-level=3, then compare the generated assembly for f1 and f2. Investigate why the static case retains the call to FnOnce::call_once even though the needs_drop branch is eliminated. Done means the unnecessary call is inlined or removed while preserving the expected results 18 and 15.

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
Stale
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.