Repository navigation
Use the shared cutile-rs host crates in cuda-oxide - #1102
Conversation
466c97d to
b62897d
Compare
…1106) Closes #1090. `mlir-dialects.md` states its own freshness rule: At catalog SHA-256 `26a9cc13` (the stamp in every `ops/generated/` file header), the dialect holds 570 operations across 42 modules ... If the header stamp no longer starts with `26a9cc13`, the counts on this page predate the catalog you are reading. Nothing enforced that rule, so the rule that would expose stale counts was itself unchecked. The page failed its own test for weeks before #1052 (560 printed against 570 in the tree), and **it is failing again right now**: the catalog hashes to `00372dfe`, five hand-written ops and five catalog entries after that refresh landed. Two hand audits in two batches is the pattern this closes. ## The gate One file hashed, one page grepped, no cargo and no network. The hash is the one `cuda-intrinsics-gen` stamps into every generated file -- `sha256_bytes(catalog_json.as_bytes())` over the file as committed -- so a plain `sha256sum` reproduces it, and the page prints its first eight digits. Three checks rather than one, so each failure says which thing moved: - The generated sources must stamp the hash `catalog.json` actually has. Failing here means the generated outputs are stale, and the message says to run `cuda-intrinsics-gen -- generate` -- not to edit the book. - The page must print exactly one distinct stamp. It names it twice, once stating it and once in the rule that tells a reader what a mismatch means, so updating one and not the other leaves the rule contradicting the statement above it. - The stamps must agree, and the failure prints both sides plus the two commands that re-derive the counts the stamp guards. It refuses to report success from a check that found nothing: a page reworded so no stamp is left fails with that stated, rather than passing vacuously. That path needed the `|| true` on the capture rather than on a later pipeline -- under `pipefail` a grep matching nothing aborted the script before the diagnostic, turning a reworded page into a silent non-zero exit. ## The refresh that makes it pass Five numbers and the stamp, all re-derived from the tree: | claim | was | now | |---|--:|--:| | catalog stamp (x2) | `26a9cc13` | `00372dfe` | | total operations | 570 | 575 | | hand-written operations | 21 | 26 | | hand-written `wgmma` row | 9 | 14 | | catalog entries | 1010 | 1015 | Generated stays 549 across 35 modules and every area row is unchanged, because #1085's five new catalog entries are aliases onto existing structural ops -- exactly what the page's "several intrinsics can share one structural op" sentence predicts. The +5 hand-written are all `wgmma`, from #1075, #1077 and #1079. The `wgmma` row's description moves with its count, as in #1052: it said "the m64n64k16 bf16/f16 shapes", and the module now holds four shape/type pairs -- `m64n64k16` bf16 and f16, `m64n128k16` bf16 (#1079), `m64n64k8` tf32 (#1077). ## Verification - Four mutations, each failing with its own message: a stale stamp, only one of the two stamps updated, the page reworded so none is found, and an edited `catalog.json`. The healthy tree passes. - Every count re-checked against the tree: 26 + 549 = 575, both tables sum to their stated totals (7 rows to 26, 11 rows to 549), and each still lists every module on disk. - All eleven guard scripts pass, `check-spdx-headers.sh` accepts the new file, `cuda-intrinsics-gen -- check` reports outputs current, the workflow parses with all nine jobs on `ubuntu-latest`, and the book builds clean under `sphinx-build -W`. `just check-guards` should list this script too, one line beside the other ten. Its recipe is being restructured in #1102, so that line is left for a follow-up rather than conflicting; no guard enforces that list, so CI is unaffected either way. Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
|
I updated the PR description to be clearer and more precise about what is already working and what is still needed. I also fixed the one failing example. I published The remaining upstream items I see are:
The VMM implementation itself is already present in #233. I am also treating primitive Once those upstream pieces are in, I will replace the temporary branch dependencies here with the published If we close those items without dropping existing cuda-oxide capability, I think this is in good shape to merge. |
|
Thanks for the fixes and for publishing oxide-artifacts. The interop example is looking good. Follow-ups now on #233:
Re: the CUDA floor: the shared host-side crates keep cuda-oxide's 12.x support using your probe approach. The bindings floor drops to 12.0, the elapsed-time helper goes back to the v1/v2 dispatch under the probed cfg, multicast goes back to a header probe (your cuda-core build script, verbatim), and CI here goes back to 13.1.1. cutile enforces its own 13.2 floor in cutile-compiler at tool discovery, so SIMT users below that never see it. We added a 12.x compile lane for the shared crates so we don't silently break 12.x. One thing we should settle now rather than later: the cuTe DSL will have similar versioning constraints to cuTile (it has a tileiras-like binary for compiling cute dsl). I think the pattern should be per-stack floors enforced at each stack's tool boundary, with the shared crates at the lowest common floor. Does that match what you expect? I'll clean up the DCO trailers before release. |
|
Correction on the floor: 12.0 is lower than what the current wrappers support. The 12.x lane found what the shim calls directly: cuCtxSetFlags (12.1), cuMemAdvise_v2 and cuMemPrefetchAsync_v2 (12.2), cuEventElapsedTime_v2 (12.8), and cuLaunchHostFunc_v2 plus the CUhostTaskSyncMode enum (13.x only). So the floor is 12.8 for now. The 13.x-only host-function launch goes through a probed helper in Going below 12.8 will require some work... We'd need to route the elapsed-time call through the existing helper and add fallbacks for the three 12.1/12.2 symbols. The commits list them. Will cuda-oxide require CUDA versions < 12.8? |
Every guard under `scripts/` is wired twice: a step in a workflow, and a line in the Justfile's `check-guards` recipe. The Justfile states that as a contract -- "`check-guards` covers the status-guard, naming-guard and cargo-deny workflows in full" -- and nothing enforced it. `check-book-catalog-stamp.sh` landed in NVIDIA#1106 wired into `status-guard.yml` and never added to `check-guards`, so twelve of the thirteen guards ran locally and "in full" was false. The first version of this check searched both files for each script's name. NVIDIA#1133's review was right that a text search is not enforcement evidence, and gave the counterexamples: the same text inside a `run:` block that only echoes it, a step behind `if: false`, or a reusable workflow nothing calls. All three left the search green while no pull request ran the guard. This reads structure. **Workflows are a graph.** The YAML is parsed, and reachability computed: a workflow is an entry point only if it triggers on `pull_request` or `pull_request_target`; a reusable workflow counts only if a reachable job `uses:` it, resolved to a fixpoint; a job or step proven `if: false` is dropped; and anything that may `continue-on-error` is not credited, because a swallowed failure is not enforcement. **A `run:` script is parsed into commands**, and a guard counts only in command position. `echo "bash scripts/x.sh"` runs `echo`. Leading `VAR=value` assignments and wrappers (`bash`, `sh`, `env`, `.`, `source`, `exec`, `time`, `sudo`) are stepped over, comment lines are dropped, and `&&`, `||`, `;`, `|` split a line so a guard buried mid-pipeline still counts. **The Justfile gets the same treatment**, as the review asked. Recipes and their dependencies are a graph, walked from `check`, so a guard moved into a recipe `check` does not depend on stops counting. A body line prefixed with `-` is just's `continue-on-error` and does not enforce. The same command-position rule applies, so a recipe that echoes an invocation is not running it. Reachability is conservative in one direction only, on purpose. An `if:` that is not literally `false` is treated as reachable, because `github.event_name == 'pull_request'` cannot be evaluated statically -- the guard never calls a live step dead. It is strict the other way: a step that may be skipped, or may swallow its failure, is not coverage. One subtlety worth recording, since it decides whether any of this works: YAML 1.1 reads a bare `on` as the boolean true, so PyYAML hands back `{True: ...}` for `on:` written unquoted, which is how all eleven workflows here write it. `document.get("on")` is `None` on every one of them. Both keys are looked up, and a self-test refuses if no workflow appears to trigger on `pull_request` -- the shape that would otherwise make the reachable set silently empty. Verified against every case, nine that must fail and seven that must refuse: | the guard is defeated by | caught as | |---|---| | the `run:` text only echoed | executed by `just check` but by no workflow | | a step behind `if: false` | same | | only in a reusable workflow nothing calls | same | | a step with `continue-on-error: true` | same | | a job with `continue-on-error: true` | same | | moved to a workflow with no `pull_request` trigger | same | | a Justfile line prefixed with `-` | executed by a workflow but not by `just check` | | moved to a recipe `check` does not reach | same | | the Justfile echoing it | same | | broken input | refusal | |---|---| | nothing triggers on `pull_request` | parse self-test failed, naming the `on:` case | | the `check` recipe renamed | parse self-test failed | | the `check-guards` recipe renamed | parse self-test failed | | `check` no longer depends on `check-guards` | not reachable from `check` | | a job calls a workflow without `workflow_call` | it would never run | | a job calls a workflow that does not exist | named | | a workflow file is not valid YAML | named, with the parser error | | no PyYAML available | refuses with the install command | Unmutated: "OK: all 14 guard scripts are executed by a pull-request workflow (11 reachable of 11) and by `just check` (8 reachable recipes)", exit 1 on failure, all fourteen guards still pass individually, 0.09s, no cargo, no network, no toolchain. The interpreter is probed rather than assumed: the first `python3` on PATH is not always the one with PyYAML -- where Homebrew shadows the system Python it is not -- so `python3`, `/usr/bin/python3` and `python` are tried in turn and a missing parser is a refusal with the install command, never a pass. Note for review: NVIDIA#1102 also edits the Justfile, in the `test-cuda` recipe around line 105. The `check-guards` change here is around line 235, so they do not overlap textually. Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
Every guard under `scripts/` is wired twice: a step in a workflow, and a line in the Justfile's `check-guards` recipe. The Justfile states that as a contract -- "`check-guards` covers the status-guard, naming-guard and cargo-deny workflows in full" -- and nothing enforced it. `check-book-catalog-stamp.sh` landed in NVIDIA#1106 wired into `status-guard.yml` and never added to `check-guards`, so twelve of the thirteen guards ran locally and "in full" was false. ## Before -> after The first version of this check searched both files for each script's name. NVIDIA#1133's review was right that a text search is not enforcement evidence: before: grep finds "check-book-catalog-stamp.sh" in status-guard.yml -> OK: all 14 guard scripts run in both places after: the step is `echo "bash scripts/check-book-catalog-stamp.sh"` -> error: these guards are executed by `just check` but by no pull-request workflow: check-book-catalog-stamp.sh Same for a step behind `if: false`, a step or job with `continue-on-error`, a guard in a reusable workflow nothing calls, a guard moved to a workflow with no `pull_request` trigger, a Justfile line prefixed with `-`, a recipe `check` never reaches, and a Justfile line that echoes the invocation. Nine ways to defeat the old check; all nine now fail. ## How Workflow YAML is parsed and reachability computed as a graph: an entry point must trigger on `pull_request`/`pull_request_target`; a reusable workflow counts only if a reachable job `uses:` it, to a fixpoint; a job or step proven `if: false` is dropped; anything that may `continue-on-error` is not credited. A `run:` script is split into commands and a guard counts only in command position, stepping over `VAR=value` prefixes and the `bash`/`sh`/`env`/`.`/ `source`/`exec`/`time`/`sudo` wrappers. The Justfile gets the same treatment: recipes and dependencies are a graph walked from `check`, and a `-` prefix is just's `continue-on-error`. Conservative in one direction only, on purpose: an `if:` that is not literally `false` is treated as reachable, because `github.event_name == 'pull_request'` cannot be evaluated statically. The guard never calls a live step dead. It is strict the other way -- a step that may be skipped, or may swallow its failure, is not coverage. ## No third-party parser The YAML is read by a strict-subset reader in the script rather than PyYAML. PyYAML is **not** in the `actions/runner-images` preinstalled manifest, so depending on it would risk the job failing on merge, and no other guard in this family needs a third-party package -- on a repo that already works around macOS's bash 3.2 and BSD grep for the same reason. The reader covers exactly what these files use: block mappings and sequences, plain and quoted scalars, literal `|` and folded `>` block scalars with chomping indicators, flow sequences, comments, one leading document marker. Anything else -- a tab, an anchor, an alias, a tag, a flow mapping, a second document -- is a refusal, never a guess. That is what separates it from the text search: it cannot silently misread, only decline to read. Folding is implemented rather than approximated because `book.yml` folds one command across two lines; treating it as literal would invent a second command whose first token is `cuda-oxide-book`. Where PyYAML *is* installed the script uses it as an **oracle**: it compares its own parse against PyYAML's on every workflow, over exactly the fields the walk reads, and refuses on any disagreement. Locally that reports "agrees with PyYAML on all 11 workflows". So the reader is checked, not trusted, without being required. One subtlety the whole walk depends on: YAML 1.1 reads a bare `on` as the boolean true, so `on:` written unquoted is the key `True`, and `document.get("on")` is `None` on all eleven files. Both keys are looked up. ## Tests The nine defeats above were originally verified by hand-mutating this repository, which is neither repeatable nor visible in CI. They are now fixtures the guard asserts about itself on every run, alongside the controls that catch a rule grown too strict (a live step, a guard mid-pipeline, a called reusable workflow, `@`-suppressed Justfile lines) and four reader refusals. Each was confirmed to bite by breaking the implementation it guards: | implementation broken | self-test that failed | |---|---| | step `if`/`continue-on-error` check removed | a step behind `if: false` was counted | | job `continue-on-error` check removed | a job with continue-on-error was counted | | command position -> any token | the invocation only echoed was counted | | reusable-workflow fixpoint removed | a called reusable workflow was not counted | | Justfile `-` skip removed | a `-` prefixed script path was counted | | Justfile reachability walk removed | a recipe `check` does not reach was counted | | reader's tab refusal removed | the reader accepted a tab | | reader's flow-mapping refusal removed | the reader accepted a flow mapping | Unmutated: all fourteen guards pass individually, 0.02s, needs only python3 -- no cargo, no network, no third-party package. Note for review: at 835 lines this is the largest guard in the family by some margin, and that is what structural parsing costs. Say so if you would rather have the dependency and a shorter script. NVIDIA#1102 also edits the Justfile, in the `test-cuda` recipe around line 105; the `check-guards` change here is around line 235, so they do not overlap. Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
|
Thanks, I went through the current #233 head again. CUDA 12.8+ is fine for cuda-oxide; we do not need support below 12.8. The The only remaining changes we'd need on #233 are:
Could you also refresh the #233 description? It still describes peer/VMM as omitted, Once those items and the planned DCO cleanup are done, I think #233 is ready to be the upstream release piece. I’ll handle the dependency switch, locks, local artifact identity, scaffolds/docs, and final integration here after 0.3.1 is published. |
|
@nihalpasham any thoughts on how to address the CodeQL alerts? |
@elibol - The 7 new alerts are pre-existing main-branch alerts (all 13 are open on main, created by the Aug 28 scan) that CodeQL misattributed to this PR because the diff was large. They're all rust/access-invalid-pointer on |
bf81c4a to
9cf43b9
Compare
Main's NVIDIA#1198 gates CI on sync-example-locks.sh --check; align the git-pinned workspaces the way its own docs prescribe (--align never touches the git pins). 1920 pins moved, 221 locks agree.
cuda-bindings was never given the standalone [workspace] table its two siblings got at retirement, and main's stricter license guard now walks every tracked manifest. Detach it the same way, commit the three standalone locks the guard's --locked metadata needs, and name all three retired roots first-party so their declared dependencies stay in the inventory.
The NVIDIA#233 branch gained the extern "C" wrapper ABI and the cuInit coercion regression nihalpasham asked for; manifests are unchanged, so this is a pure pin substitution across all 222 locks.
reborrow, sparse_mma_ordered_float, and shared_stage_select arrived on main with local path deps and root LaunchConfig imports; convert them the established way and seed their locks from vecadd's per the sync script's doc, which lands them aligned on the first try (224 locks agree). This also clears the clippy and cargo-deny lanes, which were failing on the unconverted expansion and the stale --locked resolve.
cutile-rs#233 was squash-merged into cutile-rs main as a2c1ea4 and its feature branch deleted, so the git dependencies on feat/cuda-oxide-host-support no longer resolve. Point the 222 manifests that declare cuda-bindings, cuda-core, and cuda-async at branch main and repin the 225 lockfiles that carry the four shared packages (cuda-bindings, cuda-core, cuda-core-derive, cuda-async, all 0.3.0) at a2c1ea4bb4ae804f573a879785c96f870a4e1b84. The shared crates' manifests are unchanged since the previous pin, so the lock rewrite is a pure source substitution; every one of the 225 workspaces resolves with --locked. The only change in the four crates between aaf1364 and main is the host-side toolkit floor moving from CUDA 12.8 to 13.0 (MIN_CUDA_VERSION 13000 in cuda-bindings/build.rs); CI installs 13.1.1 and already clears it. From here cuda-oxide tracks cutile-rs main; advancing the pin is a deliberate cargo update, reviewed like any dependency bump. Signed-off-by: Melih Elibol <elibol@users.noreply.github.com>
Point the 222 manifests that declare cuda-bindings, cuda-core, and cuda-async at the v0.3.1 tag instead of branch main, and re-resolve the 225 lockfiles that carry the four shared packages with cargo update, so they now record cuda-bindings, cuda-core, cuda-core-derive, and cuda-async 0.3.1 at the tag's current target, 9f68be2d0aff0882ec329a453ddc756ad6e4f9e7. Every workspace resolves with --locked and the example locks still agree on one version per crate. Referencing the tag rather than a commit means a re-tag before the final release costs one cargo update pass and no manifest edits. Between a2c1ea4 and the tag the shared crates gained toolkit-discovery parity (CUDA_HOME fallback, targets/<arch>/include lookup, CUDA_TOOLKIT_TARGET_DIR), executed host-crate tests, and the host runtime audit follow-ups; DType became an unsafe trait and several driver wrappers now return Result. None of those surfaces are used by cuda-oxide's own code, and the simt:: modules are unchanged, so nothing here needed adapting. Signed-off-by: Melih Elibol <elibol@users.noreply.github.com>
… copy from
The host runtime now comes from the cutile-rs crates, whose cuda-oxide
API lives under cuda_core::simt and cuda_async::simt. Every snippet a
user copies has to say so: the README, the cuda-host README, the book
pages, two example READMEs, and the two cargo oxide new templates.
The scaffold also pointed cuda-core, cuda-async, and cuda-bindings at
this repository's git URL. With the local copies retired that either
resolves to nothing or to a stale copy on an old revision, so the
templates now take the crates.io release (SHARED_HOST_CRATES_VERSION)
and a test checks no git pin comes back. cuda-bindings leaves the
async template: nothing in it names cuda_bindings.
Book links that pointed at crates/cuda-core/{src,tests} now point at
the simt files in cutile-rs.
Signed-off-by: Nihal Pasham <nihalp@nvidia.com>
… the shared crates The switch to the cutile-rs crates left the old crates/cuda-bindings, crates/cuda-core, and crates/cuda-async on disk as standalone packages "so the example workspaces keep building unchanged". Nothing built them: every example already took the git pin, no CI lane tested them, and their only effect was to make `cargo oxide new` resolve `cuda-core` from this repository's git URL to a stale 0.2.1 copy. They are gone, together with the dead `#[derive(DeviceCopy)]` in cuda-macros (cuda-core now re-exports cuda-core-derive's), the three retired roots in the license inventory guard, the CODEOWNERS rule for the moved crate, and the comments that pointed at the old paths (root Cargo.toml, doctor.rs, spine_kernel_ptx.rs, docs.yml). The shared cuda-bindings loads libcuda at run time through libloading instead of linking it, so the libcuda.so.1 stub shadowing in unit-tests.yml, docs.yml, the Justfile, and CONTRIBUTING was dead plumbing; it is removed and the `--exclude cuda-bindings` doctest exclusions with it. bindgen now also reads curand.h, which the flake provides and the docs name. cutile_inter_kernel leaves the license-policy exemption (NVIDIA#953): its git source is allowed and every cutile-rs crate is Apache-2.0, so it is judged like every other example. dependency-licenses.csv describes the crates as resolved (0.3.1 from cutile-rs) and gains cuda-core-derive and dashmap rows. Docs: the CUDA floor is 13.0 with an R580+ driver (README, installation, building-from-source, example READMEs), the remaining root-path snippets move to the simt paths, the `f16` feature is explained where it is enabled, the cuda-host re-export of cuda_async says which surface is which, and the docs.rs links for LaunchConfig point at the simt page. Seven trybuild fixtures get the line-neutral formatting fixes the path rewrite skipped. Signed-off-by: Nihal Pasham <nihalp@nvidia.com>
…arity Two things the crate switch showed nothing was checking: - Some forty book, README, and scaffold lines kept spelling the old root paths (cuda_core::LaunchConfig, cuda_async::device_operation::...), which on the shared crates either do not resolve or bind cutile's Tile-side types. No lane compiles those snippets. check-host-api-paths.sh greps them for the root spellings that are wrong on the shared crates; it fails on the pre-fix tree at 50 sites and passes now. - The shared-crate release is named once in the root workspace and copied into 221 example manifests and the `cargo oxide new` templates. check-shared-crate-pin.sh requires every copy to agree with the root on version and form (git tag or crates.io), so the crates.io repin cannot leave a straggler and the scaffold cannot drift. Both run in the status-guard workflow and in `just check-guards`. Signed-off-by: Nihal Pasham <nihalp@nvidia.com>
5204f10 to
ecf70b4
Compare
…e crate is the publish source The backend wrote artifact bundles with the in-tree crates/oxide-artifacts (a path dependency) while the shared cuda-core from cutile-rs reads them through crates.io oxide-artifacts 0.2.1. Same version, same code, but two crates to rustc: the root lock carried both, and nothing tied the two versions together. It worked only because just bytes cross the boundary. The root workspace and the backend workspace now take oxide-artifacts from crates.io like every other consumer, so writer and reader are one crate. crates/oxide-artifacts stays a workspace member as the source the release is published from; format changes go bump, publish, then move the requirement and relock. check-oxide-artifacts-parity.sh keeps the in-tree version, both manifest requirements, and the version resolved in every lock equal, and fails if any lock stops consuming the registry copy. It fails on a drifted in-tree bump and passes on this tree. Runs in status-guard and just check-guards. Validated on an RTX 5090: cargo-oxide, mir-importer, ptx-schedule and backend lib tests, workspace clippy and fmt, all guards, and the full smoketest (222/222) with the backend writing through the registry crate. Signed-off-by: Nihal Pasham <nihalp@nvidia.com>
|
@elibol - where we are, after a full review and a runtime pass:
What we need for 0.3.1:
Then I repoint the dependency lines to the release, relock, rerun the smoketest, and we merge. |
cutile-rs 0.3.1 is on crates.io (cuda-bindings, cuda-core-derive, cuda-core, cuda-async, and the cutile stack), so the git-tag pin that bridged the gap goes away: - root workspace and all 224 example workspaces (nested ones included) name `"0.3.1"` from the registry; `features = ["f16"]` and the `package = "cuda-core"` rename in cutile_inter_kernel are kept, and cutile_inter_kernel's cutile / cutile-compiler pins move with them - every Cargo.lock re-resolved with `cargo update -w`: 684 source lines flip from `git+...cutile-rs?tag=v0.3.1` to the registry plus a checksum each; no other package moves - deny.toml drops the cutile-rs `allow-git` entry (pliron is the only git source left) - dependency-licenses.csv: the four rows become "1st Party (crates.io)" - async_mlp and async_vecadd drop their direct cuda-bindings dependency; neither names `cuda_bindings` anywhere - comments in the two license guards and the cutile_inter_kernel README stop describing a git dependency Provenance, checked before this commit: the published .crate archives match the v0.3.1 tag tree (cdc69c1) file for file, and their sha256 matches both the crates.io index and the new lock checksums. The tag moved from 9f68be2 (the previous pin) to cdc69c1 for the release; the shared-crate delta is confined to the Tile-side `AsyncKernelLaunch` argument storage in cuda-async plus workspace lint config. The `cuda_async::simt` launch path cuda-oxide uses is unchanged. Signed-off-by: nihalpasham <nihalp@nvidia.com>
Run cutile-rs workflows from the nested tree, and point semver-checks and CUDA 13 shared-crate builds at that workspace after NVIDIA#1102. Skip cutile-rs in Oxide license inventory and SPDX checks so the import is not judged by Oxide's CSV and headers. ci: run the cutile-rs workflow on main This import lands cutile-rs on private main, so main pushes should run the workflow. Keep the paths filter off so copy-pr-bot mirror pushes still match.
Run cutile-rs workflows from the nested tree, and point semver-checks and CUDA 13 shared-crate builds at that workspace after NVIDIA#1102. Skip cutile-rs in Oxide license inventory and SPDX checks so the import is not judged by Oxide's CSV and headers. ci: run the cutile-rs workflow on main This import lands cutile-rs on private main, so main pushes should run the workflow. Keep the paths filter off so copy-pr-bot mirror pushes still match. fix: stop seeding inline kernel-arg pointers with null CodeQL followed that null fill into the test read of a parameter pointer.
Update workspace members, CI, and developer tooling for the nested SIMT tree. Search reserved-prefix literals under cuda-oxide/crates after NVIDIA#1102 removed the root host crates. Keep CODEGEN_CRATE_SUBDIR only in tests. ci: match stacked PR bases with one glob per line Oxide pull_request gates run for main, ci/**, and layout/** so stacked private PRs do not need a new branch name in seven workflows. One pattern per line keeps the conflict to a single added or removed line.
Update workspace members, CI, and developer tooling for the nested SIMT tree. Search reserved-prefix literals under cuda-oxide/crates after NVIDIA#1102 removed the root host crates. Keep CODEGEN_CRATE_SUBDIR only in tests. ci: match stacked PR bases with one glob per line Oxide pull_request gates run for main, ci/**, and layout/** so stacked private PRs do not need a new branch name in seven workflows. One pattern per line keeps the conflict to a single added or removed line.
Update workspace members, CI, and developer tooling for the nested SIMT tree. Search reserved-prefix literals under cuda-oxide/crates after NVIDIA#1102 removed the root host crates. Keep CODEGEN_CRATE_SUBDIR only in tests. ci: match stacked PR bases with one glob per line Oxide pull_request gates run for main, ci/**, and layout/** so stacked private PRs do not need a new branch name in seven workflows. One pattern per line keeps the conflict to a single added or removed line. fix: retarget checks after nesting the SIMT tree The source-reference guard and its workflow step still assumed scripts and crates at the git root. Drop the private ci/** and layout/** pull_request branches the path fix added. docs: keep the Oxide README at the repository root GitHub only renders the root file, so the nested layout should not replace the Oxide overview with a short index. Point links in that file at the nested SIMT tree.
Update workspace members, CI, and developer tooling for the nested SIMT tree. Search reserved-prefix literals under cuda-oxide/crates after NVIDIA#1102 removed the root host crates. Keep CODEGEN_CRATE_SUBDIR only in tests. ci: match stacked PR bases with one glob per line Oxide pull_request gates run for main, ci/**, and layout/** so stacked private PRs do not need a new branch name in seven workflows. One pattern per line keeps the conflict to a single added or removed line. fix: restore paths after host-crate lift Join the lifted crates to the root workspace with inlined cutile metadata (edition 2021, 0.3.1). Nested cutile-rs path-depends on ../cuda-*; Oxide keeps the crates.io pin so example workspaces do not see two cuda_core crates. Host-crate tests run from the git root. Oxide rustdoc excludes the imported crates. PR gates also run when the base is layout/cuda-oxide-crates.
Update workspace members, CI, and developer tooling for the nested SIMT tree. Search reserved-prefix literals under cuda-oxide/crates after NVIDIA#1102 removed the root host crates. Keep CODEGEN_CRATE_SUBDIR only in tests. ci: match stacked PR bases with one glob per line Oxide pull_request gates run for main, ci/**, and layout/** so stacked private PRs do not need a new branch name in seven workflows. One pattern per line keeps the conflict to a single added or removed line. fix: restore paths after host-crate lift Join the lifted crates to the root workspace with inlined cutile metadata (edition 2021, 0.3.1). Nested cutile-rs path-depends on ../cuda-*; Oxide keeps the crates.io pin so example workspaces do not see two cuda_core crates. Host-crate tests run from the git root. Oxide rustdoc excludes the imported crates. PR gates also run when the base is layout/cuda-oxide-crates.
Update workspace members, CI, and developer tooling for the nested SIMT tree. Search reserved-prefix literals under cuda-oxide/crates after NVIDIA#1102 removed the root host crates. Keep CODEGEN_CRATE_SUBDIR only in tests. ci: match stacked PR bases with one glob per line Oxide pull_request gates run for main, ci/**, and layout/** so stacked private PRs do not need a new branch name in seven workflows. One pattern per line keeps the conflict to a single added or removed line. fix: restore paths after host-crate lift Join the lifted crates to the root workspace with inlined cutile metadata (edition 2021, 0.3.1). Nested cutile-rs path-depends on ../cuda-*; Oxide keeps the crates.io pin so example workspaces do not see two cuda_core crates. Host-crate tests run from the git root. Oxide rustdoc excludes the imported crates. PR gates also run when the base is layout/cuda-oxide-crates.
…t source (#1488) ## What this adds `cargo oxide new` wrote a manifest that took `cuda-device` and `cuda-host` from the repository's git URL but `cuda-core` (and `cuda-async` with `--async`) from crates.io 0.4.0. `cuda-host` depends on `cuda-core` by path inside that checkout, so every scaffolded project resolved two `cuda-core` packages and the code `#[cuda_module]` generates failed to type-check. The templates now name every CUDA crate from the same git source, so cargo resolves one copy of each. ```text Before: cuda-host = { git = <repo> } cuda-core = "0.4.0" Cargo.lock: cuda-core x2 (registry + git), cuda-bindings x2, cuda-core-derive x2 error[E0308]: expected `cuda_core::simt::context::CudaContext`, found `CudaContext` 7 errors sync, 13 errors async; nothing runs After: cuda-host = { git = <repo> } cuda-core = { git = <repo> } cuda-async = { git = <repo> } Cargo.lock: cuda-core x1, cuda-bindings x1, cuda-core-derive x1, all at one commit PASSED: all 1024 elements correct ``` ```rust #[cuda_module] mod kernels { #[kernel] pub fn vecadd(a: &[f32], b: &[f32], mut c: DisjointSlice<f32>) { /* ... */ } } let module = unsafe { kernels::load(&ctx)? }; // generated code names cuda-host's CudaContext ``` ## What we fixed - Both templates take `cuda-core` and `cuda-async` from `GIT_REPO`, the source `cuda-host` comes from; `SHARED_HOST_CRATES_VERSION` is gone. - `check-shared-crate-pin.sh` section 3 requires `git = "{GIT_REPO}"` on every host-crate line in the templates and rejects a crates.io requirement there. - New test `scaffold_takes_every_cuda_crate_from_one_git_source` asserts every `cuda-*` line in both templates names the same git source as `cuda-device`. - The manifest comment and the status-guard step name that still described a crates.io scaffold now describe the git source. ## Scaffold dependency contract Every CUDA crate in a new project comes from one git source with no version requirement. The first build locks all of them to one commit of that repository, and `cargo update` moves them together. A crates.io `cuda-core` or `cuda-async` beside a git `cuda-host` is always a second crate and does not compile. <details><summary>Implementation</summary> - The regression began at bd6c3ce (#29, 2026-10-01), which moved `cuda-oxide` from crates.io `cuda-core` 0.3.1 onto the git-root path copies while the scaffold kept its crates.io constant; #1466 only dropped the `version = "=0.4.0"` qualifier. Lockfiles resolved at the commit before #29 hold one `cuda-core`; at #29, before #1466 and at #1466 they hold two. - Cargo identifies a package by name, version and source. A path dependency declared inside a git-sourced crate resolves to that git source, and nothing unifies it with a registry copy, so the scaffold has to name `cuda-core` from the source `cuda-host` uses. - This is the pre-#1102 form of the template minus `cuda-bindings`, which no user code names and which `cuda-core` pulls by path. - The failures are identity mismatches on `CudaContext`, `CudaModule`, `DeviceBuffer` and the `DriverError` closure: 6 E0308 plus 1 E0277 in the sync template; 9 E0308, 3 E0277 and 1 E0599 in the async one. - This supersedes the #1466 plan to derive the scaffold's version from the root manifests: with no version in the template there is nothing to derive. </details> ## Verification Checked at `180770a9`: - ✅ `cargo test -p cargo-oxide --all-targets`: 244 passed. With the old template lines restored and the new tests kept, 3 scaffold tests fail. - ✅ `cargo clippy -p cargo-oxide --all-targets -- -D warnings`, `cargo fmt --check` and rustdoc with `-D warnings` clean. - ✅ All 18 `check-*.sh` guards pass. `check-shared-crate-pin.sh` rejects four reintroduced forms (`cuda-core = "0.4.0"`, `{ version = "0.4.0" }`, a different git URL, `cuda-async = "0.4.0"`) with exit 1. - ✅ Baseline: main's `cargo oxide new` for both templates fails to build, 7 and 13 errors, with two `cuda-core`, `cuda-bindings` and `cuda-core-derive` per lockfile (and two `cuda-async`). - ✅ Fix: `cargo oxide new` then `cargo oxide run` for both templates on an RTX 5090 (sm_120a, CUDA 13.4): `PASSED: all 1024 elements correct`, `compute-sanitizer --tool memcheck` 0 errors, one `cuda-core` per lockfile and zero registry `cuda-*` packages. Same result on the author's RTX 5060 Laptop (sm_120, CUDA 13.3). - ✅ Merges cleanly with #1489 in either order; after both, every template line names `https://github.com/NVIDIA/cuda-rust.git`. ## Caveats - The test and guard compare the git URL only. A `rev`, `branch` or `tag` added to one line still resolves a second `cuda-core`, even at the same commit; a follow-up tightens the comparison to the full source. - No CI job compiles a scaffolded project, which is how the broken template survived from 2026-10-01; a scaffold compile gate is a separate follow-up. - The GPU runs used a cached backend; the standalone backend build from the scaffold's git checkout is unchanged by this PR and was not re-run here. - `GIT_REPO` still names `NVlabs/cuda-oxide`, which GitHub redirects; #1489 renames it. Closes #1459. --------- Signed-off-by: Alejandro Soto Franco <sotofranco.eng@gmail.com> Signed-off-by: nihalpasham <nihalp@nvidia.com> Co-authored-by: nihalpasham <nihalp@nvidia.com>
## What This PR Does Moves the orphaned `**Rust**` row in `cuda-oxide/cuda-oxide-book/getting-started/installation.md` back inside the `Prerequisites` table directly below `**Clang**`: 1. **Restore the `**Rust**` row to the `Prerequisites` table (`cuda-oxide-book/getting-started/installation.md`)** --- ### 1. Why the `**Rust**` Row Rendered Outside the Table When `#1102` added the `libcuda` runtime-loading paragraph under `Prerequisites`, it inserted the paragraph between the `**Clang**` and `**Rust**` rows, splitting the Markdown table so Sphinx rendered the `**Rust**` row as literal pipe text at the end of the paragraph: ```text BEFORE (main): | **Clang** | 21+ | `clang-21` ... | The host runtime does not link `libcuda` at build time... | **Rust** | 1.99.1 (pinned) | Pinned in `cuda-oxide/rust-toolchain.toml` | AFTER (This PR): | **Clang** | 21+ | `clang-21` ... | | **Rust** | 1.99.1 (pinned) | Pinned in `cuda-oxide/rust-toolchain.toml` | The host runtime does not link `libcuda` at build time... ``` --- ## Verification * **`just -f cuda-oxide/Justfile book`**: **PASS** (48 HTML pages built with `-W` warnings-as-errors, 0 warnings; `<tr class="row-odd">` for **Rust** confirmed inside `<tbody>` in `getting-started/installation.html`). * **Repository Guards (`scripts/check-*.sh`)**: **18 / 18 PASS** (including `check-toolchain-parity.sh` and `check-cli-doc-coverage.sh`). Signed-off-by: Canberk Pitirli <canberkpitirli@gmail.com>
What this does
This gets cuda-oxide and cutile-rs onto the same three host-crate package identities before the monorepo move, without making high-level API unification part of that move.
The shape
cutile-rs#233temporarily carries both API families:This PR points cuda-oxide at those shared crates:
This is a namespaced compatibility bridge. It does not rewrite
cuda-hostorcuda-macrosaround the Tile APIs, and it does not decide the eventual unified API.Most Rust edits are path changes. For example:
One mechanical edit needed semantic correction:
cutile_inter_kernelcomposes a TileDeviceOp, so its five Tile-side imports must remain under rootcuda_async. That is fixed at the current head, and the example now compiles and runs on an RTX 5090.What is actually still missing upstream
Peer access: carry
can_access_peer,enable_peer_access, anddisable_peer_access, plus cuda-oxide's real two-GPU VMM/P2P test. The VMM implementation itself is already in#233; peer access and the cross-GPU test are the missing pieces.DeviceCopyderive: preserve the existing one-import API, or agree on a compatible replacement, and carry its trybuild tests:One compatibility question, not a merge gate
Primitive Rust
f16is nightly-only, so making itsDeviceCopyimpl an opt-in feature in a stable shared crate is reasonable. Can we confirm that this is the intended policy?half::f16remains available normally.atomic_f16andf16_stressexplicitly enable the newf16feature.gemm_sol_final, thetcgen05examples, and the matmul examples do not depend on primitivef16or this feature.Compatibility and release gates
CUDA/toolkit support: preserve cuda-oxide's CUDA 12.x/13.1 and toolkit-layout support, or explicitly agree that the shared crates now require CUDA 13.2+ and Cutile's toolkit layout.
Use one published
oxide-artifactscrate:oxide-artifacts 0.2.1is now published. Make both cuda-oxide and the sharedcuda-coreuse that release. The sharedcuda-coreshould re-export the dependency instead of copying its source intocuda_core::simt::artifacts. The copied reader understands the writer's v2 bytes today, but it is a second implementation and its public Rust types have a different identity:Keeping both projects on the same crates.io package gives the compiler and shared runtime one Rust type and one format implementation. No copied implementation, Git dependency, or transitional Cargo patch is needed.
Preserve the bindings C function type: ordinary calls through the shared dynamic loader work, but the generated shim currently changes the function item's Rust type from
extern "C"to the Rust ABI:Preserve bindgen's ABI when generating the public loader shims and keep this assignment as a compile-time regression test. Dynamic loading remains unchanged.
Scaffolds and docs: update
cargo oxide new, repository/book docs, and any examples that still point at the retired local crates.Release/reproducibility: publish
oxide-artifacts 0.2.1andcuda-bindings 0.3.1first, followed bycuda-core 0.3.1→cuda-async 0.3.1. Verify each package, pin the released versions, regenerate affected locks, and keep DCO clean.Final runtime gate: run the peer/VMM P2P path on both GPUs and keep the representative SIMT, async, cooperative, cluster, and interop examples green at the final release head.
What is green now
cuda-coreandcuda-async: 196 passed, 1 ignored on CUDA 13.3 / RTX 5090.#233.vecadd,async_vecadd,cluster,coop_groups_demo,atomic_f16, andcutile_inter_kernel.nvls_all_reducestill lives in cuda-oxide, builds against the shared VMM surface, and correctly skips on these 5090s because they do not have NVSwitch multicast support.cutile-rs#202now separately provides Cutile's root VMM API and runs itsvmm/vmm_multicasttargets from the GPU script. That is useful shared coverage, but it is not a substitute for the missing peer/P2P test.Merge picture
Once those items are closed, this remains a packaging move rather than a capability trade. Safety issues already present in either implementation remain separately tracked work; this migration does not claim to solve them.