Skip to content

feat(unroll): point not-unrolled warnings at the #[unroll] attribute - #1530

Merged
nihalpasham merged 5 commits into
NVIDIA:mainfrom
midagedev:feat/unroll-warning-location
Oct 19, 2026
Merged

nihalpasham merged 5 commits into
NVIDIA:mainfrom
midagedev:feat/unroll-warning-location

Conversation

@midagedev

@midagedev midagedev commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

What this adds

A not-unrolled warning now says where the request was. The #[kernel], #[device] and #[cuda_module] macros give the hidden __unroll_config marker the span of the #[unroll] attribute instead of the span of the outer macro, and the unroll pass prints the position the importer attached to that marker as a second line in rustc's --> form. A kernel with several annotated loops shows which one was left alone.

before:
warning: #[unroll] requested but the loop was not unrolled: no recognized induction variable (loop counter)

after:
warning: #[unroll] requested but the loop was not unrolled: no recognized induction variable (loop counter)
  --> src/main.rs:504:9
#[kernel]
fn k(n: u32) {
    let mut other = 0u32;
    #[unroll]                 // the warning points here
    for i in 0..=4u32 {       // inclusive range: not a recognized counter
        other += i;
    }
}

What we fixed

  • The marker took Span::call_site(), the whole #[kernel] attribute, so neither the pass nor rustc had a useful position for anything about the loop.
  • rustc's own const-eval note for a bad factor, #[unroll(Invalid::UNROLL)], pointed at #[kernel] with a macro-backtrace hint; it now points at the attribute.
  • The five copies of the hint-unlink loop in the pass collapsed into one helper that also returns the position.
  • Layered on while landing: the pliron 0.20 port (main moved in chore(cuda-oxide): upgrade Pliron to 0.20.0 #1533 after the branch was cut), and the const argument's braces kept on the macro's own span, because on the attribute span every literal #[unroll(N)] tripped rustc's unused_braces lint and failed the per-example clippy lane on four examples.

Warning contract

  • The first line is unchanged: warning: #[unroll] requested but the loop was not unrolled: <reason>, or #[unroll(N)] for a partial request.
  • The second line is --> file:line:column of the #[unroll] attribute, 1-based, with the path as rustc prints it for the crate.
  • When the hint carries no single file position the second line is omitted.
Implementation
  • cuda-macros: take_unroll_factor returns the factor together with attr.span(); marker_stmt builds the call with parse_quote_spanned! at that span and interpolates the braced factor with the macro's span. One visitor serves all three entry points.
  • mir-importer: untouched. It already sets the hint op's Location from the call terminator's span, so the position is available without debug info.
  • mir-transforms: hint_location reads Location::SrcPos { Source::File, pos } and returns None for every other variant; take_loop_hints unlinks a loop's hints and returns the first located one; not_unrolled_warning is the single text source for the seven emission sites.
  • Tests: a span test over while, for and loop at different indents, two unit tests for a located and an unlocated hint, the updated trybuild snapshot, and a trybuild pass case under #![deny(unused_braces)] with literal factors.

Verification

Checked at fdcb34315 (nightly-2026-10-11, RTX 5090):

  • ✅ cargo test -p cuda-macros --all-targets: the unit tests and every trybuild case pass, including the new span test, the new deny(unused_braces) pass case and the policy_config_invalid_unroll snapshot at the attribute (15:5).
  • ✅ cargo test -p mir-transforms --all-targets: 35 unit tests, two of them new, plus the integration suites pass.
  • ✅ cargo oxide run unroll_smoke on sm_120a: PASS; the four deliberate negatives print positions 249:13, 273:13, 504:9 and 508:9; zero unused_braces warnings; memcheck 0 errors.
  • ✅ PTX against main: 22 of 22 kernel bodies identical; --lineinfo line tables identical.
  • ✅ The per-example clippy gate is green on unroll_smoke, unroll_bounds_check, gemm_sol and gemm_sol_final; fmt, clippy and rustdoc with warnings as errors on both crates, the 18 guards, the lock check and the book build are clean.

Caveats

  • The warning still reaches stderr from the pass rather than rustc's diagnostics: cargo does not replay it and -D warnings does not see it. Routing it like the launch-bounds spill warning is a follow-up.
  • With --device-debug the goto that replaces the marker carries the attribute's line instead of the #[kernel] line; --lineinfo tables and PTX are unchanged. Follow-up.
  • An annotated non-loop construct inside an annotated loop reports the outer attribute only, as before.

Signed-off-by: midagedev <midagedev@gmail.com>
@copy-pr-bot

copy-pr-bot Bot commented Oct 16, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@nihalpasham nihalpasham added the cuda-oxide SIMT programming model: rustc backend, cargo oxide, cuda-device, cuda-host, book label Oct 17, 2026
pliron 0.20 removed the Located trait and Operation::loc() now returns a
borrowed Location, so the match binds the source path key by reference.
Drop the trait import in the pass and its tests and pass the key by value
to uniqued_any::get, which has always taken it by value.

Signed-off-by: nihalpasham <nihalp@nvidia.com>
The #[unroll] marker now carries the attribute's span so not-unrolled
warnings point at the attribute. Giving that span to the braces around a
literal factor made rustc's unused_braces lint report every #[unroll(N)]
in the user's crate, with a fix-it that deletes the attribute, and failed
the per-example clippy gate on four examples.

Build the braced const argument with the macro's own span and give only
the call the attribute's span. The marker text and position are
unchanged, so the warning positions and the const-eval note still land
on the attribute. A trybuild pass case under #![deny(unused_braces)]
with literal factors guards it.

Signed-off-by: nihalpasham <nihalp@nvidia.com>
…bute

Each warning about a loop that was not unrolled now carries the file,
line and column of its #[unroll] attribute on a --> line. Add one
sentence to the two book pages that describe those warnings.

Signed-off-by: nihalpasham <nihalp@nvidia.com>
@nihalpasham nihalpasham added QoL-improvement Quality-of-life improvement; not a correctness fix codegen Device code-generation pipeline (Rust MIR to IR to PTX) labels Oct 19, 2026
@nihalpasham

Copy link
Copy Markdown
Collaborator

/ok to test fdcb343

@nihalpasham

Copy link
Copy Markdown
Collaborator

Thanks, the attribute is the right anchor and the pass only reads the location the importer already attaches. Three things on top before merging:

  • Ported the location lookup to pliron 0.20, since main moved in chore(cuda-oxide): upgrade Pliron to 0.20.0 #1533 after this branch was cut.
  • Kept the const argument's braces on the macro's own span. On the attribute span, rustc's unused_braces lint fired on every literal #[unroll(N)] and the per-example clippy lane failed on four examples. The call still carries the attribute span, so the warning position and the const-eval note are unchanged; a trybuild case under #![deny(unused_braces)] guards it.
  • One sentence in the book: each warning names its attribute as file:line:column.

At fdcb34315, unroll_smoke passes on the 5090 with its four warnings at 249:13, 273:13, 504:9 and 508:9, all 22 kernels match main's PTX, memcheck is clean, and all CI checks passed. Two follow-ups, not part of this change: under --device-debug the goto that replaces the marker should take the artificial location the other markers use, and these warnings belong in rustc's diagnostics so cargo replays them.

@nihalpasham
nihalpasham merged commit 44df2a1 into NVIDIA:main Oct 19, 2026
64 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codegen Device code-generation pipeline (Rust MIR to IR to PTX) cuda-oxide SIMT programming model: rustc backend, cargo oxide, cuda-device, cuda-host, book QoL-improvement Quality-of-life improvement; not a correctness fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants