Conversation
`alloc_special` declared local memory as an external global named after its `id`, so call sites using the same `id` shared one allocation. Define it with internal linkage and an `undef` initializer instead, so that each call site gets its own, as KernelInterface's `localmemory` requires: it has no `id`. Co-authored-by: Christian Guinard <28689358+christiangnrd@users.noreply.github.com>
The maximum number of work-items along each dimension of a workgroup, from the device's `maxThreadsDim`. It is cached in `HIPDevice`, since KernelInterface queries it on every launch whose workgroup size it chooses.
KernelAbstractions 0.10 builds on KernelInterface, which defines what a back end provides: memory and device management, compiling and launching kernels, and the device-side intrinsics. KernelAbstractions then launches `@kernel` kernels itself on any KernelInterface back end, which replaces AMDGPU's copy of that launch path: partitioning the ndrange, building the kernel's context, and tuning the workgroup size. `ROCBackend` now implements KernelInterface, which AMDGPU depends on instead of on KernelAbstractions. What KernelAbstractions still needs from a back end moves to an extension: the `MArray` behind `@private`, the Adapt rule for `@Const`, and the `maxthreads` hint for a static workgroup size. `KI.launch` receives the kernel arguments as a tuple. A `HIPKernel` can't be launched with a tuple yet (#1115), so it splats them for now, which is slow for more than 32 arguments. It rejects `groupsize` and `gridsize`, which would override the launch geometry that KernelInterface validated. `KI.copyto!` accepts dense arrays and contiguous views of them. `KI.kernel_function` receives the callable unconverted. It compiles its converted form, and the kernel keeps the original alive, since the converted form only holds pointers to the arrays a closure captures. `KI.launch` converts it again for the launch's stream, like the arguments, so that those arrays are made available to the stream too. Co-authored-by: Christian Guinard <28689358+christiangnrd@users.noreply.github.com>
A barrier for the lanes of a wavefront, fenced at wavefront scope like `sync_workgroup` is at workgroup scope, so that memory accesses before it are visible to the other lanes afterwards. `llvm.amdgcn.wave.barrier` alone doesn't generate any code; it only keeps the compiler from moving code across it. Co-authored-by: Christian Guinard <28689358+christiangnrd@users.noreply.github.com>
Sub-groups are wavefronts: implement the sub-group queries,
`sub_group_barrier` with `sync_wavefront`, and `shfl_down`, and report their
support to KernelInterface. Kernels execute with the device's wavefront size,
which `KI.kernel_function` enforces.
KernelInterface leaves unspecified how work-items are grouped into sub-groups;
AMD GPUs form wavefronts from consecutive linear work-item indices, so the last
wavefront of a workgroup can be partial. The lane is the hardware lane
(`mbcnt`), which unlike `activelane` doesn't change in divergent code. Tests
check both.
`shfl_down` of a `Complex{Int32}` didn't compile, as `Int32` components had no
shuffle method.
Co-authored-by: Christian Guinard <28689358+christiangnrd@users.noreply.github.com>
Implement `KI.record_event` and `KI.wait_event` with a `HIPEvent` recorded on, and waited for by, the task's stream. `KernelAbstractions.@spawn` uses them to order a new task's work after the work its parent had queued, without synchronizing the parent; the default is a full synchronization. KernelInterface's testsuite checks them once `record_event` returns an event. Co-authored-by: Christian Guinard <28689358+christiangnrd@users.noreply.github.com>
`KI.versioninfo(ROCBackend())` prints `AMDGPU.versioninfo()`. Co-authored-by: Christian Guinard <28689358+christiangnrd@users.noreply.github.com>
…edKernels from git KernelAbstractions 0.10 and KernelInterface 0.4 aren't registered yet, and the registered AcceleratedKernels doesn't support KernelAbstractions 0.10. KernelAbstractions is only a weak dependency of AMDGPU, so it is listed in [extras] to be allowed in [sources]. Julia 1.10 doesn't support [sources], and without workspaces (Julia 1.11) only the test project picks up KernelAbstractions from them, so Buildkite runs the tests in the test project there, developing the packages from git on 1.10. The GPU-less and Enzyme jobs move to Julia 1.12. Drop this commit once they are registered.
gbaraldi
reviewed
Oct 1, 2026
gbaraldi
left a comment
Member
There was a problem hiding this comment.
Tested on MI300A + MI250 (ROCm 7.2.4):
- CI failure.
KA.Scratchpadbuilds aStaticArraysCore.MArray(undef), whose constructor and indexing live in StaticArrays, and KA 0.10 no longer loads that. GPUArrays' matmul (@private) then fails withInvalidIRError(jl_f_throw_methoderror): 524 failures and 67 errors across 9 files on both GPUs, while main passes. WrappingGPUCompiler.alloca(T, Val(prod(dims)), Val(AS.Private))in aROCDeviceArray(like POCL) fixes it; depending on StaticArrays would too. - Device switch. Calling a
KI.Kernelafterdevice!to another GPU segfaults inhipFuncGetAttribute(viaKI.max_work_group_size(kernel)). Store the device inROCKerneland re-resolve or throw. [sources]: AcceleratedKernelsmainis now 0.5.0, outside compat, so instantiate fails. AK 0.5 also makesbackenda kwarg, which breaksaccumulate/cumsum/cumprodinsrc/kernels/accumulate.jl.- Nit: each launch does 2–3 uncached attribute queries (~0.4–0.5 µs each); they could be cached like
max_workgroup_dims.
Perf on MI300A, main → PR: async launch 11.8 → 4.4 µs, launch+sync 28.1 → 17.8 µs, 3-D scale 0.334 → 0.222 ms. div/rem are gone from 3-D indexing (ISA 188 → 36 instructions).
This branch has not been deployed
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.
KernelAbstractions 0.10 moves its back-end API into a separate package, KernelInterface. A back end implements KernelInterface: allocating and copying memory, selecting devices, compiling and launching kernels, and the device-side intrinsics. KernelAbstractions then launches
@kernelkernels the same way on every back end (JuliaGPU/KernelAbstractions.jl#801). This PR ports AMDGPU.jl to that model in one step, as JuliaGPU/CUDA.jl#3314 does for CUDA. It supersedes #1047, which added KernelInterface next to the KernelAbstractions 0.9 back end, and Christian'sintrinsicsbranch, which ported an earlier version of it to KernelAbstractions 0.10.ROCBackendnow implements KernelInterface, and AMDGPU depends on KernelInterface instead of KernelAbstractions. That makes the KernelInterface layer usable on its own, without KernelAbstractions' macros:KernelAbstractions becomes a weak dependency. Its extension only provides what KernelAbstractions still needs from a back end: the
MArraybehind@private, the Adapt rule behind@Const(which still reads through the constant address space), and themaxthreadshint for kernels with a static workgroup size. AMDGPU's own copy of the KernelAbstractions launch path goes away: partitioning the ndrange, building the kernel's context, tuning the workgroup size with the occupancy API, and calling the kernel. In practice KernelAbstractions is still always loaded, because AcceleratedKernels depends on it, so the extension is about where the code lives rather than about load time.Kernels behave the same, but they are launched differently. KernelAbstractions now launches them on a 3-D grid, and computes
@indexin 32 bits when the iteration space fits (JuliaGPU/KernelAbstractions.jl#797). So a kernel over a 3-Dndrangedoesn't need integer divisions to compute its index anymore, which matter more on AMD GPUs than elsewhere because they have no hardware integer division. The test suite checks that for the generated LLVM IR. I have no AMD GPU to measure the effect on, so unlike #3314 there are no timings here.Some smaller changes came along:
KI.localmemory(and thus@localmem) needs its own memory, and KernelInterface doesn't pass an id to tell them apart.alloc_specialnow defines local memory with internal linkage, so that allocations don't alias because they share a name.AMDGPU.sync_wavefront()is a new barrier for the lanes of a wavefront, fenced at wavefront scope. It backsKI.sub_group_barrier.HIP.max_workgroup_dims(dev)reports the per-dimension workgroup limits, cached in the device, since KernelInterface queries them on every launch whose workgroup size it picks.shfl_downof aComplex{Int32}compiles now.KI.launchrejectsgroupsizeandgridsize, which would override the launch geometry KernelInterface has validated, but passes AMDGPU's other launch options on:KI.kernel_functionreceives the kernel's callable unconverted. It compiles the converted form, which only holds pointers to the arrays a closure captures, so the returned kernel keeps the original callable alive. Every launch converts it again for the launch's stream, like the arguments, so that the arrays it captures are handed to that stream the same way.KernelInterface 0.4 passes the kernel arguments to the back end as one tuple (JuliaGPU/KernelAbstractions.jl#811), so that kernels with many arguments stay cheap to launch. A
HIPKernelcan't be launched with a tuple yet, soKI.launchsplats it for now, and launching a kernel with more than about 32 arguments still goes through dynamic dispatch, as it does on master. #1115 adds the tuple launch; once it is in,KI.launchshould forward the tuple to it.The first two commits prepare local memory and the workgroup limits, and the third is the port. The next ones implement parts of KernelInterface that are optional:
sync_wavefrontand sub-groups (wavefronts, including a partial last wavefront and lane ids that don't change in divergent code), ordering work across tasks with HIP events forKernelAbstractions.@spawn, andKI.versioninfo.What changes for users:
ROCBackendis aKernelInterface.Backendrather than aKernelAbstractions.GPU, which no longer exists. Code that usesKernelAbstractions.get_backend,allocate,synchronizeand so on keeps working, since KernelAbstractions re-exports them from KernelInterface.@printin a kernel still does nothing on AMDGPU.I could not run any of this on AMD hardware. Locally, without a GPU, I checked that AMDGPU and its KernelAbstractions extension precompile and load on Julia 1.10, 1.11, 1.12 and 1.13, that
ROCBackendhas methods for everything KernelInterface requires, and, compiling for gfx1030 and gfx90a through GPUCompiler, that KernelAbstractions kernels using local memory,@private,@Const, atomics and@print, the sub-group queries, andshfl_downfor every typeKI.supports_shuffleclaims all compile. Whether they run correctly depends on AMDGPU's CI.This needs a breaking release, since it requires KernelAbstractions 0.10, which isn't registered yet, as well as an AcceleratedKernels release that allows it (the registered 0.4.3 caps KernelAbstractions at 0.9). Until then, the last commit takes KernelAbstractions and KernelInterface from their development branch and AcceleratedKernels from its main branch, through
[sources]. Julia 1.10 doesn't support[sources], and without workspaces Julia 1.11 only picks up KernelAbstractions from the test project's, so Buildkite runs the tests in the test project on those versions, and the Enzyme and GPU-less jobs move to Julia 1.12. That commit is dropped before merging, and the AcceleratedKernels compat bound should then require the release that supports KernelAbstractions 0.10.