[AMDGPU] Widen ndarray linear index to int64#776
Conversation
…d removes some warning logic around int32. It does not make quadrants globally indexed by int64. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 364b86d25b
ℹ️ 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".
| else: | ||
| element_dim = needed_arg_dtype.ndim | ||
| array_shape = v.shape[element_dim:] if is_soa else v.shape[:-element_dim] | ||
| if any(dim > np.iinfo(np.int32).max for dim in array_shape): |
There was a problem hiding this comment.
Keep product warning for SPIR-V backends
When this runs on Vulkan/Metal, ndarray addressing is still flattened in SPIR-V as an i32 value (quadrants/codegen/spirv/spirv_codegen.cpp:864-894), so a shape like (65536, 65536) has no single dimension above int32 but still wraps the linear offset and reads/writes the wrong element. This branch handles external NumPy/Torch arrays for every arch, so replacing the old product check with only a per-dimension check removes the only warning for unsupported SPIR-V launches; please keep the product warning for non-LLVM backends or widen the SPIR-V path too.
Useful? React with 👍 / 👎.
hughperkins
left a comment
There was a problem hiding this comment.
Using a different indexing bitwidth on different platforms conflicts with the ideal that Quadrants code should run unchanged across all platforms.
We could consider globally migrate all indices to int64, but:
- currently there is no obvious end-user need, and
- likely will have a performance impact
Adds a deterministic simulation of the ExternalPtrStmt linear-index arithmetic (i * D1 + j) showing i32 accumulation wraps negative while i64 stays correct, plus a memory-gated end-to-end test that reads an ndarray with more than 2**31 elements at the overflow-triggering index. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@hughperkins |
|
The criteria is that correctness should be ~identical across all GPU platforms. Example of what happens if this is not the case:
To justify changing the i64 for AMDGPU, you would need a platform-agnostic test that, on main, without your changes:
|
Summary
Backport of ROCm/quadrants@e0ec4416a.
Widens index values to int64 in the LLVM codegen path (ndarray
ExternalPtrStmtlinear indexing), makes
Ndarray::nelement_asize_taccumulation, and removesthe obsolete int32-boundary warning logic. This does not make Quadrants globally
int64-indexed; it only prevents int32 overflow in the generated linear-index
arithmetic for large ndarrays.
Cross-backend change (CPU + AMDGPU); conflicts on cherry-pick were purely
clang-format cosmetic and resolved to match the original commit.
Tests
known-flaky amdgpu block-scan tests (
test_block_inclusive,test_exclusive_scan_composition) also present in the base branch — no new regressions.test_ndarray_external_ptr_uses_i64_linear_indexpasses, verifyingi64 linear indexing in the generated LLVM IR.
To make the motivation concrete, I added two tests to
tests/python/test_ndarray_indexing_i64.pythat show why the linear index must be accumulated ini64.visit(ExternalPtrStmt)flattens an N-D ndarray access into a single linear element index — for a 2-D array of shape(D0, D1)it emitslinear = i * D1 + j. Before this PR that accumulation was done ini32and only sign-extended toi64for the final GEP, so any array with more than2**31elements overflowed before the extend and produced a wrong (often negative) address. The smallest 2-D shape that triggers it is(2, 2**30 + 1), indexed at[1, 2**30], where the true linear index is2**31 + 1— just pastINT32_MAX.test_i32_linear_index_overflows_but_i64_is_correct(always runs, CPU-only) — reproduces the exacti * D1 + jarithmetic with two kernels, one accumulating ini32(pre-fix behavior) and one ini64(the fix), and asserts the i32 version wraps to a negative value while the i64 version stays correct:test_ndarray_read_past_int32_index_boundary(end-to-end regression guard) — allocates a real>2**31-elementint8ndarray, writes a sentinel at[1, 2**30], and reads it back through the actualExternalPtrcodegen path. On the pre-fix code the wrapped negative offset returns garbage; with this PR it correctly returns the sentinel. It's gated with@pytest.mark.skipifon available memory (needs ~2 GB backing + headroom) so it self-skips on small runners. This test adds apsutilimport, consistent with the existingtests/python/test_memory.py.How to run
Or directly with pytest:
Verified against this branch's build: the simulation test passes and the end-to-end read returns the correct sentinel; on the pre-fix codegen the i32 accumulation wraps negative, which is exactly the failure this PR resolves.