Skip to content

Tile libraries: call Rust device functions from Tile kernels - #1545

Merged
nihalpasham merged 3 commits into
NVIDIA:mainfrom
nihalpasham:feat/tile-simt-interop
Oct 19, 2026
Merged

nihalpasham merged 3 commits into
NVIDIA:mainfrom
nihalpasham:feat/tile-simt-interop

Conversation

@nihalpasham

@nihalpasham nihalpasham commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator

What this adds

A Rust device function can now be called from inside a Tile kernel. #[tile_export] marks the function, and cargo oxide tilelib packages the crate as a .tilelib, the static tile library format tileiras links with -l.

before: a Tile kernel and a Rust kernel could only share a stream and buffers
after:  a Tile kernel calls the Rust function in its body, one invocation per tile element
#[tile_export(tile_shape = 128)]
pub fn relu(x: f32) -> f32 {
    if x > 0.0 { x } else { 0.0 }
}
cargo oxide tilelib tilelib_relu --arch sm_120   # -> tilelib_relu.tilelib

Direct linkage flow

cuda-oxide is the SIMT provider: it ships the code (LTOIR from the existing pipeline) and two small descriptions (the interface and the implementation, as Tile IR bytecode). tileiras does the matching, marshalling and linking. The Tile side only emits the call. Everything lives under cuda-oxide/; cutile-rs is untouched.

The contract

  • in scalars are passed by value, &T, &mut T and fixed arrays as pointers, and a scalar return value becomes a trailing out pointer. #[tile_export] generates that wrapper next to the function.
  • sync = "warp" means 32 lanes call the function together, so warp shuffles are legal inside it.
  • Preview content is read only at the assembler's current bytecode version. The command asks the installed tileiras and writes the descriptions at that version: 13.5 for a CUDA 13.5 assembler, the newer version a newer one names, and 13.5 when none can read them yet.
Implementation
  • oxide-tilebc is a new crate that writes the two descriptions as Tile IR bytecode (preview$func extern and preview$extern_impl, with the fragment types and layouts they carry). It has a typed model, a structural verifier with the Tile compiler's rules, and a textual printer in the upstream form for the --dump output. It writes nothing else of the format; ordinary kernels are not touched.
  • oxide-tile-export is the shared description vocabulary and its rules (one dimension, one footprint per export, written parameters as one contiguous run, whole warps for warp sync).
  • #[tile_export] generates the extern "C" wrapper plus a host-only metadata function whose body is the JSON description; the backend reads it from MIR, roots the wrapper, and writes <crate>.tileexports.json beside the device artifact.
  • cargo oxide tilelib runs emit-ltoir, checks that the NVVM IR defines every symbol with the parameters the ABI predicts, writes both bytecode halves, and calls tileiras --preview-create-tile-lib. On an older assembler it stops after the descriptions with exit 2, or exit 0 with --library-optional, which is how the smoketest's new tilelib lane runs it.
  • The tilelib_relu example has three consumers: a host program that proves the ABI on CUDA 13.4 (a CUDA C++ caller generated from the descriptions, linked with nvJitLink), a Tile C++ kernel (cpp/tile_caller.cu), and a cuTile Python kernel (python/call_from_cutile.py).

Verification

Checked at a7c465e9d:

  • ✅ oxide-tilebc 15 tests (hand-built byte tables, verifier rules, version parsing), clippy with warnings as errors, rustdoc.
  • ✅ cargo-oxide 255 tests, cuda-macros 132 tests plus the trybuild pass and nine compile-fail cases, oxide-tile-export 16 tests; all 18 repository guards, lockfile sync, cargo deny, book with warnings as errors.
  • ✅ RTX 5090: cargo oxide run tilelib_relu verifies all three exports through the native ABI on CUDA 13.4.
  • ✅ RTX 5090 with a 13.5-capable tileiras: cargo oxide tilelib builds tilelib_relu.tilelib, and the Tile C++ kernel calls all three exports through it (3 of 3, bit for bit). The decoded descriptions equal the accepted reference bytecode.
  • ✅ cuTile Python 1.7 dry run: the kernel compiles to 13.5 bytecode carrying the preview$call to relu.

Caveats

  • The .tilelib itself needs a CUDA 13.5 or newer tileiras; on 13.4 the command writes the descriptions and the LTOIR and says so.
  • The cuTile Python run needs a cuTile Python that lists the assembler's bytecode version; the Tile C++ path has no such gap.
  • One-dimensional tiles, f16 but not bf16, concrete shapes only. Linkage providers (implementations compiled on demand) are not part of this change.

@nihalpasham nihalpasham added the enhancement New feature or request label Oct 18, 2026
@nihalpasham
nihalpasham requested a review from rparolin as a code owner October 18, 2026 14:34
@nihalpasham nihalpasham added cargo-oxide The `cargo oxide` subcommand and build automation codegen Device code-generation pipeline (Rust MIR to IR to PTX) labels Oct 18, 2026
@nihalpasham
nihalpasham requested a review from cryos as a code owner October 18, 2026 14:34
@nihalpasham nihalpasham added the interop Interop with external compilers / frontends label Oct 18, 2026
@nihalpasham
nihalpasham requested a review from roivanov as a code owner October 18, 2026 14:34
@nihalpasham
nihalpasham force-pushed the feat/tile-simt-interop branch from b1d6b1f to 1cd610b Compare October 18, 2026 14:39
…ile kernels

`#[tile_export(shape = N, ...)]` marks a device function as an entry of a
cuTile tile library. The attribute leaves the function as it is and
generates two items beside it: a `#[device] extern "C"` wrapper on the
native SIMT ABI the Tile compiler calls (`in` scalars by value, `&T`,
`&mut T`, `&[T; N]` and `&mut [T; N]` as pointers, a scalar return as a
trailing `out` pointer), and a host-only metadata function whose body is
the export's JSON description.

- `oxide-tile-export` (new crate): the description vocabulary shared by
  the macro, the backend and `cargo oxide tilelib`, with the rules a
  library entry must satisfy (one dimension, one footprint for every
  parameter, one contiguous run of written parameters, whole warps for
  `sync = "warp"`).
- `reserved-oxide-symbols`: the `cuda_oxide_tile_export_246e25db_`
  prefix of the metadata functions.
- `rustc-codegen-cuda`: the collector reads each description out of the
  metadata function's MIR, roots the wrapper even when the crate has
  kernels, rejects duplicate names and symbols and device-side calls of
  the metadata function, and writes `<crate>.tileexports.json` next to
  the device artifact.
- trybuild tests for the accepted forms and every rejection; the example
  lockfiles pick up the new dependency of cuda-macros.

Signed-off-by: nihalpasham <nihalp@nvidia.com>
A tile library lets a Tile kernel call a SIMT function. cuda-oxide is the
SIMT provider: it ships the code as LTOIR and describes it twice, as the
declaration a Tile kernel calls and as the native symbol with the fragment
each parameter receives. This crate writes those two descriptions as Tile
IR bytecode and nothing else in the format: header, function table with
bodiless `preview$func extern` and `preview$extern_impl` entries, type and
string tables, and the one-entry debug table.

- A small model (`Declaration`, `Implementation`, tiles, fragments,
  layouts, the synchronization, arch and frontendABI clauses) with a
  verifier for the rules the Tile compiler applies when the halves meet.
- `interface_bytecode` and `implementation_bytecode` take the version the
  consuming assembler calls current; preview content is readable at no
  other version, and anything below 13.5 is an error.
- A readable textual form of both modules.

Every encoding was derived from the Tile IR compiler sources and checked
against the CUDA 13.6 assembler: `tileiras --preview-create-tile-lib`
packs this crate's output into a library a Tile C++ kernel then calls.

Signed-off-by: nihalpasham <nihalp@nvidia.com>
@nihalpasham
nihalpasham force-pushed the feat/tile-simt-interop branch from 1cd610b to 81452f9 Compare October 18, 2026 15:32
…mple

`cargo oxide tilelib <crate> --arch sm_XX` packages a crate's
`#[tile_export]` functions as a cuTile tile library. It runs emit-ltoir,
checks that the NVVM IR defines every implementation symbol with the
parameter list the native SIMT ABI predicts, maps the descriptions to the
oxide-tilebc model, writes the interface and implementation bytecode plus
a readable `.tilelib.mlir`, and asks `tileiras --preview-create-tile-lib`
for the `.tilelib`. An assembler reads preview content only at the version
it calls current, so the descriptions are written at the newest version
the installed tileiras lists (13.5 when none can read them yet). An older
assembler stops after the descriptions with exit 2, or exit 0 with
`--library-optional`; the summary ends with a "written to" or "skipped"
line that scripts read.

The `tilelib_relu` example is a three-export library (`relu`,
`scale_in_place`, `warp_sum`) with three consumers: a host program that
proves the ABI on the installed toolkit (a CUDA C++ caller generated from
the descriptions, linked with nvJitLink, checked on the GPU), a Tile C++
kernel in `cpp/tile_caller.cu` built with nvcc's `--enable-tile` and
`--tile-preview simt`, and a cuTile Python kernel in
`python/call_from_cutile.py` with a dry run that stops at the bytecode.

Also: the smoketest `tilelib` lane and the book chapter on tile libraries.
Signed-off-by: nihalpasham <nihalp@nvidia.com>
@nihalpasham
nihalpasham force-pushed the feat/tile-simt-interop branch from 81452f9 to a7c465e Compare October 18, 2026 15:37
@nihalpasham
nihalpasham merged commit 9571746 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

cargo-oxide The `cargo oxide` subcommand and build automation codegen Device code-generation pipeline (Rust MIR to IR to PTX) enhancement New feature or request interop Interop with external compilers / frontends

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant