[Perf] Tiles 1: _load, _store, _eye_#466
Conversation
Add the foundation for register-resident 16x16 tile operations: - _make_tile16x16(dtype) factory creating @qd.dataclass with r0–r15 - _load/_store/_load3d/_store3d with row and column bounds checking - eye_() for in-place identity initialization - zeros/eye factories - Fix InternalFuncStmt type_check to preserve polymorphic return types
- Import _TILE from tile16 instead of hardcoding - Replace 3 separate load/store tests with parametrized test over 92x92 array - Parametrize 3D load/store test over batches, offsets, and window sizes
| np.testing.assert_allclose(dst.to_numpy(), np.eye(_TILE, dtype=np.float32)) | ||
|
|
||
|
|
||
| _GRID = 92 |
There was a problem hiding this comment.
move this inside each test metdho
|
|
||
| from quadrants.lang.simt.tile16 import _make_tile16x16 | ||
| Tile = _make_tile16x16(qd.f32) | ||
| TILE = Tile.SIZE |
There was a problem hiding this comment.
should this SIZE be private? Or is it the same one we use later? If not it should be priate.
| The thread's lane index (tid) is obtained internally via subgroup.invocation_id(), | ||
| so callers never need to pass it. Load/store methods take a row offset (row0); | ||
| each thread accesses row = row0 + tid. | ||
|
|
There was a problem hiding this comment.
STate that everythign is private, because hti is an internal API, wrapped by th elater Tile proxy classes.
| t._store(arr, row0, col0, col_stop, row_stop) # store to 2D array | ||
| t._load3d(arr, i0, row0, col0, col_stop, row_stop) # load from 3D array | ||
| t._store3d(arr, i0, row0, col0, col_stop, row_stop) # store to 3D array | ||
| t.eye_() # set to identity matrix (in-place) |
There was a problem hiding this comment.
shouldnt eye_ be private? ie _eye_?
- Docstring: state everything is private, wrapped by Tile16x16Proxy - Move GRID/NBATCH constants inside each test method
| void visit(InternalFuncStmt *stmt) override { | ||
| // TODO: support return type specification | ||
| stmt->ret_type = PrimitiveType::i32; | ||
| // ret_type is already set correctly during flatten via |
There was a problem hiding this comment.
why this change?
There was a problem hiding this comment.
opus:
The old code hardcoded every InternalFuncStmt return type to i32, overriding whatever was set during IR flattening. This breaks subgroupShuffle — the shuffle operation returns
the same type as its input (e.g. f32 for float tiles), but the type checker was forcing it to i32.
Without this fix, every warp shuffle in the tile operations would be type-checked as returning an integer, causing miscompilation. The ret_type is already set correctly by
InternalCallOperation::flatten and the operation's own type_check, so the override just needs to be removed.
There was a problem hiding this comment.
in this pr are we tesing both f32 and f64?
There was a problem hiding this comment.
opus:
No — PR 1 only tests f32. The f64 parametrization is in the full test suite introduced in PR 4 (which has @pytest.mark.parametrize("qd_dtype", [qd.f32, qd.f64])).
Do you want me to add f64 coverage to PR 1's tests?
There was a problem hiding this comment.
oes this pr support both f32 and f64?
There was a problem hiding this comment.
Opus:
Yes — the code supports both. _make_tile16x16(dtype) accepts any scalar dtype (qd.f32 or qd.f64), and all register fields are declared with the passed dtype. The tests just
only exercise f32. Want me to parametrize the PR 1 tests over both?
Skip f64 on Metal (unsupported) and Vulkan (unreliable).
The old names (row0, col0, n_cols, row_stop) were misleading — n_cols was actually an absolute column boundary, not a count. Fix the tests to pass absolute boundaries (col_start + ncols) instead of counts.
| from quadrants.lang.simt import block, grid, subgroup, warp | ||
|
|
||
| __all__ = ["warp", "subgroup", "block", "grid"] | ||
| __all__ = ["warp", "subgroup", "block", "grid", "tile16"] |
There was a problem hiding this comment.
should this be private?
There was a problem hiding this comment.
Opus:
On PR 1, qd.simt.tile16 is exposed in init.py via lazy loading, but the only consumer is the test file — and the test imports directly from quadrants.lang.simt.tile16, not
through qd.simt.tile16.
So no, it doesn't need to be public in PR 1. We could remove it from all and the getattr lazy loader, deferring that to PR 4 when the public Tile16x16 proxy is
introduced. Want me to make that change?
There was a problem hiding this comment.
why does Tile16x16 proxy need tile16 to be public?
There was a problem hiding this comment.
Opus:
It doesn't. Nothing uses qd.simt.tile16 as a public module — every consumer imports directly from the full path quadrants.lang.simt.tile16. The getattr lazy loader for
"tile16" in init.py is dead code.
So we can remove tile16 from all and the getattr block entirely, on all branches. Want me to do that?
There was a problem hiding this comment.
yes. so then it would not be publicly visible? or do we need to rename to _tile16 too?
There was a problem hiding this comment.
Opus:
Removing it from all and getattr means qd.simt.tile16 won't resolve — users can't discover or access it through the public API. That's the main thing.
However, from quadrants.lang.simt.tile16 import ... (the full path) would still work because the file tile16.py exists on disk. Python doesn't enforce privacy for module
files. Renaming to _tile16.py would make the "don't use this" convention stronger, but it's just a convention — both are still importable.
Given that everything inside the module is already prefixed with _ (_make_tile16x16, _TILE, _Tile16x16, etc.), I'd say removing from all/getattr is sufficient.
Renaming the file would require updating imports across ~5 files on every branch — a lot of churn for a convention signal.
Want to just remove it from the public surface, or also rename the file?
There was a problem hiding this comment.
rename the file
The module is internal — all its symbols are already underscore-prefixed. Remove from __all__ and __getattr__ in simt/__init__.py so it's not accessible via qd.simt.tile16.
| @@ -0,0 +1,317 @@ | |||
| # type: ignore | |||
There was a problem hiding this comment.
why is this on type: ignore?
There was a problem hiding this comment.
Opus:
Every file in quadrants/lang/simt/ has it. It's a convention for this package — these modules define GPU kernel functions using @qd.func/@qd.dataclass with type annotations
like dtype (a runtime variable) as field types, which don't make sense to mypy/pyright. The # type: ignore suppresses type checker errors across the whole file.
It was there in the original branch; not something we added.
There was a problem hiding this comment.
no, it's not a convention. remove it please, and make sure pyright passes
Replace with targeted pyright suppressions: reportInvalidTypeForm=false at file level for DSL type annotations, and individual reportAttributeAccessIssue ignores for StructType monkey-patching.
| self.r15 = arr[row, col_start + 15] | ||
|
|
||
| @qd.func | ||
| def _load3d(self, arr: qd.template(), i0, row_start, col_start, col_end, row_end): |
There was a problem hiding this comment.
why i0? why not just i?
There was a problem hiding this comment.
Opus:
No good reason — it was named to match the old row0/col0 convention. Now that those are row_start/col_start, i0 is inconsistent. Should I rename it to batch?
| Each thread stores to arr[row_start + tid, col_start:col_end]. | ||
| Threads where row_start + tid >= row_end skip the store. | ||
| """ | ||
| row = row_start + qd.i32(qd.simt.subgroup.invocation_id()) |
There was a problem hiding this comment.
why do we need qd.i32?
| } | ||
|
|
||
| void visit(InternalFuncStmt *stmt) override { | ||
| // TODO: support return type specification |
There was a problem hiding this comment.
why is it ok to remove this comment?
|
Some queitons to opus: question: is there any code that is duplicate by or in this PR that we could refactorize better somehow?Looking at _tile16.py on hp/tiles-1, the major duplication is:
The question is what can actually be refactored given the Quadrants DSL constraints: Want me to try the code generation approach? Or check if 3D-to-2D slicing works first? Is there any code or code change in this PR that might be embarrassing in some way if published in a public github repo?A few things that would catch a reviewer's eye:
No secrets, offensive content, or anything truly bad. Want me to clean these up? yes please⬢ To-do All done Done. All cleaned up and propagated: Are there any layout inconsistencies in this code relative to itself and to other existing repo code?Here's what I found:
Want me to fix these — remove the banner, replace Unicode with ASCII, add the blank line, and factor out the f64 skip helper? yes⬢ To-do All done Done. All branches pushed with: Are there any coding inconsistencies in this code relative to itself and to other existing repo code?Here's what I found. The main coding inconsistency is: @test_utils.test(arch=qd.gpu) But 94.5% of tests in the repo (189 out of 200 instances across 61+ files) use the opposite order -- @pytest.mark.parametrize first, then @test_utils.test: @pytest.mark.parametrize("qd_dtype", _QD_DTYPES) This affects every parametrized test in test_tile16.py on hp/tiles-1 (and carries through to all downstream branches). yes⬢ To-do All done Done. All branches updated and pushed: All branches pass pre-commit. Is there anything in this PR that could be implemented in less code?The obvious candidate is the massive repetition: _load, _load3d, _store, _store3d, and eye each repeat the same pattern 16 times for r0 through r15. Together that's ~230
|
- Remove redundant col_start + 0 (just col_start) - Remove unnecessary qd.i32() casts (invocation_id already returns i32) - Rename dst_row_dx/dst_col_dx to row_offset/col_offset - Rename col_stop/row_stop to col_end/row_end to match API naming
- Remove section separator banner (not used elsewhere in simt/) - Replace Unicode dashes with ASCII - Add blank line after pyright directive - Factor out skip_if_f64_unsupported into test_utils
parametrize decorators go before @test_utils.test, matching 94% of existing tests in the codebase.
|
(continuing in new comment, since previous comment getting unwiedly) Is there anything in this PR that could be implemented in a way that will run faster at runtime?The runtime-critical code is in _tile16.py. Let me look at the _load/_store methods specifically. /home/hugh/ais/seagull/quadrants/python/quadrants/lang/simt/_tile16.py lines 62-74 Every load/store does 16 individual if col_start + i < col_end checks. For the common case (full 16-column tile load), all 16 checks pass and they're pure overhead. if col_start + 16 <= col_end: That said, in practice this is likely a marginal win: col_end is typically uniform across the subgroup, so the GPU handles these as predicated instructions rather than true Is there anything in this PR which might exhibit incorrect behavior in some way?yes, and also unit tests for this⬢ To-do All done Done. Here's a summary of the changes:
All branches rebased, pre-commit passes, force-pushed. |
Column bounds were already clamped to arr.shape but row bounds were not, which could cause out-of-bounds access when row_end exceeds the array height. Add symmetric row clamping and tests for the new behavior.
Change from (row_start, col_start, col_end, row_end) to (row_start, row_end, col_start, col_end) so row and column parameters are grouped together, matching numpy slice convention.
check the test coverage of the tests. Is there any code that is being added or modified in this PR, that is not being exercised by the tests? Is there any functionality that is implied by consistency or otherwise that is not being tested by these tests?Here's the coverage analysis:
Functionality implied by consistency but not tested: Most impactful gaps to close would be items 1 and 5: Want me to add these? Request to Opus: " │ → - remove All done. Here's what was completed:
|
…R 3); revert type_check.cpp (moving to PR 1b) - Add tests: Tile.SIZE, Tile.zeros(), column clamping, partial-col store, _make_tile16x16 caching - Remove _quadrants_internal flag (only consumed by ast_transformer in PR 3) - Revert type_check.cpp to main (moving to separate PR 1b)
check the factorization of the tests. are there some tests that could be merged, e.g. making use of parametrize?Here's my analysis of merge opportunities:
reminder: you can use
|
- zeros + zeros_factory -> single test with use_zeros_alias param - eye + eye_inplace -> single test with inplace param (qd.static branch) - 2D row clamp load + store -> single test with clamp_side param - 3D row clamp load + store -> single test with clamp_side param
Variables defined inside qd.static branches don't escape to outer scope.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2988a5b2e9
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| arr_row_end = arr.shape[0] | ||
| if arr_row_end < row_end: | ||
| row_end = arr_row_end | ||
| row = row_start + qd.simt.subgroup.invocation_id() |
There was a problem hiding this comment.
Derive tile row from block-local thread id
Using subgroup.invocation_id() to choose the tile row assumes a subgroup width of at least 16, but this is not guaranteed (the repo’s SIMT tests only assume a minimum subgroup size of 4). On GPUs where a 16-thread block is split into smaller subgroups (e.g., 2×8 lanes), lane IDs repeat per subgroup, so _load/_store/_load3d/_store3d/_eye_ operate on rows 0–7 twice and never touch rows 8–15, corrupting tile I/O and identity initialization.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
This is a great observation by Claude.
This is being addressed in #455
| def skip_if_f64_unsupported(dtype): | ||
| """Skip the current test if the active backend does not reliably support f64.""" | ||
| if dtype != qd.f64: | ||
| return | ||
| arch = qd.lang.impl.current_cfg().arch | ||
| if arch == qd.metal: | ||
| pytest.skip("Metal does not support f64") | ||
| if arch == qd.vulkan: |
There was a problem hiding this comment.
🟡 The new shared skip_if_f64_unsupported() helper in test_utils.py skips Vulkan f64 on all platforms, but the pre-existing _skip_if_f64_unsupported() in test_simt.py only skips on macOS (where MoltenVK is the Vulkan implementation). This causes tile16 f64 tests to be silently skipped on Linux Vulkan even though Vulkan natively supports f64 there — a test coverage regression. Fix by adding and platform.system() == 'Darwin' to the Vulkan branch, matching test_simt.py's established convention.
Extended reasoning...
What the bug is
tests/test_utils.py lines 334-335 (the new shared helper) unconditionally skip all Vulkan f64 tests:
if arch == qd.vulkan:
pytest.skip("Vulkan does not reliably support f64")But the pre-existing _skip_if_f64_unsupported() in test_simt.py lines 19-20 only skips on Darwin:
if arch == qd.vulkan and platform.system() == 'Darwin':
pytest.skip("MoltenVK does not support f64")The rationale in test_simt.py is precise: the problem is MoltenVK (the macOS Vulkan implementation), which does not support f64. Native Vulkan on Linux has no such limitation.
The specific code path
Every tile16 f64 test in test_tile16.py calls test_utils.skip_if_f64_unsupported(qd_dtype) at the top. When the test runner is configured to use Vulkan on Linux (which conftest.py does), this call reaches the new helper, hits the unconditional arch == qd.vulkan branch, and issues pytest.skip — regardless of whether the platform is Linux, where f64 Vulkan actually works.
Why existing code doesn't prevent it
The PR author was aware of the discrepancy (PR timeline: "ours skips Vulkan unconditionally, test_simt.py only skips Vulkan on macOS") but chose to be more conservative. No justification is given for why tile16 operations would fail on Linux Vulkan when test_simt.py shuffle f64 tests (which exercise the same underlying subgroup intrinsics) continue to pass there.
Impact
All tile16 f64 tests (test_tile16_zeros, test_tile16_eye, test_tile16_load_store, test_tile16_clamp_to_array_rows, etc.) are silently skipped on Linux Vulkan. Meanwhile, test_simt.py f64 tests run normally on the same platform with the same backend. This is a silent test coverage regression — CI stays green while f64 tile16 coverage is absent on Linux Vulkan.
Step-by-step proof
- Linux CI runner with Vulkan enabled runs pytest.
test_tile16_zeros[qd_dtype=qd.f64-arch=vulkan]is invoked.test_utils.skip_if_f64_unsupported(qd.f64)is called.dtype \!= qd.f64is False, so execution continues.arch = qd.lang.impl.current_cfg().archreturnsqd.vulkan.arch == qd.vulkanis True →pytest.skip("Vulkan does not reliably support f64")fires.- Test is marked as skipped with no failure, no warning.
- The same runner executes
test_simt.pyf64 shuffle tests with Vulkan — those use_skip_if_f64_unsupportedwhich checksplatform.system() == 'Darwin'→ False on Linux → tests run and pass.
How to fix
Change the Vulkan branch in test_utils.skip_if_f64_unsupported to match the convention established in test_simt.py:
if arch == qd.vulkan and platform.system() == 'Darwin':
pytest.skip("MoltenVK does not support f64")This restores f64 tile16 coverage on Linux Vulkan while still correctly skipping on macOS where MoltenVK is the implementation.
There was a problem hiding this comment.
This is intentional. Some tests were failing wiht f64, even on linux vulkan.
erizmr
left a comment
There was a problem hiding this comment.
Approving on the basis that I have reviewed the design and the public facing API, and they look reasonable to me.
* [Misc] Warn user to disable caching when print_ir/QD_DUMP_IR enabled (Genesis-Embodied-AI#425) Co-authored-by: v01dxyz <v01dxyz@v01d.xyz> * [Build] Pin torch version to CUDA 12.8 for CUDA tests (Genesis-Embodied-AI#428) * [Misc] Fixing up taichi-dev urls (Genesis-Embodied-AI#429) * [Perf] Rename cuda_graph to gpu_graph across the codebase (Genesis-Embodied-AI#430) * Misc: fix typo integeral -> integral (Genesis-Embodied-AI#434) Co-authored-by: v01dxyz <v01dxyz@v01d.xyz> * [Perf] CUDA graph 4: call from multiple locations (Genesis-Embodied-AI#420) * [Bug] Fix fastcache not restoring graph_do_while_arg (Genesis-Embodied-AI#435) * [Perf] Cache last-call result in perf_dispatch for single-compatible case (Genesis-Embodied-AI#438) * Fix gpu_graph fallback on old Nvidia GPU. (Genesis-Embodied-AI#443) * Fix shared memory offset not reset between CUDA kernels. (Genesis-Embodied-AI#442) * [Misc] Allow disabling GPU graph via QD_GPU_GRAPH=0 env var (Genesis-Embodied-AI#439) * [Misc] Add named top-level loops (Genesis-Embodied-AI#440) * [Misc] Rename gpu_graph to graph (Genesis-Embodied-AI#446) * [Misc] Add cross-platform shuffle (Genesis-Embodied-AI#447) * [Bug] Fix graph_do_while on Windows: search for cudadevrt.lib (Genesis-Embodied-AI#456) * [Bug] Also search default CUDA toolkit install location on Windows (Genesis-Embodied-AI#461) * [SPIRV] Feature Parity Atomics & Shared Array (Genesis-Embodied-AI#432) * [Misc] Change clang format to 120 characters (Genesis-Embodied-AI#463) * [Misc] CUDA graph 5 Add fatbin (Genesis-Embodied-AI#464) * [Bug] Reuse VkInstance across init/reset cycles (Genesis-Embodied-AI#465) * [Perf] Tiles 1: _load, _store, _eye_ (Genesis-Embodied-AI#466) * [Misc] Remove dead InternalFuncStmt type_check override (Genesis-Embodied-AI#471) * [Perf] Tiles 2: add cholesky and ger (Genesis-Embodied-AI#472) * [Perf] Tiles 2b: add triangular solve (Genesis-Embodied-AI#474) * [Misc] Refactor: use _get_col/_set_col in tiles load/store/init (Genesis-Embodied-AI#475) * [Build] Fix flaky test_clock_accuracy (Genesis-Embodied-AI#436) * Fix AARCH64 emitting invalid asm in CUDA kernels. (Genesis-Embodied-AI#473) Co-authored-by: Hugh Perkins <hughperkins@gmail.com> * [AMDGPU] Enable HIP memory pool and surface pool-exhaustion errors. (Genesis-Embodied-AI#485) * [AMDGPU] Scope hsaco tmp dir per-user to avoid collisions. (Genesis-Embodied-AI#484) * [Perf] Tiles 3: Add slice syntax, qd.outer() and initial doc (Genesis-Embodied-AI#477) * [AMDGPU] Fix gradient computation. (Genesis-Embodied-AI#486) * Enable all backends that are supported in unit tests. (Genesis-Embodied-AI#488) * Fix SPIRV ID overflow for large kernels due to autodiff. (Genesis-Embodied-AI#489) * [Misc] Fix purity checker to allow accessing constants from quadrants modules (Genesis-Embodied-AI#487) * [Misc] Increase tolerance for clock monotonic test (Genesis-Embodied-AI#492) * [CI] Serialize api doc workflow (Genesis-Embodied-AI#494) * [CI] Increase tolerance for clock test (Genesis-Embodied-AI#506) * [CI] Increase clock test tolerance to 20% (Genesis-Embodied-AI#509) * [Perf] Add tensor_type parametrization to tile16 tests (Genesis-Embodied-AI#504) * [Perf] Tiles 4b: Migrate tiles16 tests to enable fastcache (Genesis-Embodied-AI#505) * [Perf] Tiles 4c: add Tiles16x16 proxy (Genesis-Embodied-AI#507) * [Perf] Tiles 4d: Consolidate slice error tests using parametrize (Genesis-Embodied-AI#508) * [Perf] Tiles 4: add SharedArray slice support (Genesis-Embodied-AI#482) * [Perf] Tiles 5: add Cholesky benchmark demo (Genesis-Embodied-AI#483) * [Doc] Add user guide page for subgroup shuffle (Genesis-Embodied-AI#512) * [Perf] Implement cross-platform shuffle_down (Genesis-Embodied-AI#510) * [Perf] Add portable subgroup reduce_add and reduce_all_add (Genesis-Embodied-AI#511) * [Perf] Add first warmup config to perf dispatch (Genesis-Embodied-AI#422) * [AutoDiff] Autodiff 1: Add baseline adstack regression test for unary_collections (Genesis-Embodied-AI#500) * [AutoDiff] Autodiff 2: Implement derivative for tan (Genesis-Embodied-AI#501) * [AutoDiff] Autodiff 3: Recompute tanh/exp on the operand in the reverse pass (Genesis-Embodied-AI#502) * [AutoDiff] Autodiff 4: Mark rsqrt as non-linear for adstack promotion (Genesis-Embodied-AI#503) * [AutoDiff] Autodiff 5: Fix adjoint-alloca placement for GlobalLoads outside the current range-for (Genesis-Embodied-AI#496) * [AutoDiff] Autodiff 6: Adstack regression tests (Genesis-Embodied-AI#491) * [AutoDiff] Autodiff 7: Fix header size in AdStackAllocaStmt to match u64 runtime layout (Genesis-Embodied-AI#534) * [AutoDiff] Autodiff 8: Surface LLVM adstack push/pop overflow as a Python exception (Genesis-Embodied-AI#535) * [AutoDiff] Autodiff 9: Guard against LLVM worker-thread stack overflow from large per-task adstack budget (Genesis-Embodied-AI#495) * [AutoDiff] Autodiff 10: Implement adstack for SPIR-V (Genesis-Embodied-AI#490) * [AutoDiff] Autodiff 11: Latent adstack-adjacent fixes (AMDGPU hipFree, flush() keeps ctx_buffers_, always-preallocate) (Genesis-Embodied-AI#536) * [Doc] Add AGENTS.md with instructions for AI agents (Genesis-Embodied-AI#541) * [Bug] Abort kernel execution on assertion failure instead of segfaulting (Genesis-Embodied-AI#419) * [Type] ndarray typing 1: Add eval_str=True to inspect.signature() calls (Genesis-Embodied-AI#411) * [CI] Suppress reportPrivateImportUsage in torch-using files (Genesis-Embodied-AI#552) * [Misc] QD_DUMP_IR dumps to files with the task_id added to the filename (Genesis-Embodied-AI#441) * [Type] ndarray typing 2: Fix NDArray single-arg subscript crash (Genesis-Embodied-AI#412) * [Test] Flush xdist channel before worker exit so test failure reports are visible (Genesis-Embodied-AI#555) * [CI] Reduce test retries on CI from 3 to 1. (Genesis-Embodied-AI#554) * [AutoDiff] Autodiff 12: Heap-backed adstack on LLVM backends (CPU/CUDA/AMDGPU) (Genesis-Embodied-AI#537) * [AutoDiff] Autodiff 13: Heap-backed adstack on SPIR-V backends (Metal, Vulkan) (Genesis-Embodied-AI#493) * [AutoDiff] Autodiff 14: Resolve bounded-inner-loop adstacks without default_ad_stack_size fallback (Genesis-Embodied-AI#539) * [SPIRV] Vulkan SPIR-V correctness: atomic-view aliasing, PSB stride, narrow storage caps, u1 cast, per-init layer recheck (Genesis-Embodied-AI#513) * [Build] Autodiff 15: Replace 2022 MoltenVK pin with LunarG Vulkan SDK fetch and sanitise MoltenVK cap advertisement (Genesis-Embodied-AI#551) * [Test] Suppress stock pytest-timeout to avoid conflict with pytest_hardtle (Genesis-Embodied-AI#557) * [Vulkan] Use SDK validation layer for debugPrintf instead of apt package (Genesis-Embodied-AI#562) * [Test] Fix flaky perf_dispatch tests by increasing work amounts (Genesis-Embodied-AI#559) * [Test] Add --maxfail CLI option to run_tests.py (default 20) (Genesis-Embodied-AI#558) * [CI] Vulkan debug printf fix to address flaky tests (Genesis-Embodied-AI#563) * [Docs] Add a new page to help for first time contributors (Genesis-Embodied-AI#426) Authored-by: v01dxyz <v01dxyz@v01d.xyz> * [AutoDiff] Autodiff 16: Resolve reverse-mode adstack depths per-launch via runtime-evaluated SizeExpr (Genesis-Embodied-AI#543) * Fix: raise error if device memory allocation fails (Genesis-Embodied-AI#451) (Genesis-Embodied-AI#453) Co-authored-by: v01dxyz <v01dxyz@v01d.xyz> Co-authored-by: Hugh Perkins <hughperkins@gmail.com> * [CI] Add CI job to check line wrapping of comments and docs (Genesis-Embodied-AI#564) * [Misc] Add coverage report to PRs, including kernels (Genesis-Embodied-AI#470) * [CI] CI wrap check feeds only diffs to agent (Genesis-Embodied-AI#567) * Skip 'flaky' test on MacOS CI. (Genesis-Embodied-AI#573) * [Test] Fix missing `import sys` in test_fail_device_memory_allocation (Genesis-Embodied-AI#574) * [CI] Fix Vulkan debugPrintf flake with session-scoped warmup (Genesis-Embodied-AI#571) * [AutoDiff] determine_ad_stack_size: replace whole-CFG Bellman-Ford with SCC + DAG DP (Genesis-Embodied-AI#575) * [Test] Fix macOS OOM skip reason to describe actual root cause (Genesis-Embodied-AI#576) * [Lang] whole_kernel_cse: 2.5x compile time speedup on large kernels (Genesis-Embodied-AI#577) * [CI] Add CI check for unnecessarily deleted comments (Genesis-Embodied-AI#570) * [CI] Migrate coverage report to github Check page (Genesis-Embodied-AI#566) * [Lang] Skip IR verifier between passes unless debug=true (Genesis-Embodied-AI#579) * [Lang] Inline AdStack ops on release LLVM codegen: dramatically reduces compile time for adstack-enabled reverse-mode kernels (Genesis-Embodied-AI#584) * [CUDA] Honor offline_cache=False end-to-end so QD_OFFLINE_CACHE=0 actually gives a cold compile (Genesis-Embodied-AI#580) * [Type] Tensor 24 (Genesis-Embodied-AI#561) Co-authored-by: hugh <hugh@slurm-login-0.slurm-login.tenant-slurm.svc.cluster.local> * [Lang] auto_diff host-walk reductions: dramatically faster front-end compile time on adstack-enabled reverse-mode kernels (Genesis-Embodied-AI#587) * [AutoDiff] Speed up reverse-mode kernel launches on GPU backends (Genesis-Embodied-AI#578) * [Vulkan] Move adstack-sizer scratch out of Function-scope memory to fix SPIR-V pipeline build failures (Genesis-Embodied-AI#588) * [AutoDiff] Improve diagnosis of unsupported reverse-mode AD patterns (Genesis-Embodied-AI#590) * [Bug] Fix: promote Ndarray to AnyArray in build_Name for flattened struct fields (Genesis-Embodied-AI#592) * [SPIR-V] Shrink reverse-grad kernel MSL by ~50% (Genesis-Embodied-AI#591) * [CI] Add CI check that PR changes have test coverage (Genesis-Embodied-AI#596) * [Perf] Enable zero-copy in to_torch() and to_numpy() (Genesis-Embodied-AI#450) * Add BufferView: safe sub-range ndarray access for kernels (Genesis-Embodied-AI#585) Co-authored-by: alanray-tech <alanray-tech@users.noreply.github.com> Co-authored-by: Hugh Perkins <hughperkins@gmail.com> * [Doc] Add user-facing fastcache documentation (Genesis-Embodied-AI#597) Co-authored-by: hugh <hugh@slurm-login-0.slurm-login.tenant-slurm.svc.cluster.local> * [Misc] Upgrade to enable v1 dlpack so to_numpy(copy=False) writable (Genesis-Embodied-AI#598) Co-authored-by: root <root@rtx-209-201.slurm-compute.tenant-slurm.svc.cluster.local> * [AutoDiff] Cut reverse-mode adstack memory usage 10x on all backends (Genesis-Embodied-AI#599) * [Misc] Add CI check for feature file factorization (Genesis-Embodied-AI#606) * [Perf] Skip _recursive_set_args for all-Field frozen dataclass structs (Genesis-Embodied-AI#607) Co-authored-by: Cursor <cursoragent@cursor.com> * [AutoDiff] SNode-arm bound-expr capture rejects fold-attack gate indices (Genesis-Embodied-AI#610) * [Misc] Suppress field fastcache warning for qd.Tensor (Genesis-Embodied-AI#615) Co-authored-by: Cursor <cursoragent@cursor.com> * [AutoDiff] Adstack heap: clip reducer count by per-task loop trip count (compile-time and SizeExpr-evaluated) (Genesis-Embodied-AI#611) * [Misc] Forward copy= through qd.Tensor, add copy=None option (Genesis-Embodied-AI#616) Co-authored-by: Cursor <cursoragent@cursor.com> * [Doc] Update README (Genesis-Embodied-AI#617) Co-authored-by: Cursor <cursoragent@cursor.com> * [CI] Fix coverage report showing def lines as uncovered (Genesis-Embodied-AI#623) Co-authored-by: Cursor <cursoragent@cursor.com> * [Perf] Generic launcher: persistent context, JIT-pointer reuse, Metal compute encoder, LLVM-GPU async memory ops (Part 1/2) (Genesis-Embodied-AI#619) * [CI] Encode Python-first testing policy in coverage-check prompt (Genesis-Embodied-AI#622) Co-authored-by: Cursor <cursoragent@cursor.com> * [CI] Add PR Line change report (Genesis-Embodied-AI#624) Co-authored-by: Cursor <cursoragent@cursor.com> * [CI] Disable quadrants pytest plugin during quadrants internal coverage runs (Genesis-Embodied-AI#629) Co-authored-by: Cursor <cursoragent@cursor.com> * [AutoDiff] Adstack load+store eliminations: EliminateRecomputableAdStackPushes pass + leaf extensions (Genesis-Embodied-AI#621) * [CI] Simplify coverage PR comment to a single linked line (Genesis-Embodied-AI#630) * [CUDA] Add AGX Thor, SM_110 (Genesis-Embodied-AI#631) Co-authored-by: Johnny Nunez and Hugh Perkins * [CI] Lines changed report: collapse PR comment to a single linked totals line (Genesis-Embodied-AI#632) * [FEATURE] Support external Metal command queue via qd.init (Genesis-Embodied-AI#618) Co-authored-by: Cursor <cursoragent@cursor.com> * [Perf] Cache adstack-sizer metadata per task across SPIR-V + LLVM-GPU; per-snode / DeviceAllocation invalidation (Part 2/2) (Genesis-Embodied-AI#620) * [AutoDiff] Disable EliminateRecomputableAdStackPushes pending mutated-SNode chain-leaf fix (Genesis-Embodied-AI#633) * [AutoDiff] Adstack chain-clone safety: mutated-SNode leaf reject + load_top consumer-aware guard (Genesis-Embodied-AI#634) * [Docs] Add user-guide page for qd.simt.block.* primitives (Genesis-Embodied-AI#638) * [Docs] Expand qd.simt.subgroup user-guide page to cover every op (Genesis-Embodied-AI#639) * [Perf] Streams 1-4 (Genesis-Embodied-AI#410) * [Docs] Add user-guide page for matrix decompositions and solvers (Genesis-Embodied-AI#643) * [Bug] Revert "[Perf] Streams 1-4 (Genesis-Embodied-AI#410)" (Genesis-Embodied-AI#650) * [Docs] Add user-guide page for atomics and bit operations (Genesis-Embodied-AI#640) * [Docs] Add user-guide page for qd.simt.grid.* primitives (Genesis-Embodied-AI#641) * [AutoDiff] Adstack max-reducer: parallel multi-axis MaxOverRange dispatch (Genesis-Embodied-AI#635) * [AMDGPU] Fix amdgpu parallel rand init (Genesis-Embodied-AI#658) * [Perf] Adstack: skip max-reducer recognizer on CPU + lift host-eval cap (Genesis-Embodied-AI#655) * [Perf] Re-land Streams 1-4 with bug fixes (Genesis-Embodied-AI#653) * [AMDGPU] Apply device_memory_GB=0.3 cap to AMDGPU tests (Genesis-Embodied-AI#659) * [Perf] Per-launch host sync: drop wait_idle on SPIR-V, pin stream and drop stream_synchronize on CUDA/AMDGPU (Genesis-Embodied-AI#654) * [AMDGPU] Unload hipModule_t in JITModuleAMDGPU destructor (Genesis-Embodied-AI#660) * [AMDGPU] Trim default mempool on qd.reset() (Genesis-Embodied-AI#669) * [AMDGPU] Hoist rand-state buffer to process lifetime (Genesis-Embodied-AI#668) * [Streams] Use events for streams serialization on AMDGPU and CUDA (Genesis-Embodied-AI#667) * [Perf] Adstack max-reducer: launch cache + zero-copy result map; content-stable registry_id (Genesis-Embodied-AI#671) * [SPIR-V] dispatch_max_reducers: register each task with the real kernel name (Genesis-Embodied-AI#675) * [AutoDiff] Debug-mode field/grad/dual: dtype, layout, and access-time invariants (Genesis-Embodied-AI#677) * [Docs] Add user-guide page for qd.algorithms.* device-wide algorithms (Genesis-Embodied-AI#642) Co-authored-by: alanray-tech <alan.ray@genesis-ai.company> * [Docs] Doc for existing atomics: switch support table to per-backend columns (Genesis-Embodied-AI#657) Co-authored-by: alanray-tech <alan.ray@genesis-ai.company> * [GPU] Cross gpu atomics (Genesis-Embodied-AI#666) Co-authored-by: alanray-tech <alan.ray@genesis-ai.company> * [GPU] Make block operations portable cross-gpu (Genesis-Embodied-AI#664) * [Perf] CPU LLVM adstack-cache: skip per-launch bump-writes + ndarray_shapes capture on forward-only handles (Genesis-Embodied-AI#685) * [GPU] Cross-GPU for grid ops (Genesis-Embodied-AI#670) * [Math] Make bitop operations portable cross-gpu (Genesis-Embodied-AI#662) * [AMDGPU] Always use wave64, on both RDNA and CDNA (Genesis-Embodied-AI#687) * [AMDGPU] Use syncscope("agent") for atomix xor to avoid CAS livelock (Genesis-Embodied-AI#672) * [GPU] New bit ops for QIPC (Genesis-Embodied-AI#679) * [GPU] Subgroup ops cross-gpu (Genesis-Embodied-AI#665) * [Graph] Rename CUDA Graph to Graph in docs (Genesis-Embodied-AI#691) * [SPIR-V] Fix FIFO-queue ordering when sharing command queue. (Genesis-Embodied-AI#694) * [Atomics] New QIPC ops for atomics (Genesis-Embodied-AI#690) * Pass dataclass sub-structs into qd.func (Genesis-Embodied-AI#698) * [AMDGPU] HIP graph runtime support for @qd.kernel(graph=True) (Genesis-Embodied-AI#692) * [CI] Add per-file timing report to Mac Metal test job (Genesis-Embodied-AI#695) Co-authored-by: Cursor <cursoragent@cursor.com> * [CI] Enable kernel disk cache during tests (Genesis-Embodied-AI#696) * [Math] New QIPC ops for single-threaded linalg (Genesis-Embodied-AI#683) * [BREAKING][GPU] New QIPC ops for subgroups (Genesis-Embodied-AI#676) * [GPU] New QIPC ops for block (Genesis-Embodied-AI#684) * [GPU] New device-level ops for QIPC (Genesis-Embodied-AI#693) * [algorithms] PrefixSumExecutor: drop unused GRID_SZ local (Genesis-Embodied-AI#701) * [block] sync(): fix unsupported-arch error message (Genesis-Embodied-AI#700) * [volatile_load] add qd.volatile_load primitive (closes Genesis-Embodied-AI#648) (Genesis-Embodied-AI#702) * [AutoDiff] Reject recycled identity_key in AdStackCache::register_adstack_sizing_info (Genesis-Embodied-AI#708) * [Vulkan] Declare GroupNonUniform SPIR-V caps and enable shaderSubgroupExtendedTypes (Genesis-Embodied-AI#707) * Fix duplicate HIP graph driver-function declarations after v1.0.0 merge The amd-integration fork had cherry-picked the HIP graph driver functions (graph_create / graph_destroy / graph_add_kernel_node / graph_instantiate / graph_exec_destroy / graph_launch), and upstream v1.0.0 added the same set. The per-file 3-way merge appended both copies into amdgpu_driver_functions.inc.h, producing redeclaration errors that broke the AMDGPU RHI/runtime compile. Drop the upstream duplicate block; the signatures are identical to the fork's existing declarations. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix AMDGPU launcher coherence and num_instructions visibility after v1.0.0 merge - kernel_launcher.cpp: the 3-way merge spliced upstream v1.0.0's launch_llvm_kernel rewrite (ephemeral arg/context buffers, explicit-stream path, AmdgpuDefaultStream PinGuard) onto the AMD fork's kernarg-by-value + persistent-scratch design, leaving references to undefined `ephemeral_context_ptr`. Restore the fork's coherent launch_llvm_kernel verbatim; it calls the (already merged) enhanced launch_offloaded_tasks, which keeps the max-reducer dispatch and stream-parallel groups adapted onto the AMD launch path. - llvm_context.h: both the fork and upstream added `num_instructions`; the merge kept upstream's private placement, but the AMDGPU codegen force-inline heuristic calls it statically from outside the class. Move it back to the public section. Co-authored-by: Cursor <cursoragent@cursor.com> * Restore async result D2H and hoist kernarg vectors in AMDGPU launcher The v1.0.0 merge resolution regressed two amd-integration baseline optimizations in launch_llvm_kernel / launch_offloaded_tasks: - The per-launch result-buffer copy was a blocking memcpy_device_to_host, forcing a host stall on every value-returning launch and serializing the GPU pipeline. Restore the async D2H (the caller synchronizes lazily when it needs the value); external-array transfers still stream_synchronize once before reading back. - launch_task constructed the kernarg std::vectors from initializer lists ({kernarg_payload} / {kernarg_size}) on every dispatch (heap alloc + free per launch). Hoist arg_ptrs/arg_sizes out of the per-task launch and reuse. Co-authored-by: Cursor <cursoragent@cursor.com> * amdgpu: default to LDS permlane64 emulation; drop host-x86 barrier asm on retarget Two AMDGPU JIT-compile crashes surfaced after the v1.0.0 merge pulled in the QIPC subgroup ops (Genesis-Embodied-AI#676), which made the rigid constraint solver's wave-cooperative reductions route through `amdgpu_cross_half_shuffle_i32`. Both manifested as a SIGSEGV inside `llvm::SIInstrInfo::getInstSizeInBytes` during `JITSessionAMDGPU::compile_module_to_hsaco` (i.e. at first kernel launch), and reproduce on gfx942 / MI300X. Baseline 0.4.6 never emitted these constructs, which is why it was unaffected. 1. Native `llvm.amdgcn.permlane64` lowering crashes the bundled LLVM 22.1.0 AMDGPU backend. Default `amdgpu_permlane64` to the existing LDS-roundtrip software emulation on every target (it produces identical results). Add `QD_AMDGPU_USE_NATIVE_PERMLANE64=1` to opt back into the native instruction once the backend bug is fixed; the old `QD_AMDGPU_FORCE_PERMLANE64_FALLBACK` is now the default and still honored. This is the actual crash fix. 2. The runtime module is compiled by the host x86_64 clang and only retargeted to amdgcn here, so `amdgpu_cross_half_shuffle_i32`'s `__asm__ volatile("" : "+v"(byte))` optimization barrier carries x86 flag clobbers (`~{dirflag},~{fpsr},~{flags}`) that are meaningless on AMDGPU. The IR verifies but the empty-body INLINEASM is invalid on the amdgcn target. Neutralize empty-body barrier asm during retarget (forward the tied value, then erase) so no stale host asm reaches codegen. On the wave64 targets we ship `ds_bpermute` already addresses the full wave, so the hint is a no-op. Co-authored-by: Cursor <cursoragent@cursor.com> * style: apply clang-format (v19.1.7) to AMDGPU fn_attrs and launcher sources CI pre-commit's clang-format hook reformatted these files (long declarations/lambda signatures collapsed onto single lines per the repo's clang-format config). Apply the same formatting so the hook passes. No functional changes. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(amdgpu): use CreateNeg for branchless i32 sgn instead of CreateSub(0, input) clang-tidy (modernize-use-nullptr, -warnings-as-errors) flagged `builder->CreateSub(0, input)` in the i32 sgn path: the literal `0` binds to the `llvm::Value*` LHS parameter as a null pointer, not an integer zero. Replace with `builder->CreateNeg(input)`, which emits `0 - input` with a proper zero constant -- identical intended semantics, and clang-tidy clean. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Robert Dazi <14996868+v01dXYZ@users.noreply.github.com> Co-authored-by: v01dxyz <v01dxyz@v01d.xyz> Co-authored-by: Hugh Perkins <hughperkins@gmail.com> Co-authored-by: Alexis DUBURCQ <alexis.duburcq@gmail.com> Co-authored-by: hugh <hugh@slurm-login-0.slurm-login.tenant-slurm.svc.cluster.local> Co-authored-by: alanray-tech <alan.ray@genesis-ai.company> Co-authored-by: alanray-tech <alanray-tech@users.noreply.github.com> Co-authored-by: root <root@rtx-209-201.slurm-compute.tenant-slurm.svc.cluster.local> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Johnny <johnnynuca14@gmail.com>
Add the foundation for register-resident 16x16 tile operations:
Issue: #
Brief Summary
Description from Opus:
for the purposes of pr description, describe what is in pr 1, and the good and bad points about pr 1
What's in PR 1
Introduces Tile16x16, a register-resident 16x16 matrix tile distributed across 16 subgroup threads. Each thread owns one row stored in 16 scalar registers (r0-r15). This is the
foundation for blocked linear algebra kernels (Cholesky, triangular solve) that follow in later PRs.
Files changed (614 lines, 4 files):
•
_tile16.py(324 lines) -- The tile dataclass with _load/_store (2D), _load3d/_store3d (3D), and eye methods. A _make_tile16x16(dtype) factory creates dtype-specializedtile types, cached to avoid re-creation. Both f32 and f64 are supported.
•
type_check.cpp(4 lines) -- Fixes InternalFuncStmt type checking to preserve polymorphic return types (e.g. subgroupShuffle returning f32 instead of being hardcoded toi32). Required for tile shuffles to work correctly.
•
test_tile16.py(276 lines) -- Tests for zeros, eye, eye-inplace, parametrized load/store (various offsets, window sizes, partial rows/cols), 3D load/store, and array-shapeclamping (both row and column).
•
test_utils.py(12 lines) -- Shared skip_if_f64_unsupported helper.────────────────────────────────────────
Good points
• Correct bounds clamping in both dimensions -- _load/_store clamp both row_end and col_end to the actual array shape, preventing out-of-bounds GPU memory access when the tile
window extends past the array.
• Thoroughly parametrized tests -- Load/store tests cover 8 combinations of offsets, partial rows, partial columns on a 92x92 grid, all crossed with f32/f64. Edge cases (array
narrower/shorter than 16) are explicitly tested.
• Clean internal API -- Everything is private (_tile16.py, _load, _store, eye). The public API (Tile16x16 proxy with slice syntax) is deferred to later PRs, keeping this PR
focused.
• No shared memory needed -- The tile design uses only scalar registers and subgroup shuffles, making it portable across CUDA, Vulkan, and Metal.
• The
type_check.cppfix is minimal and correct -- It removes a hardcoded i32 return type that would silently corrupt f32/f64 shuffle results. The fix relies on ret_typealready being set correctly during IR flatten, which is guaranteed by InternalCallOperation::flatten.
Bad points
• Extremely repetitive -- _load, _store, _load3d, _store3d, and eye each repeat the same pattern 16 times for r0 through r15. The four load/store methods alone are ~220
lines of near-identical code. This is an inherent consequence of the DSL not supporting computed field access, but it makes the file hard to review and maintain.
•
_load3d/_store3dare near-duplicates of_load/_store-- The only difference is an extra batch index and arr.shape[2] vs arr.shape[1]. If the DSL supported 2D viewsfrom 3D arrays, these could be eliminated entirely.
• Parameter order is non-standard -- _load(arr, row_start, col_start, col_end, row_end) interleaves row and column parameters instead of grouping them (row_start, row_end,
col_start, col_end). Easy to accidentally swap col_end and row_end when calling the internal API directly.
•
_tile16_cacheis never invalidated -- The module-level cache persists across qd.reset() / qd.init() cycles. This appears to work in practice because the cached StructTypeis just a type descriptor, but it's fragile and undocumented.
• No assertion if
row_end - row_start > 16-- Passing a range larger than 16 rows silently ignores rows beyond row_start + 15. This is correct behavior for a 16-row tile,but a caller mistake here would be hard to debug.
Fix the parameter order
All call sites now use the new order (arr, row_start, row_end, col_start, col_end).
what are your thoughts on 'No assertion if row_end - row_start > 16 -- Passing a range larger than 16 rows silently ignores rows beyond row_start + 15. This is correct behavior for a 16-row tile, but a caller mistake here would be hard to debug.'?
Thinking about it more, I don't think it's worth adding. A few reasons:
rows but not columns would be inconsistent, and adding both would be annoying.
total_rows). The tile naturally handles this by only loading the 16 rows it can hold. Asserting would force every caller to do that clamping manually.
I'd remove it from the "bad points" list. It's working as designed.
To Opus: "tell the reviewer a story about PR 1. Assume they know nothing about this code, but they do know high level quadrants concepts, as a user. Walk through: │
The whole story should be readable in order, without needing to skip ahead to find information that hasn't been presented yet.”
copilot:summary
Walkthrough
copilot:walkthrough