Skip to content

PTX: handle atomics on local memory - #963

Merged
maleadt merged 2 commits into
JuliaGPU:mainfrom
timesselens:pr/ptx-local-atomics
Oct 17, 2026
Merged

maleadt merged 2 commits into
JuliaGPU:mainfrom
timesselens:pr/ptx-local-atomics

Conversation

@timesselens

@timesselens timesselens commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

PTX only defines atomic instructions (atom, red, and relaxed, acquire or release loads and stores) on global and shared memory, and on generic addresses that point there. On the stack they are undefined, and two things go wrong in practice:

This PR handles both from the PTX finish_ir!, after optimization and before validation, the way the Metal and SPIR-V back-ends already deal with atomics on thread memory:

  • Atomics on pointers that are known to point to the stack (derived from an alloca, or in the local address space) become plain accesses, since no other thread can access that memory. They also no longer fail validation, e.g. for system scope before sm_60.
  • Atomics on generic pointers that may point to the stack check isspacep.local at run time, and use plain accesses on local memory and the original atomic otherwise. That is what NVIDIA's libcu++ does for every cuda::atomic_ref operation, on every architecture (Fix UB in atomics with automatic storage NVIDIA/cccl#2586), and what AMDGPU does in its back-end for flat atomics that may hit private memory. The kept atomic gets !noalias.addrspace metadata, so it isn't checked again.

Atomics on global or shared pointers, which is what CUDA.jl's arrays use, are left alone, and NVPTX folds the check when it can infer the address space, e.g. for kernel arguments. In the worst case, a generic atomic in a callee that isn't inlined, a kernel doing 256 atomic additions per thread took 0.60 ms with and without the check on an RTX 5080.

The check covers atomicrmw, cmpxchg, and atomic loads and stores that are at least monotonic. Julia's unordered accesses of heap references aren't checked, as they are too common to pay for it: NVPTX only lowers those to the relaxed instructions that the stack doesn't support from sm_70 on, where they haven't been seen to fail.

The new tests in test/ptx.jl work on IR and PTX, and pass on Julia 1.10 (typed pointers) and 1.13. On an RTX 5080, a kernel doing a cmpxchg on a stack slot fails to compile on main and runs correctly with this PR, as do atomics in a non-inlined callee on its caller's stack slot. I could not test on a Pascal GPU.

@codecov

codecov Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.09804% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.72%. Comparing base (5419b7b) to head (e0b534d).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
src/ptx.jl 95.09% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #963      +/-   ##
==========================================
+ Coverage   89.54%   89.72%   +0.18%     
==========================================
  Files          31       31              
  Lines        6935     7029      +94     
==========================================
+ Hits         6210     6307      +97     
+ Misses        725      722       -3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

maleadt and others added 2 commits October 16, 2026 16:45
PTX has no atomics on the stack (local memory): ptxas rejects the
`atom.local.cas` that NVPTX selects for a `cmpxchg` on an `alloca`,
atomic loads and stores on the stack become `ld.acquire` and
`st.release` of generic addresses, which PTX leaves undefined, and NVPTX
only demotes `atomicrmw` on pointers it knows to be local, and only
when optimizing. Atomics on stack slots also failed validation for no
reason, e.g., system-scope ones before sm_60.

Like on Metal and SPIR-V, replace atomics on the thread's own memory by
plain accesses from `finish_ir!`, after optimization and before
validation. On PTX, pointers in the local address space are private too.

Co-authored-by: Tim Esselens <tim.esselens@gmail.com>
Atomics on generic pointers that point to the stack are undefined in
PTX, and fail on Pascal GPUs with CUDA_ERROR_INVALID_ADDRESS_SPACE,
which leaves the context unusable. Enzyme emits them when it accumulates
into the shadow of an argument without seeing where the argument points,
e.g., when a stack slot of the caller is passed to a function that isn't
inlined (Enzyme.jl#428). Inlining hides the problem only sometimes.

Like NVIDIA's libcu++ does for `cuda::atomic_ref`, check `isspacep.local`
before atomic operations on generic pointers that may point to the
stack, and perform the operation with plain accesses on local memory.
The atomic that is kept for other memory gets `!noalias.addrspace`
metadata, so that it isn't checked again. Pointers known to point to
global or shared memory are left alone, and NVPTX folds the check when
it can infer the address space, e.g., for pointers passed to a kernel.

Julia's `unordered` accesses of heap references aren't checked: they are
not used to synchronize, and NVPTX only lowers them to the relaxed
loads and stores that local memory doesn't support from sm_70 on, where
they haven't been seen to fail.

Co-authored-by: Tim Esselens <tim.esselens@gmail.com>
@maleadt
maleadt force-pushed the pr/ptx-local-atomics branch from d8cfd5c to e0b534d Compare October 16, 2026 15:07
@maleadt maleadt changed the title PTX: avoid atomics on thread-private memory PTX: handle atomics on local memory Oct 16, 2026
@maleadt
maleadt marked this pull request as ready for review October 16, 2026 15:13
@maleadt

maleadt commented Oct 16, 2026

Copy link
Copy Markdown
Member

I'd rather this would be fixed in NVPTX, but a workaround is fine for now. Rebased, and slightly reworked; will merge when it passes CI.

@maleadt
maleadt merged commit d261082 into JuliaGPU:main Oct 17, 2026
64 of 68 checks passed
@timesselens

Copy link
Copy Markdown
Contributor Author

Thank you! Will report back with Pascal tests

@timesselens

Copy link
Copy Markdown
Contributor Author

Confirmed on Pascal (GTX 1080, Linux, Julia 1.12.7): the ptx tests and CUDA.jl's atomics tests pass, and the Enzyme reverse-mode kernel that failed with CUDA_ERROR_INVALID_ADDRESS_SPACE (717) before this PR now gives the right result.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants