Skip to content

Metal: load thread pointers from other address spaces as words - #1012

Merged
maleadt merged 3 commits into
mainfrom
tb/metal-pointer-words
Oct 14, 2026
Merged

maleadt merged 3 commits into
mainfrom
tb/metal-pointer-words

Conversation

@maleadt

@maleadt maleadt commented Oct 14, 2026

Copy link
Copy Markdown
Member

On Metal, a Symbol read from an array compares wrong (JuliaGPU/Metal.jl#1017):

julia> map(t -> t[1] === :begin, MtlArray([(:begin, :end)]))
1-element MtlVector{Bool, Metal.PrivateStorage}:
 0

Julia object references end up as ptr in address space 0, which AIR treats as thread memory. When such a pointer is loaded from another address space (load ptr, ptr addrspace(1)), Apple's back-end keeps only its low 32 bits: a stored 0x1050e7450 reads back as 0x050e7450. Loading the same bytes as i64 and converting with inttoptr keeps all 64 bits, through stores, select, icmp, and round-trips through thread memory. I checked this with hand-written IR kernels on an M1 with macOS 27; the issue was reported on an M2.

#975 already loads relocation-table entries this way. This PR moves that code into a helper and adds a pass at the end of the AIR lowering. The pass rewrites every load of an AS 0 pointer from a non-AS 0 address into a word load plus inttoptr. It has to run last, because InstCombine would fold it back into a pointer load. Alignment, volatility, atomic ordering and the metadata that applies to integers carry over.

Mapping object references to the device address space instead doesn't work: boxes demoted to the stack and sret slots are real thread pointers that get merged with these references, and AIR can't cast between the two spaces. Loads of aggregates or vectors that contain such pointers, and pointer atomics, are not rewritten.

Tested on Julia 1.10, 1.11 and 1.12 with the GPUCompiler Metal tests; the new IR test fails without the pass. The Metal.jl regression tests are on the tb/thread-pointers branch of Metal.jl.

The relocation lowering loads table entries as words and converts them
back with `inttoptr`. Move that into a helper so that the Metal back-end
can use it for other pointer loads, and bitcast the address under typed
pointers, where the load type has to match the pointee type.
Julia object references are AS 0 pointers, which AIR considers thread
memory. The AGX back-end keeps only the low 32 bits of such a pointer when
it is loaded from device memory, so a `Symbol` stored in an array no longer
compared equal to itself (JuliaGPU/Metal.jl#1017). Pointers produced by
`inttoptr` keep all their bits, so load these pointers as words, at the end
of the AIR lowering where InstCombine cannot fold them back.
Byref kernel arguments arrive in device memory, and were copied to a stack
slot for the body, which expects AS 0 pointers. Optimization normally
removes that copy, but not when the argument is indexed dynamically, and
the AGX back-end miscompiles some kernels where it survives: a closure
capturing arrays and a dynamically indexed `SVector` wrote nothing for
most threads (JuliaGPU/Metal.jl#1018).

Pass arguments that are only loaded from as casts of the device pointer
instead, which address-space inference turns into device-memory loads.
Arguments with other uses, e.g. passed to a call that also receives stack
memory, are still copied.
@maleadt
maleadt merged commit e9adac4 into main Oct 14, 2026
31 of 33 checks passed
@maleadt
maleadt deleted the tb/metal-pointer-words branch October 14, 2026 14:50
@codecov

codecov Bot commented Oct 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.18182% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.54%. Comparing base (7ac7582) to head (e176f41).
⚠️ Report is 5 commits behind head on main.

Files with missing lines Patch % Lines
src/utils.jl 80.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1012      +/-   ##
==========================================
+ Coverage   89.51%   89.54%   +0.02%     
==========================================
  Files          31       31              
  Lines        6906     6935      +29     
==========================================
+ Hits         6182     6210      +28     
- Misses        724      725       +1     

☔ 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.

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.

1 participant