Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

#[unroll] over the clone budget leaves an array-indexing loop rolled and moves the array to .local, with no way to raise the budget

Open
#1,478 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 2 days

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
45/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Active
Tech stack
compilers, rust

Research direction

Start in mir-transforms/src/unroll.rs and inspect MAX_CLONED_OPS and the unroll decision; the issue also cites kernel instances in crates/speakrs-cuda-kernels/src/segdense.rs. Reproduce the reported array placement and clone-budget warning with the provided accumulator pattern on sm_80, then decide which proposed behavior to implement. Done means a chosen override or diagnostic is implemented and covered by a regression test for the over-budget loop.

Written by the indexing model from the issue text.

Description

What problem does this solve?

When #[unroll] on the loop that selects an element of a local array goes over the 65536-operation clone budget (MAX_CLONED_OPS in mir-transforms/src/unroll.rs), the loop stays rolled. The whole array then moves to .local memory. The only signal is a warning, and there's no way to raise the budget for that loop.

The budget counts the outer loop's body after its inner #[unroll] loops have already been expanded. So a typical tensor-core accumulator pattern goes over budget at modest sizes:

#[kernel]
pub fn acc_tiles(a: *const f32, b: *const f32, out: *mut f32) {
    let tid = thread::threadIdx_x() as usize;
    let mut acc = [[0.0f32; 8]; 32];
    let mut t = 0usize;
    #[unroll]
    while t < 32 {
        let mut j = 0usize;
        #[unroll]
        while j < J {
            let mut n = 0usize;
            #[unroll]
            while n < 8 {
                let x = unsafe { *a.add(tid + (j * 32 + t) * 128) };
                let y = unsafe { *b.add(tid + (j * 8 + n) * 128) };
                acc[t][n] += x * y;
                n += 1;
            }
            j += 1;
        }
        t += 1;
    }
    // store acc with the same unrolled t/n loops
}

On main (bafffe45), sm_80, ptxas 13.0.88 -v:

J warning acc registers stack / spill
1 none registers 72 0 / 0
3 none registers 168 0 / 0
8 not unrolled (152672 ops) .local, 792 LDL/STL 96 1024 B / 0
32 not unrolled (609632 ops) .local, 944 LDL/STL 255 1184 B / 160 B
warning: #[unroll] requested but the loop was not unrolled: unrolling would clone 152672 operations
(32 copies x 4771 operations); the safety limit is 65536 cloned operations

At J = 8, ptxas still has registers to spare, but the 1 KiB accumulator array sits in .local. The inner loops unroll correctly; only the loop that selects acc[t] stays rolled. In speakrs, this decided several kernel shapes (crates/speakrs-cuda-kernels/src/segdense.rs, the spk_segdense_conv1_b32_x3 and spk_segdense_embed_b32_x3 instances). We split work across more warps or kernels so the unrolled body fits, even where a larger tile per warp would have been faster.

Proposed behavior

Either or both:

  1. A per-loop override, such as #[unroll(full)] or #[unroll(max_ops = N)], that unrolls even past the default budget. That's like CUDA C's #pragma unroll, which has no size limit.
  2. A more precise diagnostic: name the local array that will move to .local because the loop stays rolled. Optionally, make it an error when the loop index selects an aggregate local, since the result is rarely what the author wants.

Alternatives considered

Restructuring kernels so the expanded body fits, which is what we do now.

Related: #1346 (for range loops not unrolled, same .local consequence), #1375 and #1468 (loop metadata and #[nounroll]).

Dominant language
Rust
Stars
3.7k
Forks
312
Avg merge
6d 5h
Merged PRs (30d)
68

Getting set up

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.

More from NVIDIA/cuda-rust

All issues in NVIDIA/cuda-rust

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.