Skip to content

Report device-side exceptions at host synchronization - #763

Open
vchuravy wants to merge 2 commits into
mainfrom
pocl/device-exceptions
Open

vchuravy wants to merge 2 commits into
mainfrom
pocl/device-exceptions

Conversation

@vchuravy

Copy link
Copy Markdown
Member

POCL kernels can now report failures to the host as KernelExceptions. A faulting work-item returns, and the error is raised when the host synchronizes. Because POCL launches are synchronous, that is normally the launch itself; a kernel started through @opencl (which does not wait for its event) reports at the next KernelAbstractions.synchronize. The report is consumed before throwing, so subsequent kernels can run normally.

using KernelAbstractions

@kernel function oob(a)
    i = @index(Global, Linear)
    a[i + 1] = 1.0f0
end

a = KernelAbstractions.zeros(POCLBackend(), Float32, 1)
oob(POCLBackend())(a; ndrange = 1)
# ERROR: KernelException: A BoundsError was thrown on device cpu-haswell-...: Out-of-bounds array access
# For more details, run Julia with `-g2`

The new debug_level compiler keyword defaults to Julia's -g setting. Level 0 records only that an exception occurred; level 1 adds names and reasons for common runtime errors; level 2 also records work-item coordinates and a bounded device-side backtrace:

KernelException: A BoundsError was thrown by work-item 1×1×1 in work-group 1×1×1 on device cpu-haswell-...: Out-of-bounds array access
Stacktrace:
 [1] macro expansion at src/pocl/device/runtime.jl:214
 [2] throw_boundserror at src/pocl/device/quirks.jl:57
 [3] checkbounds at ./abstractarray.jl:699
 [4] arrayset at src/pocl/device/array.jl:128
 [5] setindex! at src/pocl/device/array.jl:171
 ...

Each device in a context has a mailbox shared by its queues. Synchronizing may therefore wait for and report an error from another queue on that device. Device-scoped atomics select one writer, identified by its launch id and work-item coordinates, so later kernels cannot overwrite its diagnostics.

Differences from upstream

Ports JuliaGPU/OpenCL.jl#478. The device-side half (mailbox layout, atomic claim protocol, @gputhrow, the report_* runtime hooks, quirks) is a direct port. Two things are simpler here:

  • Mailbox memory. Upstream picks between USM host memory, fine- or coarse-grained SVM and a mappable buffer, and maps/unmaps around every host access. POCL's CPU device shares the host address space and nanoOpenCL already hands raw host pointers to kernels, so the mailbox is a plain Libc.malloc allocation the kernel reaches by pointer — no backend selection, no mapping, and no "device cannot report exceptions" warning path.
  • Where the check happens. Upstream hooks cl.finish and blocking copies, and needs a non-throwing variant for finalizers. The POCL back-end's arrays are Base.Array and every launch already waits on its event, so the check lives at the end of the two launch paths in backend.jl, plus KernelInterface.synchronize (previously a no-op) for kernels launched via @opencl. There is no finalizer-driven synchronization to work around.

Also unlike upstream, KernelException is not exported; it is reachable as KernelAbstractions.POCL.KernelException.

Requires GPUCompiler 2.7 for SPIR-V exception lowering, failed-boxing handling, and the correct address space for diagnostic strings — so this is stacked on 761 and targets that branch.

Validation

Julia 1.12.7, POCL 7.2, cpu-haswell device. Full test suite green (2424 pass, 4 pre-existing broken, 0 failures) with all three ports applied together. New tests cover debug levels 0/1/2 in the emitted IR, a KA @kernel that goes out of bounds, mailbox reset and device reuse afterwards, the @opencl deferred-report path, a boxed exception whose throw path must survive the optimizer, and the recorded name/reason. Manually checked -g0, -g1 and -g2 sessions end to end.

🤖 Generated with Claude Code

https://claude.ai/code/session_019X2CqtAxNnxqTxgXvz19pJ

vchuravy and others added 2 commits September 10, 2026 16:38
Yggdrasil replaced the `llc`, `lld` and `llvm-downgrade` executables in the GPU
LLVM JLLs with shared libraries exposing a small C API, moving the back-ends to
LLVM 23 at the same time. GPUCompiler 2.7 calls those libraries in-process and
bumps its compat to the new JLL majors. We pin the same JLL (for `versioninfo`),
so its compat has to move in lockstep.

Follows JuliaGPU/OpenCL.jl#480.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019X2CqtAxNnxqTxgXvz19pJ
POCL kernels can now report failures to the host as `KernelException`s. A
faulting work-item returns, and the error is raised when the host synchronizes.
Because POCL launches are synchronous, that is normally the launch itself; a
kernel started through `@opencl` (which does not wait for its event) reports at
the next `KernelAbstractions.synchronize`. The report is consumed before
throwing, so subsequent kernels can run normally.

```julia
using KernelAbstractions

@kernel function oob(a)
    i = @index(Global, Linear)
    a[i + 1] = 1.0f0
end
a = KernelAbstractions.zeros(POCLBackend(), Float32, 1)
oob(POCLBackend())(a; ndrange = 1)  # KernelException with BoundsError details
```

The new `debug_level` compiler keyword defaults to Julia's `-g` setting. Level 0
records only that an exception occurred; level 1 adds names and reasons for
common runtime errors; level 2 also records work-item coordinates and a bounded
device-side backtrace.

Each device in a context has a mailbox shared by its queues. Synchronizing may
therefore wait for and report an error from another queue on that device. Device
atomics select one writer, identified by its launch and work-item coordinates,
so later kernels cannot overwrite its diagnostics. POCL's CPU device shares the
host address space, so the mailbox is an ordinary host allocation reached by
pointer; it is retained for the lifetime of the process. Failed kernels may leave
incomplete results, and work-group barriers must still obey convergence rules.

Requires GPUCompiler 2.7 for SPIR-V exception lowering, failed-boxing handling,
and the correct address space for diagnostic strings.

Follows JuliaGPU/OpenCL.jl#478.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019X2CqtAxNnxqTxgXvz19pJ
@github-actions

Copy link
Copy Markdown
Contributor

Benchmark Results

Show table
main 903995f... main / 903995f...
saxpy/default/Float16/1024 24.3 ± 9.5 μs 26.4 ± 8.9 μs 0.924 ± 0.48
saxpy/default/Float16/1048576 0.317 ± 0.018 ms 0.321 ± 0.019 ms 0.987 ± 0.08
saxpy/default/Float16/16384 0.0485 ± 0.023 ms 0.0509 ± 0.024 ms 0.953 ± 0.63
saxpy/default/Float16/2048 28.8 ± 19 μs 29.4 ± 15 μs 0.979 ± 0.82
saxpy/default/Float16/256 23.5 ± 6.6 μs 25.6 ± 7.5 μs 0.919 ± 0.37
saxpy/default/Float16/262144 0.114 ± 0.022 ms 0.117 ± 0.021 ms 0.974 ± 0.26
saxpy/default/Float16/32768 0.0542 ± 0.024 ms 0.0549 ± 0.024 ms 0.988 ± 0.62
saxpy/default/Float16/4096 0.0405 ± 0.021 ms 0.0349 ± 0.022 ms 1.16 ± 0.96
saxpy/default/Float16/512 25.3 ± 15 μs 26.5 ± 15 μs 0.953 ± 0.79
saxpy/default/Float16/64 23.2 ± 5.1 μs 25.2 ± 7.8 μs 0.919 ± 0.35
saxpy/default/Float16/65536 0.0615 ± 0.025 ms 0.0587 ± 0.021 ms 1.05 ± 0.57
saxpy/default/Float32/1024 24.1 ± 11 μs 26.7 ± 12 μs 0.902 ± 0.58
saxpy/default/Float32/1048576 0.318 ± 0.056 ms 0.33 ± 0.058 ms 0.965 ± 0.24
saxpy/default/Float32/16384 0.0492 ± 0.023 ms 0.0524 ± 0.024 ms 0.939 ± 0.61
saxpy/default/Float32/2048 0.0405 ± 0.024 ms 0.0355 ± 0.025 ms 1.14 ± 1
saxpy/default/Float32/256 23.7 ± 9.8 μs 26 ± 9.8 μs 0.911 ± 0.51
saxpy/default/Float32/262144 0.105 ± 0.019 ms 0.11 ± 0.019 ms 0.954 ± 0.24
saxpy/default/Float32/32768 0.0518 ± 0.024 ms 0.0542 ± 0.024 ms 0.956 ± 0.62
saxpy/default/Float32/4096 0.0476 ± 0.021 ms 0.0434 ± 0.026 ms 1.09 ± 0.81
saxpy/default/Float32/512 24.1 ± 10 μs 26.3 ± 8.4 μs 0.918 ± 0.49
saxpy/default/Float32/64 23.1 ± 4.6 μs 25.4 ± 6.6 μs 0.91 ± 0.3
saxpy/default/Float32/65536 0.0596 ± 0.025 ms 0.0648 ± 0.026 ms 0.92 ± 0.54
saxpy/default/Float64/1024 25.6 ± 21 μs 27.7 ± 23 μs 0.924 ± 1.1
saxpy/default/Float64/1048576 0.574 ± 0.088 ms 0.569 ± 0.07 ms 1.01 ± 0.2
saxpy/default/Float64/16384 0.0493 ± 0.025 ms 0.0526 ± 0.026 ms 0.937 ± 0.66
saxpy/default/Float64/2048 0.0327 ± 0.025 ms 0.0326 ± 0.026 ms 1 ± 1.1
saxpy/default/Float64/256 23.5 ± 5.6 μs 25.8 ± 5.5 μs 0.911 ± 0.29
saxpy/default/Float64/262144 0.168 ± 0.03 ms 0.172 ± 0.03 ms 0.979 ± 0.25
saxpy/default/Float64/32768 0.056 ± 0.025 ms 0.0601 ± 0.026 ms 0.932 ± 0.59
saxpy/default/Float64/4096 0.0399 ± 0.025 ms 0.0428 ± 0.026 ms 0.933 ± 0.82
saxpy/default/Float64/512 23.7 ± 6.6 μs 25.9 ± 7 μs 0.917 ± 0.36
saxpy/default/Float64/64 23.1 ± 5.3 μs 25.6 ± 7.8 μs 0.901 ± 0.34
saxpy/default/Float64/65536 0.0698 ± 0.024 ms 0.0734 ± 0.026 ms 0.95 ± 0.47
saxpy/static workgroup=(1024,)/Float16/1024 24.2 ± 14 μs 25.9 ± 9.3 μs 0.932 ± 0.64
saxpy/static workgroup=(1024,)/Float16/1048576 0.316 ± 0.019 ms 0.321 ± 0.019 ms 0.986 ± 0.082
saxpy/static workgroup=(1024,)/Float16/16384 0.0498 ± 0.023 ms 0.0521 ± 0.022 ms 0.956 ± 0.6
saxpy/static workgroup=(1024,)/Float16/2048 28 ± 19 μs 29.2 ± 14 μs 0.958 ± 0.78
saxpy/static workgroup=(1024,)/Float16/256 23.3 ± 15 μs 25.4 ± 9.3 μs 0.92 ± 0.7
saxpy/static workgroup=(1024,)/Float16/262144 0.112 ± 0.021 ms 0.116 ± 0.021 ms 0.967 ± 0.25
saxpy/static workgroup=(1024,)/Float16/32768 0.0467 ± 0.021 ms 0.0549 ± 0.024 ms 0.851 ± 0.53
saxpy/static workgroup=(1024,)/Float16/4096 0.0406 ± 0.02 ms 0.0367 ± 0.021 ms 1.11 ± 0.84
saxpy/static workgroup=(1024,)/Float16/512 24.8 ± 20 μs 26.7 ± 16 μs 0.93 ± 0.93
saxpy/static workgroup=(1024,)/Float16/64 22.9 ± 8.4 μs 24.8 ± 8.5 μs 0.923 ± 0.46
saxpy/static workgroup=(1024,)/Float16/65536 0.0599 ± 0.023 ms 0.0633 ± 0.024 ms 0.947 ± 0.51
saxpy/static workgroup=(1024,)/Float32/1024 23.2 ± 12 μs 25.4 ± 8.5 μs 0.915 ± 0.55
saxpy/static workgroup=(1024,)/Float32/1048576 0.329 ± 0.051 ms 0.324 ± 0.055 ms 1.02 ± 0.23
saxpy/static workgroup=(1024,)/Float32/16384 0.0472 ± 0.022 ms 0.05 ± 0.022 ms 0.945 ± 0.61
saxpy/static workgroup=(1024,)/Float32/2048 0.0358 ± 0.023 ms 0.0333 ± 0.025 ms 1.08 ± 1.1
saxpy/static workgroup=(1024,)/Float32/256 24 ± 17 μs 26.6 ± 13 μs 0.901 ± 0.79
saxpy/static workgroup=(1024,)/Float32/262144 0.103 ± 0.019 ms 0.109 ± 0.02 ms 0.95 ± 0.24
saxpy/static workgroup=(1024,)/Float32/32768 0.0515 ± 0.024 ms 0.0563 ± 0.025 ms 0.915 ± 0.59
saxpy/static workgroup=(1024,)/Float32/4096 0.0357 ± 0.023 ms 0.0379 ± 0.025 ms 0.943 ± 0.86
saxpy/static workgroup=(1024,)/Float32/512 23.7 ± 15 μs 25.7 ± 9 μs 0.921 ± 0.66
saxpy/static workgroup=(1024,)/Float32/64 23.3 ± 12 μs 24.7 ± 7 μs 0.943 ± 0.57
saxpy/static workgroup=(1024,)/Float32/65536 0.0593 ± 0.025 ms 0.0622 ± 0.024 ms 0.953 ± 0.54
saxpy/static workgroup=(1024,)/Float64/1024 26.1 ± 24 μs 26.6 ± 20 μs 0.981 ± 1.2
saxpy/static workgroup=(1024,)/Float64/1048576 0.565 ± 0.085 ms 0.591 ± 0.079 ms 0.956 ± 0.19
saxpy/static workgroup=(1024,)/Float64/16384 0.0489 ± 0.023 ms 0.0517 ± 0.024 ms 0.946 ± 0.62
saxpy/static workgroup=(1024,)/Float64/2048 0.0339 ± 0.025 ms 0.0336 ± 0.025 ms 1.01 ± 1
saxpy/static workgroup=(1024,)/Float64/256 25.2 ± 14 μs 25.5 ± 7.5 μs 0.988 ± 0.61
saxpy/static workgroup=(1024,)/Float64/262144 0.168 ± 0.029 ms 0.174 ± 0.031 ms 0.966 ± 0.24
saxpy/static workgroup=(1024,)/Float64/32768 0.055 ± 0.024 ms 0.058 ± 0.024 ms 0.948 ± 0.58
saxpy/static workgroup=(1024,)/Float64/4096 0.0393 ± 0.024 ms 0.0431 ± 0.024 ms 0.912 ± 0.75
saxpy/static workgroup=(1024,)/Float64/512 23.8 ± 10 μs 26 ± 9 μs 0.918 ± 0.5
saxpy/static workgroup=(1024,)/Float64/64 23.3 ± 14 μs 25.4 ± 14 μs 0.916 ± 0.76
saxpy/static workgroup=(1024,)/Float64/65536 0.0679 ± 0.024 ms 0.073 ± 0.025 ms 0.929 ± 0.46
time_to_load 0.605 ± 0.0035 s 0.62 ± 0.0084 s 0.975 ± 0.014

Benchmark Plots

A plot of the benchmark results have been uploaded as an artifact to the workflow run for this PR.
Go to "Actions"->"Benchmark a pull request"->[the most recent run]->"Artifacts" (at the bottom).

@christiangnrd
christiangnrd force-pushed the pocl/spirv-backend-jll-23 branch from 0fa7d1c to fc80276 Compare September 11, 2026 19:58
Base automatically changed from pocl/spirv-backend-jll-23 to main September 11, 2026 20:56
@vchuravy vchuravy added this to the 0.10.0 milestone Sep 13, 2026

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.

1 participant