Skip to content

pocl: define the device runtime as overlays so sysimages load - #930

Open
IanButterworth wants to merge 2 commits into
JuliaGPU:mainfrom
IanButterworth:ib/pocl-overlay-runtime
Open

IanButterworth wants to merge 2 commits into
JuliaGPU:mainfrom
IanButterworth:ib/pocl-overlay-runtime

Conversation

@IanButterworth

@IanButterworth IanButterworth commented Oct 27, 2026 •

Copy link
Copy Markdown

A PackageCompiler sysimage that includes KernelAbstractions 0.10 builds, but doesn't load:

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

The POCL device runtime (kernel_state, claim_output, signal_exception, the RNG helpers, ...) was defined as ordinary methods whose bodies reach llvmcalls that declare device intrinsics. Sysimage builds compile such methods for the host:

  • Julia 1.12 and later compile every method with a compileable signature.
  • Julia 1.11 keeps native code instances, e.g. those left by inferring initialize_rng_state with Core.Compiler.return_type in finish_module!.

A sysimage is loaded with RTLD_NOW, so the undefined symbols stop it from loading. Package images load lazily, except on Windows, where lld links them eagerly: that is why src/precompile.jl didn't launch a kernel on Julia 1.11 on Windows. This is the same class of leak as JuliaGPU/GPUCompiler.jl#611.

pocl: Define the device runtime as overlays so sysimages load

  • The device-only runtime functions are @device_functions: a host method that throws, with the real body in the overlay table.
  • malloc and report_oom use no intrinsics and stay host methods.
  • initialize_rng_state returns nothing. It's looked up through the job's method tables and called as a void function, instead of inferring its return type with the native interpreter.
  • A test checks GPUToolbox.Overlays.host_leaks(KernelAbstractions) is empty. It emulates the sysimage compile-all on typed IR and reports methods that reach device symbols.

Precompile a kernel launch on Julia 1.11 on Windows again

With the runtime fixed, and GPUCompiler 2.19 (JuliaGPU/GPUCompiler.jl#1067) making its boxing functions linkable on the host, the Windows 1.11 gate on the precompile workload goes.

Requires GPUToolbox 3.5 (host_leaks, registered), SPIRVIntrinsics 1.6 (JuliaGPU/OpenCL.jl#598, whose @device_function defines the body as a plain rather than a consistent overlay, so inference can't fold calls into the host error) and GPUCompiler 2.19 (being registered).

Reproducer:

# precompile.jl
using KernelAbstractions
@kernel function fill_index!(a)
    i = @index(Global)
    a[i] = i
end
a = zeros(Float32, 16)
fill_index!(CPU())(a; ndrange = length(a))
KernelAbstractions.synchronize(CPU())

# build.jl
using PackageCompiler
create_sysimage([:KernelAbstractions]; sysimage_path = "ka.so", precompile_execution_file = "precompile.jl")

Then julia -J ka.so -e 'println("loaded")'. On v0.10.3 this fails on Julia 1.11.9, 1.12.7 and 1.13.3; with this PR it loads on all three. On Windows, Julia 1.11 precompiles with the launch, loads, runs kernels and reports device exceptions.

The other back ends get the same treatment in separate PRs.

@codecov

codecov Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.44444% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 83.13%. Comparing base (5dd8a01) to head (de05727).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
src/pocl/device/random.jl 83.33% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #930   +/-   ##
=======================================
  Coverage   83.13%   83.13%           
=======================================
  Files          31       31           
  Lines        2568     2568           
=======================================
  Hits         2135     2135           
  Misses        433      433           

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

IanButterworth and others added 2 commits October 28, 2026 08:40
The POCL device runtime was defined as ordinary methods whose bodies
declare device intrinsics such as `julia.gpu.state_getter`. Sysimage
builds compile those methods for the host: Julia 1.12 and later compile
every method with a compileable signature, and Julia 1.11 keeps the
native code instances that inferring `initialize_rng_state` left
behind. A sysimage is loaded with RTLD_NOW, so the undefined
intrinsics stop it from loading.

Define the device-only runtime functions with `@device_function`, so
the host only sees a method that throws and the real body lives in the
overlay table, as SPIRVIntrinsics does for its built-ins. `malloc` and
`report_oom` use no intrinsics and stay host methods. Look up
`initialize_rng_state` through the job's method tables and call it as
a void function, instead of inferring its return type natively. Test
that no method of the package reaches device code on the host, with
GPUToolbox's `host_leaks`.

This needs SPIRVIntrinsics 1.6, whose `@device_function` defines the
body as a plain overlay, and GPUToolbox 3.5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Julia 1.11, the launch in the precompile workload left GPUCompiler's
runtime in the package image, and on Windows lld links package images
eagerly. The device symbols that runtime referenced made the link fail,
so the workload skipped the launch there. The POCL runtime no longer
reaches device intrinsics on the host, and GPUCompiler 2.19 makes its
boxing functions refer to type tags it can link, so drop the gate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@maleadt

maleadt commented Oct 28, 2026

Copy link
Copy Markdown
Member

Rebased and reworked.

  • The device functions now use GPUToolbox 3.5's shared definitions, via SPIRVIntrinsics 1.6.
  • kernel_state is a @device_function directly, since that handles @generated now.
  • seed!(::Philox2x32, ::Nothing) is back to a plain method: there's no host method for it to be consistent with.
  • The test is now GPUToolbox.Overlays.host_leaks, which emulates the sysimage compile-all.
  • I added a commit that re-enables the precompiled launch on Windows 1.11, now that GPUCompiler 2.19 fixes the box tags.

The other back ends get the same fix in separate PRs.

This branch has not been deployed

No deployments
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