Skip to content

Add shared definitions of device functions, and a check for host leaks - #38

Merged
maleadt merged 1 commit into
mainfrom
tb/device-functions
Oct 27, 2026
Merged

maleadt merged 1 commit into
mainfrom
tb/device-functions

Conversation

@maleadt

@maleadt maleadt commented Oct 27, 2026

Copy link
Copy Markdown
Member

Device code that ends up in the global method table gets compiled for the host by sysimage builds: Julia 1.12+ compiles every method with a compileable signature, and on 1.11 native code instances leak in. The result refers to symbols that only exist on the device (julia.gpu.state_getter, julia.opencl.random_keys, __spirv_*) or to intrinsics of a GPU target. Such a sysimage fails to load, and on Windows a package image fails to link. For example, a sysimage with KernelAbstractions 0.10:

ERROR: could not load library "ka.so"
ka.so: undefined symbol: julia.gpu.state_getter

Back-ends avoid this by defining device-only functions as a host method that throws plus an overlay with the real body (CUDA.jl#3249). Each back-end has its own copy of the @device_override and @device_function macros, and the copies have drifted apart:

  • SPIRVIntrinsics' @device_function, which KernelAbstractions, OpenCL.jl and oneAPI.jl use, makes the device body a @consistent_overlay. Its host method throws, so inference can constant-fold calls into that error.
  • Only CUDA.jl's version handles definitions in @doc blocks on Julia 1.13.

This PR adds the shared pieces to GPUToolbox.Overlays:

  • device_override(mod, table, ex) and device_function(mod, table, ex) implement these macros, given the back-end's method table. Each back-end keeps a one-line macro:
    macro device_function(ex)
        esc(GPUToolbox.Overlays.device_function(__module__, GlobalRef(MyBackend, :method_table), ex))
    end
    device_override replaces an existing host method and uses @consistent_overlay on 1.11+. device_function uses a plain @overlay, and handles single definitions, blocks, docstrings and @generated definitions.
  • host_leaks(mod) finds the methods that sysimage builds compile for the host, following Julia's own enumeration, and whose code reaches device-only symbols. Back-ends can check it in their tests:
    @test isempty(GPUToolbox.Overlays.host_leaks(MyBackend))
    It inspects typed IR, so it needs no back-end knowledge and no dependencies. It is conservative: an llvmcall that declares a non-intrinsic function counts. It doesn't follow calls into other packages, which should run their own check.

On current releases (Julia 1.13), host_leaks reports:

package leaks time
CUDACore 0 7 s
KernelAbstractions 0.10.3 4 (signal_exception, claim_output, random_keys, random_counters) 4 s
KernelAbstractions with JuliaGPU/KernelAbstractions.jl#930 0 3 s
OpenCL.jl 6 3 s
AMDGPU.jl 9, including llvm.amdgcn.mbcnt.lo 16 s
oneAPI.jl 5 8 s

These match the functions found by reading the code, and in KernelAbstractions they are exactly what keeps its sysimage from loading.

Tested on Julia 1.10 to 1.13.

Back-ends each have their own `@device_override` and `@device_function`
macros, which have drifted apart: SPIRVIntrinsics' `@device_function`
makes the device implementation a consistent overlay of a host method
that throws, and only CUDA.jl's handles definitions in `@doc` blocks on
Julia 1.13. A device function that doesn't end up as an overlay leaves
device code in the global method table, where sysimage builds compile it
for the host; such an image fails to load, as does a package image on
Windows.

Add `Overlays.device_override` and `Overlays.device_function`, which
implement these macros given the back-end's method table, and
`Overlays.host_leaks`, which finds the methods that sysimage builds would
compile and whose code reaches device-only symbols, for back-ends to
check in their tests.
@maleadt
maleadt force-pushed the tb/device-functions branch from c1633bf to 524190f Compare October 27, 2026 17:45
@github-actions

Copy link
Copy Markdown

Documentation preview: https://juliagpu.github.io/GPUToolbox.jl/previews/PR38/

@maleadt
maleadt merged commit f92e6e7 into main Oct 27, 2026
10 checks passed
@maleadt
maleadt deleted the tb/device-functions branch October 27, 2026 19:03
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