Repository navigation
Conversation
maleadt
force-pushed
the
vc/ki-subgroup-ops
branch
from
October 10, 2026 17:36
8e0e202 to
c507ee3
Compare
maleadt
added this pull request to stack #1154
October 10, 2026 17:45
`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, and passes it on to the `HIPKernel` without splatting it. 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>
KernelAbstractions 0.10 and KernelInterface 0.4 aren't registered yet. 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 job moves to Julia 1.12. Drop this commit once they are registered.
KernelAbstractions now backs @Private with a PrivateArray over GPUCompiler.alloca, defined as an overlay in GPUCompiler's shared method table, which GPUCompiler stacks under AMDGPU's own method table. Drop our MArray-based Scratchpad override, which would otherwise take precedence, along with the StaticArraysCore dependency. The shared table needs GPUCompiler 2.12, which we already require.
Co-authored-by: Tim Besard <tim@juliahub.com>
Implement `shfl`, `shfl_down`, `shfl_up` and `shfl_xor` with `ds_bpermute` on the hardware lane, for the primitive types it supports by decomposing them into 32-bit words; other types use KernelInterface's fallbacks. Past the wavefront, `shfl_down` and `shfl_up` return the work-item's own value, as on CUDA, comparing the offset before narrowing it. The votes use the wavefront ballot, which only has bits for the work-items of the wavefront. Wavefronts are formed from consecutive linear work-item indices and execute independently, so report both `supports_linear_subgroups` and `supports_independent_subgroups`. Co-authored-by: Tim Besard <tim@juliahub.com>
The device overrides of `Base.min` and `Base.max` for floats called OCML's `__ocml_min`/`__ocml_max`, which, like C's `fmin`/`fmax`, return the other argument for a NaN, while Julia's `min` and `max` return NaN. Drop them: Julia's own definitions (`llvm.minimum`/`llvm.maximum` on Julia 1.12+, arithmetic before) compile for AMDGPU. Assisted-by: Claude Code (Opus 5.5) Co-authored-by: Tim Besard <tim@juliahub.com>
maleadt
force-pushed
the
vc/ki-subgroup-ops
branch
from
October 10, 2026 18:45
c507ee3 to
c3d2f0d
Compare
Report `KI.supports_partitionable_subgroups`: the functions of KernelInterface with a `width` only need the lanes of the caller's segment on AMD GPUs: `ds_bpermute` only involves the lanes it reads from, `llvm.amdgcn.ballot` only reports the lanes that execute it, and the sub-group barrier is wavefront-scope fences around `llvm.amdgcn.wave.barrier`, which HIP's tile synchronization also uses in divergent code. KernelInterface's fallbacks for the width variants build on those, so KernelAbstractions' tiles can now pack several narrow tiles into a wavefront.
maleadt
force-pushed
the
vc/ki-subgroup-ops
branch
from
October 12, 2026 09:08
6474af8 to
b49da45
Compare
An error occurred while trying to automatically change base from
ka-0.10
to
main
October 13, 2026 09:03
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.
Implements the sub-group contract of KernelInterface from JuliaGPU/KernelAbstractions.jl#831 for
ROCBackend: the shufflesshfl,shfl_down,shfl_upandshfl_xor, the votessub_group_any,sub_group_allandsub_group_ballot, and aget_max_sub_group_sizethat is a constant of the generated code.What's implemented
shfl(val, lane)ds_bpermute(Device.bpermute) from hardware lanelane - 1shfl_down(val, offset)ds_bpermutefrom hardware lanembcnt + offset, ormbcntpast the wavefrontshfl_up(val, offset)ds_bpermutefrom hardware lanembcnt - offset, ormbcntif that is negativeshfl_xor(val, mask)ds_bpermutefrom hardware lanembcnt ⊻ masksub_group_ballot(pred)Device.ballot(pred)(llvm.amdgcn.ballot.i32/.i64, widened toUInt64)sub_group_any(pred)Device.ballot(pred) != 0sub_group_all(pred)Device.ballot(!pred) == 0get_max_sub_group_size()Device.wavefrontsize(), folded to a constant (unchanged)mbcnt), likeget_sub_group_local_id, notactivelane. They don't useDevice.shfl*, which clamp out-of-range offsets to the own lane and work onactivelane.ds_bpermuteonly uses the low bits of the address, so a lane out of range reads an unspecified value rather than trapping. Lanes and offsets are truncated with% Cint, so there is no checked conversion: the IR of a kernel using all shuffles and votes with runtimeIntlanes/offsets has nothrow/trap/unreachable.supports_shuffleare now only defined forconst ShuffleTypes = Union{Bool, Base.BitInteger, Base.IEEEFloat}(whatDevice._shfldecomposes into 32-bitbpermutes). The previous genericwhere {T}shfl_downoverride and thesupports_shufflethat listedComplexare gone, soComplexand otherisbitsstructs/tuples go through KernelInterface's fallback, as packedUInt32words.shfl_down/shfl_upreturn the work-item's own value where the source lane is past the wavefront (as KernelInterface now requires, like CUDA): one compare and select against the constant wavefront size before theds_bpermute.shfl_xorneeds no change, as KernelInterface requiresmaskbelow the width.width(KI.shfl(val, lane, width)etc.) use KernelInterface's fallbacks: oneds_bpermutefrom a lane computed with a few integer operations, the same asDevice.shfletc. DPP/ds_swizzlecould be cheaper for constant offsets, but isn't used here.KI.supports_linear_subgroupsandKI.supports_independent_subgroupsaretrue: wavefronts are formed from consecutive linear work-item ids, x fastest (KernelInterface's layout tests check this), and are independent. KernelInterface no longer implies either fromsupports_subgroups.ballotsetting only the bits of active lanes, i.e. of the work-items of the (possibly partial) sub-group. The result is uniform.KI.supports_partitionable_subgroupsistrue: the functions with awidthonly need the lanes of the caller's segment.ds_bpermuteonly involves the lanes it reads from,llvm.amdgcn.ballotonly reports the lanes that execute it, and the sub-group barrier is wavefront-scope fences aroundllvm.amdgcn.wave.barrier, which HIP's tile synchronization also uses in divergent code. KernelAbstractions can then pack several narrow tiles into a wavefront.Constant width
This needed no new code.
KI.kernel_functioncompiles for the device's wavefront size (and rejects a conflictingwavefrontsize64), andfold_wavefrontsize!infinish_module!already replacesllvm.amdgcn.wavefrontsizewith the compiled-for size before optimization. I checked this with@device_code_llvmon a kernel that storesKI.get_max_sub_group_size(): it compiles tostore i64 32withwavefrontsize64=falseandstore i64 64withwavefrontsize64=true, and the call tollvm.amdgcn.wavefrontsizeis gone. I added a comment saying so.[TEMP]commit[TEMP] Test against KernelAbstractions' vc/ki-subgroup-ops branchpoints KernelAbstractions and KernelInterface atvc/ki-subgroup-ops. SPIRVIntrinsics 1.4 is registered, so the earlier pin to OpenCL.jl'svc/subgroup-votesis gone. Drop the commit once KernelAbstractions.jl#831 is merged.Local testing
gfx1036 iGPU (wave32), Julia 1.12, on
ka-0.10rebased ontomain, KernelAbstractions at the current #831: KernelInterface testsuite 6089/6089, KernelAbstractions testsuite 2639 passed, 4 broken, in three runs without hangs. Offsets up totypemax(Int64)give the own value past the wavefront.