Conversation
This was referenced Aug 21, 2026
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3243 +/- ##
==========================================
- Coverage 92.66% 92.65% -0.02%
==========================================
Files 184 184
Lines 16176 16176
==========================================
- Hits 14990 14988 -2
- Misses 1186 1188 +2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
maleadt
marked this pull request as ready for review
August 21, 2026 17:15
Contributor
CUDA.jl BenchmarksDetails
This comment was automatically generated by workflow using github-action-benchmark. |
Port LLVM libc's GPU RPC protocol: a warp-collective mailbox in pinned, device-mapped host memory, with per-lane packets and device-memory locks. Shared mailboxes use only loads, stores, and fences because system-scope RMW atomics are not atomic across PCIe. Expose raw ports plus @hostcall and hostcall/hostcall_async for by-value calls to statically identifiable host functions. Arguments may include compiler-relocated host constants; results must be isbits. A target's type hash is embedded in the kernel image, and the protocol descriptor shares the compact kernel state used by dynamic parallelism.
Service per-context hostcall areas from a foreign libuv thread so calls progress while Julia threads block in CUDA. The server polls while launches are armed, backs off when idle, and uses cuLaunchHostFunc only to post a semaphore. Accept compiler-described static targets and precompile handlers before the server runs them. Handlers use the calling context and a dedicated non-blocking stream; failures, deferred output and asynchronous completion surface at synchronization.
Recover the statically-known hostcall targets of a kernel from its compiled method instances and store them in the compile results, so that they travel with cached kernel images and get registered at link time, including for kernels compiled during package precompilation. Kernels that may hostcall get a full-size area (one port per resident warp) and arm the server around every launch, with the disarm enqueued on the stream; kernels captured into a graph are replayed behind our back, so capture is detected and such kernels are serviced by the heartbeat instead. On Windows, the launch queue is flushed after arming because WDDM may batch command submission.
Send the exception name, reason and (with -g2) stack frames through a built-in hostcall target instead of printing them from the device, and attach the decoded report to the KernelException thrown at synchronization; the strings are module constants, copied by the host on the hostcall stream. The device never waits for the host on this path, and the exception output lock admits only one lane, so the sender only needs a scalar subset of the warp-collective protocol (which also keeps compile time and PTX size down for every throwing kernel, and sidesteps a CUDA 12.9 ptxas crash on out-of-line aggregate debug info). Reports need no registration, so precompiled kernels report fine in a fresh session; printf-based reporting remains as the fallback when hostcall is unavailable.
Add a manual page describing the API layers, handler rules, synchronization semantics, multi-device behavior, performance characteristics and preferences; reference the device API from the kernel programming docs, update the debugging page for the new exception output, and add a NEWS entry.
Hostcall is core infrastructure for exception reporting and future runtime services, so remove the preference, environment variable, and printf fallback that allowed it to be disabled. Keep the no-port client only as a precompilation placeholder; its reporting guard produces a report-less KernelException if it is ever used.
Factor exception transport into the public single-lane asynchronous primitive hostcall_send_scalar!, and use it to report failed allocation sizes. The host merges this detail into the exception report in either arrival order, so synchronization reports the size through KernelException instead of device printf.
Factor the single-lane port claim, submit, and unlock operations out of hostcall_send_scalar!, and add hostcall_call_scalar! for request and reply. The primitive uses no warp collectives, so an elected lane may call from divergent code; pre-Volta callers must not independently elect several lanes from one converged warp.
Use the relocated pointer to each target's key type as its wire identifier. Julia codegen and the relocation resolver root and canonicalize these values, so the image and host registry agree without hashes or collision handling, including across cached images. Cache each target's dispatch-resolved MethodInstance by world and call Julia's exported jl_invoke entry point. This preserves invokelatest semantics across handler redefinition while leaving CodeInstance publication, compilation, and invocation to Julia's runtime. Publish registry snapshots atomically so the service thread reads them without locking.
A blocking warp must not wait for another resident warp to release a port, because GPUs do not guarantee forward progress. Treat hostcall_ports as a lower bound and raise the cap to LLVM libc RPC's 16K maximum.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR adds a hostcall foundation that lets CUDA kernels request work from Julia on the host and optionally receive a result.
The first consumer is device exception reporting, replacing limited
printfdiagnostics with structured exception names, reasons, and stack traces. The same foundation can later support features such as dynamic allocation and I/O without introducing separate device-to-host protocols.Design
Hostcalls use warp-collective mailboxes in mapped host memory. A dedicated adopted thread services requests independently of Julia’s thread pools, allowing calls to progress while other threads are blocked in CUDA.
The compiler records statically known targets alongside cached kernel images and registers them when linking. Runtime
HostFunctionhandles support closures and dynamically selected operations. Blocking, asynchronous, large-value, graph-replay, and error paths share the same protocol.Built-in, static, and runtime target identifiers occupy separate ranges, collisions are detected, and runtime handles have an explicit lifetime. Handler and kernel failures are attributed to their CUDA context and reported at the next stream, event, or device synchronization.
Platform support
Hostcalls are enabled by default on Windows, including WDDM. Command submission is flushed before polling, and the server uses a portable idle backoff. Kernels remain subject to the platform’s normal display watchdog, so handlers should avoid long or unbounded waits.
Multiple CUDA contexts and devices share one hostcall server. Requests are serviced in their originating context, exceptions are reported to the correct device, and context teardown releases associated hostcall and exception resources.
The device protocol supports pre-Volta GPUs using volatile mailbox accesses and legacy memory barriers, while Volta and newer use scoped memory operations. Both paths are covered by architecture-specific code-generation tests.
Scope and constraints
Hostcalls are intended for uncommon control paths rather than bulk data transfer. Handlers must not wait on the calling kernel, perform nested hostcalls, load new kernels, or depend on Julia task scheduling or libuv I/O. Print-family calls are queued and flushed safely during synchronization.