rust-lang / rust-lang/rust

Iterator stack allocation is not split up

Open
#144,306 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

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

Description

I tried this code (godbolt):

use std::hint::black_box;

type N = u8; // Doesn't matter

#[no_mangle]
pub fn f(n: &N, f: fn(u64)) {
    for x in (MyIter { n: *n, base: 999 }) {
        f(x);
    }
}

struct MyIter {
    base: u64,
    n: N,
}

impl Iterator for MyIter {
    type Item = u64;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        do_next(&mut self.n, self.base)
    }
}

#[inline(never)]
fn do_next(n: &mut N, base: u64) -> Option<u64> {
    // Doesn't matter 
    black_box(n);
    black_box(base);
    black_box(None)
}

I expected to see this happen: self.base is never modified, thus it gets constant-propagated at the f callsite and the only stack allocation that remains is for n

Instead, this happened: base is stored into the iterator allocation, and is loaded for every iteration of the loop even if never modified

This is to illustrate that even if only part of the iterator is ever mutated, LLVM won't recognize that and fail to constant propagate base inside of the loop body, which can have pretty drastic consequences for performance if it's used e.g. for integer division.

This is not specific to iterators, but this is how I noticed the missed optimization in real world code in this PR.

You can see in the godbolt link that we can work around this by creating a new allocation for n and passing that instead will correctly pass base as a constant to do_next.

The type of N and the contents of do_next don't matter. do_next can be fully inlined and the work around will still have its effect.

Meta

rustc --version --verbose:

rustc 1.90.0-nightly (5795086bd 2025-07-16)
binary: rustc
commit-hash: 5795086bdfe7ed988aa53a110bd0692c33d8755b
commit-date: 2025-07-16
host: x86_64-unknown-linux-gnu
release: 1.90.0-nightly
LLVM version: 20.1.8

@rustbot modify labels: +A-LLVM +I-slow +C-optimization -C-bug

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 with the Rust reproducer and Godbolt link to compare the generated output with the expected constant propagation. Investigate how the iterator allocation and mutable field are represented through optimization, then verify that the unchanged base is passed as a constant while only n remains mutable.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers, performance
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.