Skip to content

fix(mir-importer): lower from-end ConstantIndex after Subslice - #1343

Merged
nihalpasham merged 6 commits into
NVIDIA:mainfrom
YunusMutlu:fix/subslice-from-end-constant-index
Oct 17, 2026
Merged

nihalpasham merged 6 commits into
NVIDIA:mainfrom
YunusMutlu:fix/subslice-from-end-constant-index

Conversation

@YunusMutlu

@YunusMutlu YunusMutlu commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

What this adds

A from-end ConstantIndex that follows a slice Subslice now lowers in both MIR place walkers. rustc emits this chain for nested slice patterns: [_, middle @ .., _] rebuilds the slice as (data + from, len - from - to), and [.., last] on middle indexes it as rebuilt_len - offset. The importer previously knew a slice length only directly after a fat-slice deref and rejected the projection, on the read path and on the write path.

before: Unsupported construct: ConstantIndex with from_end=true not yet supported
after:  (*_1)[1:-1][-1 of 1]  ->  gep i32, (data + 1), ((len - 2) - 1)
#[inline(never)]
fn bump_middle_last(values: &mut [u32]) {
    if let [_, middle @ .., _] = values
        && let [.., last] = middle
    {
        *last += 11;
    }
}

What we fixed

  • Address walker: when a slice Subslice is followed by a from-end ConstantIndex, the rebuilt subslice's length is extracted alongside its data pointer and carried into the existing from-end arm, which computes len - offset and offsets the element pointer.
  • Value walker: a from-end ConstantIndex requires the current value to be the rebuilt fat slice or a reconstructed slice tail, extracts its length, and feeds the runtime index into the existing pointer-offset path; the forward-index path is unchanged.
  • The same value-walker arm also lowers from-end reads on a slice-tail field, ((*_1).1: [u32])[-1 of 1], which main rejected.
  • subslice_projection gains a from-end-plus-Field read, a mutable case with two from-end offsets and collateral-write detection, and a runtime-length case whose PTX keeps the from-end addressing. The constant_index_from_end README no longer says the after-Subslice case fails closed.
Implementation
  • place_addr.rs: the Deref-to-Subslice continuation accepts any ConstantIndex; for a from-end follower it sets current_slice_len from field 1 of the rebuilt mir.construct_slice, and the consumed-Subslice skip re-arms it for one iteration so the pre-existing from-end arm sees entered_slice_len. emit_from_end_slice_index becomes pub(super).
  • place_iter.rs: the ConstantIndex arm computes a runtime_index for from_end from the MirSliceType current value via emit_slice_len_extract and emit_from_end_slice_index; the Ptr path uses it instead of the i32 constant. A non-slice base fails loudly with from-end ConstantIndex requires preserved slice metadata.
  • Both walkers reuse the shared helpers; no new ops, no new route. min_length is ignored as in rustc_codegen_ssa, which binds it _; rustc's MIR validator rejects offset > min_length and the pattern-length test dominates every use, so len - offset cannot wrap on an executed path.
  • Lineage: fix(mir-importer): lower from-end ConstantIndex on slices #752 added direct from-end indexing on slices and scoped out the after-Subslice case; fix(mir-importer): lower Subslice projections #754 added Subslice; fix(mir-importer): lower Index on slice-tailed DST values #873 added the slice-tail reconstruction the value walker now also accepts.
  • Nested Subslice -> Subslice and a Subslice on a slice-tail field still punt loudly with slice Subslice reached iterative lowering without preserved fat-pointer metadata, as on main; documented in the example README and the walker doc comment.

Verification

Checked at 0c8561c59 (the PR head merged with main e84e5b3d7):

  • ✅ Fail-before: main's backend rejects this example at middle_last_field and, in isolation, at *last += 11, with the message quoted in mir-importer: from-end ConstantIndex after slice Subslice is unsupported #1342.
  • ✅ cargo oxide run subslice_projection on sm_120a, RTX 5090, CUDA 13.4: 8/8 PASS and the SUCCESS marker; compute-sanitizer memcheck 0 errors and 0 bytes leaked, synccheck 0 errors.
  • ✅ The runtime-length case reads its length from the row data (3 to 8 across six lanes) and keeps the from-end addressing in the PTX: [%rd+-8] for [.., last] and [%rd+-12] for [.., second_last, _], off base + (len << 2) behind the pattern-length guards; the pre-opt IR carries sub i64 %len, 2 then sub i64 %sublen, offset feeding getelementptr inbounds for every from-end helper.
  • ✅ Adversarial kernels pass here and fail on main's backend: f32, u8, u64 and bool elements, struct Field read and write, Option Downcast read and write, &[[u32; N]] row then column, &[&u32] and &[&mut u32] then Deref, [first, .., last] on one rebuilt subslice, from: 0 and from: 2, runtime lengths 3 to 9 with offsets 1, 2 and 4; memcheck clean.
  • ✅ PTX of the five pre-existing kernels is identical to main's at sm_120a after label renumbering.
  • ✅ cargo test -p mir-importer: 1169 passed. clippy and rustdoc on mir-importer with -D warnings, the per-example clippy step (which failed at the PR head with 4 errors), cargo oxide fmt --check, 18 repository guards and sync-example-locks.sh --check all clean. All 60 CI checks passed.
  • ✅ Author: just check, cargo oxide build --arch sm_120 subslice_projection, runtime pass on an RTX 3050 Ti Laptop GPU, sm_86.

Caveats

  • The constant-length cases fold the from-end index to a constant element offset after inlining, so the from-end arithmetic appears only in the pre-opt IR and in the runtime-length case's PTX; that case keeps its buffer in local memory.
  • Nested Subslice -> Subslice and Subslice on a slice-tail field remain unsupported and fail loudly; tracked separately.
  • just check does not lint the example directories; CI does, with -D warnings.

Closes #1342.


@nihalpasham nihalpasham added the cuda-oxide SIMT programming model: rustc backend, cargo oxide, cuda-device, cuda-host, book label Oct 11, 2026
Signed-off-by: Yunus Mutlu <mutluyunus708@gmail.com>
@YunusMutlu
YunusMutlu force-pushed the fix/subslice-from-end-constant-index branch from 7eda354 to d828864 Compare October 13, 2026 11:30
@copy-pr-bot

copy-pr-bot Bot commented Oct 13, 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.

Rewrite the two nested `if let` helpers as let chains. CI lints every
example with `-D warnings`, and the nested form failed
clippy::collapsible_match and clippy::collapsible_if. The let chain keeps
the `(*_1)[1:-1][-1 of 1]` projection that clippy's own suggestion would
have removed.

Add a runtime-length case. The four-element cases fold to one constant
store once LLVM inlines the helpers, so their PTX never performs the
from-end arithmetic. The new case slices a local eight-element buffer at
a length read from the row data (3 to 8), runs both from-end writes and a
from-end read, and checks a packed result against the same helpers on the
host. Its PTX keeps `[%rd+-8]` and `[%rd+-12]` accesses off
`base + (n << 2)` behind the pattern-length guards.

The README now says the example also covers the from-end case, counts
eight cases, and names the nested Subslice chains that still fail loudly.

Signed-off-by: nihalpasham <nihalp@nvidia.com>
The address walker's doc comment names the two chains that still return
`Ok(None)` on purpose: a slice Subslice that does not directly follow a
fat Deref, and a from-end ConstantIndex on a base without a slice length.
The from-end index helper cites rustc's MIR validator rule, `offset` at
most `min_length`, as the reason the subtraction cannot wrap.

The value walker's Subslice arm records that the rebuilt fat value is
also kept for a following from-end ConstantIndex, whose arm reads field 1
for the length, so the data-pointer filter must stay restricted to Index
and forward ConstantIndex. The array branch of the ConstantIndex arm
asserts in debug builds that no from-end index reaches it.

The two single-projection bail-outs no longer say "not yet supported":
rustc emits `from_end` only for slice patterns and its validator rejects
arrays indexed from the end, so a from-end index on a bare local cannot
occur in valid MIR.

Signed-off-by: nihalpasham <nihalp@nvidia.com>
The README said a from-end index after a Subslice still fails closed. It
now takes the rebuilt subslice length, and the subslice_projection
example covers that case. Other bases without fat-deref provenance still
fail closed.

Signed-off-by: nihalpasham <nihalp@nvidia.com>
Only the mutable case folds to a constant store; the field case keeps a load with a constant element offset.

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

Copy link
Copy Markdown
Collaborator

/ok to test 0c8561c

@nihalpasham
nihalpasham merged commit df7440e into NVIDIA:main Oct 17, 2026
60 checks passed
@nihalpasham nihalpasham added bug Something isn't working codegen Device code-generation pipeline (Rust MIR to IR to PTX) examples Example kernels and host harnesses under crates/rustc-codegen-cuda/examples labels Oct 17, 2026
@nihalpasham

Copy link
Copy Markdown
Collaborator

Thanks, this is the right fix and it matches what rustc's own backend does. Three small things on top:

  • CI lints every example with -D warnings, and the two nested if let helpers tripped collapsible_match. I turned them into let chains; clippy's own suggestion would have dropped the (*_1)[1:-1][-1 of 1] projection we want to exercise.
  • The four-element cases fold the from-end index to a constant, so their PTX never does the arithmetic. I added a case whose slice length comes from the data (3 to 8 across six lanes); its PTX keeps the [%rd+-8] and [%rd+-12] accesses behind the pattern guards.
  • The constant_index_from_end README still said this case fails closed; fixed, and both READMEs now name what still stops: a nested Subslice, and a Subslice on a slice-tail field.

8/8 cases pass on the 5090 with memcheck and synccheck clean, the five older kernels give the same PTX as main, and all 60 CI checks are green at 0c8561c59. That takes care of #1342.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working 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 examples Example kernels and host harnesses under crates/rustc-codegen-cuda/examples

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mir-importer: from-end ConstantIndex after slice Subslice is unsupported

3 participants