Skip to content

refactor: split large files into per-concern module directories - #1190

Merged
nihalpasham merged 18 commits into
mainfrom
refactor/big-file-modularization
Aug 28, 2026
Merged

nihalpasham merged 18 commits into
mainfrom
refactor/big-file-modularization

Conversation

@nihalpasham

@nihalpasham nihalpasham commented Aug 28, 2026 •

Copy link
Copy Markdown
Collaborator

TL;DR: zero functional change. Every commit is a code move. Same behavior, same PTX, same everything, just smaller files.

The problem

Some of our files got genuinely huge:

generated_intrinsic_targets.rs   147,261 lines  (one 69k const + one 76k #[test] fn)
importer generated.rs             94,518 lines  (ONE 51,700-line function)
resolve.rs                        42,000 lines  (559 fns in one file)
render.rs                         25,281
rvalue.rs                         15,707
...and 12 more above 4k

Why that hurts:

  • rust-analyzer: a 147k-line file drags every editing session in that crate
  • compile times: rustc cuts codegen units per module, and a single item can never be split, so that 51.7k-line function is one serial chunk on every build, and it sits in the crates we rebuild most
  • review: a catalog bump used to diff into one monolithic file
  • navigation: finding anything meant scrolling a 42k-line file

What we did

Every 4k+ line file became a directory of small modules, cut along real seams:

before                          after
resolve.rs        42,000   →    resolve/        52 files, one per intrinsic family
render.rs         25,281   →    render/         24 files, one per pipeline stage
rvalue.rs         15,707   →    rvalue/         17 files, one per MIR concern
commands.rs       11,297   →    commands/       20 files, one per subcommand
cuda-macros lib   10,310   →    lib.rs + 16     one per proc-macro
types/target/aggregate/memory/model              5 more source splits
4 big test suites                                split by feature area

And the generator now emits its three monoliths as per-family files, the same way it already sharded dialect-nvvm and cuda-device:

importer generated.rs    94,518   →   generated/                    39 shards + tests/
targets                 147,261   →   generated_intrinsic_targets/  per family, biggest 9.9k
lowering                 17,861   →   generated_intrinsics/         35 shards, biggest 4.5k

Nobody outside a split file notices: each new mod.rs re-exports the old surface, so no call site changed anywhere.

No functionality change

  • 16 of 17 commits are pure moves, verified item-for-item:
    • fn-count parity per file (resolve.rs: 559 fns before, 559 after)
    • test-count parity per crate (38 / 54 / 78 / 131 suite tests unchanged, etc.)
    • the sharding commit was diffed against the old monoliths: 1,017/1,017 dispatch arms, 1,016/1,016 table entries, 549/549 lowering impls, content-identical
  • The three deliberate edits, each flagged in its commit:
    1. rvalue's Aggregate arm body extracted into its own fn (own commit, prev_op order preserved)
    2. two include_str! paths in cuda-macros tests gained one ../ (tests moved a directory deeper)
    3. the generator sharding itself (dispatch becomes a per-family chain; the abi_id test goes from globally-sorted to per-family-sorted + globally-unique, safe because all lookups are linear scans)

Validation

Review guide

@nihalpasham nihalpasham added cargo-oxide The `cargo oxide` subcommand and build automation intrinsics Device intrinsics and libdevice math mappings codegen Device code-generation pipeline (Rust MIR to IR to PTX) llvm-export Textual LLVM/NVVM IR exporter (llvm-export crate) refactor Internal restructuring with no behavior change IR-lowering Lowering between dialects (dialect-mir to LLVM dialect) labels Aug 28, 2026
@nihalpasham nihalpasham changed the title refactor: split every 4k+ line file into per-concern module directories refactor: split large files into per-concern module directories Aug 28, 2026
@nihalpasham
nihalpasham merged commit 40f03e1 into main Aug 28, 2026
52 checks passed
elibol added a commit to elibol/cuda-rust that referenced this pull request Aug 28, 2026
Port the cuda-macros namespace edits into the NVIDIA#1190 module split, and
switch the three examples added on main (ex2_approx_f16,
ptr_without_provenance, shared_debug) to the shared crates with
LaunchConfig from simt::. Lockfiles are placeholders until the relock.
nihalpasham pushed a commit to vyncint/cuda-oxide that referenced this pull request Aug 30, 2026
NVIDIA#1190 split several large files into module directories. Three places in the
tree still point a reader at a path that no longer exists, and following any
of them lands nowhere:

  * `cuda-oxide-codegen/tests/spine_kernel_ptx.rs` sends the reader to
    `mir-lower/src/convert/types.rs` for `is_kernel_func`, now
    `convert/types/func_abi.rs`;
  * `examples/ref_index_projections/README.md` and
    `examples/subslice_projection/README.md` both name
    `mir-importer/src/translator/rvalue.rs`, now the `translator/rvalue/`
    directory.

Two of them needed more than a new path, because the sentence around the path
had gone stale too:

  * the same doc comment cites `lowering.rs:132` for `propagate_kernel_attrs`,
    which is at line 336. A line number in prose rots on the next edit, so this
    names the function in its file instead of pinning a number.
  * `ref_index_projections` says the `Rvalue::Ref` arm "has five cases" in the
    present tense. It has two, and had five before the fix that README
    documents; the description is of the pre-fix code, so it now reads as past
    tense and points at where that arm and its address walk live today.

`examples/atomics/README.md` also names `intrinsics/atomic.rs` and
`ops/atomic.rs`, but those are crate-relative shorthand in a pipeline diagram
and both resolve, so they are left alone.

Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
nihalpasham added a commit to vyncint/cuda-oxide that referenced this pull request Aug 30, 2026
The NVIDIA#1190 module split deleted 16 monolithic files. PR NVIDIA#1197 repointed
the three sites issue NVIDIA#1196 enumerated; this commit sweeps the rest,
including bare-basename spellings the original sweep missed:

- abi_hmm README table rows (convert/types.rs, ops/aggregate.rs)
- book lowering-pipeline.md table rows (ops/memory.rs, ops/aggregate.rs)
- mir-lower README (convert/generated_intrinsics.rs, now a directory)
- translator/layout.rs + lowering_test/calls_and_values.rs comments
  (translator/rvalue.rs)
- convert/ops/arithmetic.rs + ops/memory/tests/mod.rs comments
  (tests/lowering_test.rs, now a directory)
- cargo-oxide README source tree (commands.rs, now a directory)
- render/families/packed.rs comment (resolve.rs, now a directory)
- translator/types.rs comment (place walker lives in rvalue/place_addr.rs)
- intrinsics/indexing.rs doc (sreg dispatch lives in generated/sreg.rs)
- cast_tests README pipeline diagram (rvalue.rs -> rvalue/expr.rs)

Every new pointer was verified against the tree before writing it.
Sweep now returns zero dangling references across full-path,
crate-relative, and bare-basename spellings of all 16 deleted files.

Signed-off-by: nihalpasham <nihalp@nvidia.com>
nihalpasham added a commit that referenced this pull request Aug 30, 2026
#1197)

* docs: repoint the three source references the module split invalidated

#1190 split several large files into module directories. Three places in the
tree still point a reader at a path that no longer exists, and following any
of them lands nowhere:

  * `cuda-oxide-codegen/tests/spine_kernel_ptx.rs` sends the reader to
    `mir-lower/src/convert/types.rs` for `is_kernel_func`, now
    `convert/types/func_abi.rs`;
  * `examples/ref_index_projections/README.md` and
    `examples/subslice_projection/README.md` both name
    `mir-importer/src/translator/rvalue.rs`, now the `translator/rvalue/`
    directory.

Two of them needed more than a new path, because the sentence around the path
had gone stale too:

  * the same doc comment cites `lowering.rs:132` for `propagate_kernel_attrs`,
    which is at line 336. A line number in prose rots on the next edit, so this
    names the function in its file instead of pinning a number.
  * `ref_index_projections` says the `Rvalue::Ref` arm "has five cases" in the
    present tense. It has two, and had five before the fix that README
    documents; the description is of the pre-fix code, so it now reads as past
    tense and points at where that arm and its address walk live today.

`examples/atomics/README.md` also names `intrinsics/atomic.rs` and
`ops/atomic.rs`, but those are crate-relative shorthand in a pipeline diagram
and both resolve, so they are left alone.

Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>

* docs: repoint the remaining 14 references the module split orphaned

The #1190 module split deleted 16 monolithic files. PR #1197 repointed
the three sites issue #1196 enumerated; this commit sweeps the rest,
including bare-basename spellings the original sweep missed:

- abi_hmm README table rows (convert/types.rs, ops/aggregate.rs)
- book lowering-pipeline.md table rows (ops/memory.rs, ops/aggregate.rs)
- mir-lower README (convert/generated_intrinsics.rs, now a directory)
- translator/layout.rs + lowering_test/calls_and_values.rs comments
  (translator/rvalue.rs)
- convert/ops/arithmetic.rs + ops/memory/tests/mod.rs comments
  (tests/lowering_test.rs, now a directory)
- cargo-oxide README source tree (commands.rs, now a directory)
- render/families/packed.rs comment (resolve.rs, now a directory)
- translator/types.rs comment (place walker lives in rvalue/place_addr.rs)
- intrinsics/indexing.rs doc (sreg dispatch lives in generated/sreg.rs)
- cast_tests README pipeline diagram (rvalue.rs -> rvalue/expr.rs)

Every new pointer was verified against the tree before writing it.
Sweep now returns zero dangling references across full-path,
crate-relative, and bare-basename spellings of all 16 deleted files.

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

---------

Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
Signed-off-by: nihalpasham <nihalp@nvidia.com>
Co-authored-by: nihalpasham <nihalp@nvidia.com>
@nihalpasham
nihalpasham deleted the refactor/big-file-modularization branch September 5, 2026 15:25
vyncint added a commit to vyncint/cuda-oxide that referenced this pull request Sep 15, 2026
…t track

NVIDIA#1190 split large files into module directories and left three prose
references pointing at paths it had removed; NVIDIA#1197 repointed those and 14 more.
Nothing catches that: a path in prose is compiled by nothing,
`check-host-api-paths.sh` checks Rust API paths rather than files, and the book
gate only builds.

"In the repository" means tracked by git, not present on disk. Those are
different questions and only the first is reproducible -- a path that exists as
a local build artifact, or an untracked file someone happens to have, resolves
for them and nobody else, so a filesystem test makes the guard's answer depend
on whose checkout it ran in.

Generated outputs are declared, never inferred. An earlier revision skipped any
path whose parent directory held no tracked file, reasoning that such a
directory must be an output location. That is backwards: deleting or
misspelling a directory produces exactly the same signal as generating into
one, so the broken reference this guard exists to catch was the case it let
through. `GENERATED_OUTPUT_PATHS` is now the whole accommodation -- one entry,
`crates/fuzzer/artifacts/summary.jsonl`, which `run_seed.py` writes -- and it
is verified rather than trusted: an entry that becomes tracked, or that nothing
refers to any more, is an error instead of a silent exemption.

Roots are verified too. `docs/` was in the original list and holds no tracked
file in this repository, so the pattern anchored there matched nothing and
would have gone on matching nothing after a rename. The check that caught it
runs before every sweep.

Paths containing a traversal segment are reported rather than resolved, since
they cannot name a tracked file relative to the repository root.

The self-test runs the real scanner over four cases -- a missing file under a
tracked directory, a missing file under a directory the repo does not have, a
file that exists only in this checkout, and a tracked file -- and requires the
first three to be reported and the fourth not. It takes the tracked example
from the index rather than naming this script, which had made the self-test
depend on the guard already being committed.

Crate-relative and bare-basename spellings are still not checked. NVIDIA#1197's
follow-up fixed 14 references written that way, so that is a real gap; telling
`rvalue.rs` in prose from any other mention of it is guesswork, and a guard
that guesses is worse than one that is narrow.

Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
nihalpasham pushed a commit to vyncint/cuda-oxide that referenced this pull request Sep 15, 2026
…t track

NVIDIA#1190 split large files into module directories and left three prose
references pointing at paths it had removed; NVIDIA#1197 repointed those and 14 more.
Nothing catches that: a path in prose is compiled by nothing,
`check-host-api-paths.sh` checks Rust API paths rather than files, and the book
gate only builds.

"In the repository" means tracked by git, not present on disk. Those are
different questions and only the first is reproducible -- a path that exists as
a local build artifact, or an untracked file someone happens to have, resolves
for them and nobody else, so a filesystem test makes the guard's answer depend
on whose checkout it ran in.

Generated outputs are declared, never inferred. An earlier revision skipped any
path whose parent directory held no tracked file, reasoning that such a
directory must be an output location. That is backwards: deleting or
misspelling a directory produces exactly the same signal as generating into
one, so the broken reference this guard exists to catch was the case it let
through. `GENERATED_OUTPUT_PATHS` is now the whole accommodation -- one entry,
`crates/fuzzer/artifacts/summary.jsonl`, which `run_seed.py` writes -- and it
is verified rather than trusted: an entry that becomes tracked, or that nothing
refers to any more, is an error instead of a silent exemption.

Roots are verified too. `docs/` was in the original list and holds no tracked
file in this repository, so the pattern anchored there matched nothing and
would have gone on matching nothing after a rename. The check that caught it
runs before every sweep.

Paths containing a traversal segment are reported rather than resolved, since
they cannot name a tracked file relative to the repository root.

The self-test runs the real scanner over four cases -- a missing file under a
tracked directory, a missing file under a directory the repo does not have, a
file that exists only in this checkout, and a tracked file -- and requires the
first three to be reported and the fourth not. It takes the tracked example
from the index rather than naming this script, which had made the self-test
depend on the guard already being committed.

Crate-relative and bare-basename spellings are still not checked. NVIDIA#1197's
follow-up fixed 14 references written that way, so that is a real gap; telling
`rvalue.rs` in prose from any other mention of it is guesswork, and a guard
that guesses is worse than one that is narrow.

Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
vyncint added a commit to vyncint/cuda-oxide that referenced this pull request Sep 15, 2026
…t track

NVIDIA#1190 split large files into module directories and left three prose
references pointing at paths it had removed; NVIDIA#1197 repointed those and 14 more.
Nothing catches that: a path in prose is compiled by nothing,
`check-host-api-paths.sh` checks Rust API paths rather than files, and the book
gate only builds.

"In the repository" means tracked by git, not present on disk. Those are
different questions and only the first is reproducible -- a path that exists as
a local build artifact, or an untracked file someone happens to have, resolves
for them and nobody else, so a filesystem test makes the guard's answer depend
on whose checkout it ran in.

Generated outputs are declared, never inferred. An earlier revision skipped any
path whose parent directory held no tracked file, reasoning that such a
directory must be an output location. That is backwards: deleting or
misspelling a directory produces exactly the same signal as generating into
one, so the broken reference this guard exists to catch was the case it let
through. `GENERATED_OUTPUT_PATHS` is now the whole accommodation -- one entry,
`crates/fuzzer/artifacts/summary.jsonl`, which `run_seed.py` writes -- and it
is verified rather than trusted: an entry that becomes tracked, or that nothing
refers to any more, is an error instead of a silent exemption.

Roots are verified too. `docs/` was in the original list and holds no tracked
file in this repository, so the pattern anchored there matched nothing and
would have gone on matching nothing after a rename. The check that caught it
runs before every sweep.

A URL is not a repository path. The pattern would otherwise match the tail of
any link ending in `.../crates/<something>.rs`, so a permalink into another
repository, or into an old commit of this one, would be read as a claim about
this tree. URLs are removed before matching, with one exception spelled as an
exact literal rather than a pattern: `https://github.com/NVlabs/cuda-oxide/`
`blob/main/` and its `tree/main/` form are this repository at its default
branch, so what follows one of them is checked like any other path. Five links
in the tree are written that way today.

A traversal segment needs no rule of its own, and the one an earlier revision
had is gone: `crates/../x.rs` is not a string git tracks, so the tracked test
already reports it. Keeping the special case only changed the wording of a
message, and no mutation of it could be observed -- which is the definition of
a branch that is not carrying its weight.

The self-test runs the real scanner over eight cases -- a file whose directory
the repo tracks but which it does not, a file whose directory it does not track
either, a file that exists only in this checkout, a tracked file, a link into
another repository whose tail looks like a path here, both forms of a link into
this repository at its default branch, and a traversal segment -- and fixes
which must be reported and which must not. It takes the tracked example from
the index rather than naming this script, which had made the self-test depend
on the guard already being committed, and it writes the canonical URL out
literally rather than building it from the constant, so that repointing the
constant fails the test instead of moving it.

Crate-relative and bare-basename spellings are still not checked. NVIDIA#1197's
follow-up fixed 14 references written that way, so that is a real gap; telling
`rvalue.rs` in prose from any other mention of it is guesswork, and a guard
that guesses is worse than one that is narrow.

Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
nihalpasham pushed a commit to vyncint/cuda-oxide that referenced this pull request Sep 18, 2026
…t track

NVIDIA#1190 split large files into module directories and left three prose
references pointing at paths it had removed; NVIDIA#1197 repointed those and 14 more.
Nothing catches that: a path in prose is compiled by nothing,
`check-host-api-paths.sh` checks Rust API paths rather than files, and the book
gate only builds.

"In the repository" means tracked by git, not present on disk. Those are
different questions and only the first is reproducible -- a path that exists as
a local build artifact, or an untracked file someone happens to have, resolves
for them and nobody else, so a filesystem test makes the guard's answer depend
on whose checkout it ran in.

Generated outputs are declared, never inferred. An earlier revision skipped any
path whose parent directory held no tracked file, reasoning that such a
directory must be an output location. That is backwards: deleting or
misspelling a directory produces exactly the same signal as generating into
one, so the broken reference this guard exists to catch was the case it let
through. `GENERATED_OUTPUT_PATHS` is now the whole accommodation -- one entry,
`crates/fuzzer/artifacts/summary.jsonl`, which `run_seed.py` writes -- and it
is verified rather than trusted: an entry that becomes tracked, or that nothing
refers to any more, is an error instead of a silent exemption.

Roots are verified too. `docs/` was in the original list and holds no tracked
file in this repository, so the pattern anchored there matched nothing and
would have gone on matching nothing after a rename. The check that caught it
runs before every sweep.

Paths containing a traversal segment are reported rather than resolved, since
they cannot name a tracked file relative to the repository root.

The self-test runs the real scanner over four cases -- a missing file under a
tracked directory, a missing file under a directory the repo does not have, a
file that exists only in this checkout, and a tracked file -- and requires the
first three to be reported and the fourth not. It takes the tracked example
from the index rather than naming this script, which had made the self-test
depend on the guard already being committed.

Crate-relative and bare-basename spellings are still not checked. NVIDIA#1197's
follow-up fixed 14 references written that way, so that is a real gap; telling
`rvalue.rs` in prose from any other mention of it is guesswork, and a guard
that guesses is worse than one that is narrow.

Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
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) intrinsics Device intrinsics and libdevice math mappings IR-lowering Lowering between dialects (dialect-mir to LLVM dialect) llvm-export Textual LLVM/NVVM IR exporter (llvm-export crate) refactor Internal restructuring with no behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant