Repository navigation
Fill GPUCompiler's SPIR-V atomics descriptor from the device - #668
Merged
Merged
Conversation
Contributor
|
Your PR requires formatting changes to meet the project's style guidelines. Click here to view the suggested changes.diff --git a/lib/level-zero/device.jl b/lib/level-zero/device.jl
index 8b1e9f7..b14b8a8 100644
--- a/lib/level-zero/device.jl
+++ b/lib/level-zero/device.jl
@@ -119,9 +119,9 @@ function float_atomic_properties(dev::ZeDevice)
props = atomic_props_ref[]
return (
- fp16flags=props.fp16Flags,
- fp32flags=props.fp32Flags,
- fp64flags=props.fp64Flags,
+ fp16flags = props.fp16Flags,
+ fp32flags = props.fp32Flags,
+ fp64flags = props.fp64Flags,
)
end
diff --git a/src/compiler/compilation.jl b/src/compiler/compilation.jl
index 33b5537..f5d8418 100644
--- a/src/compiler/compilation.jl
+++ b/src/compiler/compilation.jl
@@ -268,7 +268,7 @@ end
# implemented with compare-and-swap loops.
function _atomics_capabilities(dev, properties; supports_fp16, supports_fp64)
int64 = properties.flags & oneL0.ZE_DEVICE_MODULE_FLAG_INT64_ATOMICS ==
- oneL0.ZE_DEVICE_MODULE_FLAG_INT64_ATOMICS
+ oneL0.ZE_DEVICE_MODULE_FLAG_INT64_ATOMICS
fp_atomics = oneL0.float_atomic_properties(dev)
fp_atomics === nothing && return SPIRVAtomics(; int64)
@@ -282,11 +282,11 @@ function _atomics_capabilities(dev, properties; supports_fp16, supports_fp64)
return SPIRVAtomics(;
int64,
fadd_f16_global = supports_fp16 && has_add(fp_atomics.fp16flags, global_add),
- fadd_f16_local = supports_fp16 && has_add(fp_atomics.fp16flags, local_add),
+ fadd_f16_local = supports_fp16 && has_add(fp_atomics.fp16flags, local_add),
fadd_f32_global = has_add(fp_atomics.fp32flags, global_add),
- fadd_f32_local = has_add(fp_atomics.fp32flags, local_add),
+ fadd_f32_local = has_add(fp_atomics.fp32flags, local_add),
fadd_f64_global = supports_fp64 && has_add(fp_atomics.fp64flags, global_add),
- fadd_f64_local = supports_fp64 && has_add(fp_atomics.fp64flags, local_add),
+ fadd_f64_local = supports_fp64 && has_add(fp_atomics.fp64flags, local_add),
)
end
@@ -326,8 +326,9 @@ end
atomics = _atomics_capabilities(dev, properties; supports_fp16, supports_fp64)
# create GPUCompiler objects
- target = SPIRVCompilerTarget(; backend, extensions = extensions_str, atomics,
- supports_fp16, supports_fp64, supports_bfloat16,
+ target = SPIRVCompilerTarget(;
+ backend, extensions = extensions_str, atomics,
+ supports_fp16, supports_fp64, supports_bfloat16,
driver = :intel, kwargs...)
params = oneAPICompilerParams()
CompilerConfig(target, params; kernel, name, always_inline)
diff --git a/test/device/intrinsics.jl b/test/device/intrinsics.jl
index 6130a87..ecf4436 100644
--- a/test/device/intrinsics.jl
+++ b/test/device/intrinsics.jl
@@ -278,7 +278,7 @@ end
# @testset "atomics (low level)" begin
- @testset "atomic_add($T)" for T in [Int32, UInt32, Float32]
+@testset "atomic_add($T)" for T in [Int32, UInt32, Float32]
a = oneArray([zero(T)])
function kernel(a, b)
@@ -290,7 +290,7 @@ end
@test Array(a)[1] == T(256)
end
- @testset "atomic_sub($T)" for T in [Int32, UInt32, Float32]
+@testset "atomic_sub($T)" for T in [Int32, UInt32, Float32]
a = oneArray([T(256)])
function kernel(a, b)
@@ -326,7 +326,7 @@ end
@test Array(a)[1] == T(0)
end
- @testset "atomic_min($T)" for T in [Int32, UInt32, Float32]
+@testset "atomic_min($T)" for T in [Int32, UInt32, Float32]
a = oneArray([T(256)])
function kernel(a, T)
@@ -339,7 +339,7 @@ end
@test Array(a)[1] == one(T)
end
- @testset "atomic_max($T)" for T in [Int32, UInt32, Float32]
+@testset "atomic_max($T)" for T in [Int32, UInt32, Float32]
a = oneArray([zero(T)])
function kernel(a, T)
@@ -406,7 +406,7 @@ end
@test Array(a)[1] == zero(T)
end
- @testset "atomic_xchg($T)" for T in [Int32, UInt32, Float32]
+@testset "atomic_xchg($T)" for T in [Int32, UInt32, Float32]
a = oneArray([zero(T)])
function kernel(a, b)
@@ -428,20 +428,20 @@ end
a = oneArray(Float32[0])
tt = Tuple{typeof(oneAPI.kernel_convert(a)), Float32}
- spirv = sprint(io -> oneAPI.code_spirv(io, kernel, tt; kernel=true))
+ spirv = sprint(io -> oneAPI.code_spirv(io, kernel, tt; kernel = true))
fp_atomics = oneL0.float_atomic_properties(device())
if fp_atomics !== nothing &&
- fp_atomics.fp32flags & oneL0.ZE_DEVICE_FP_ATOMIC_EXT_FLAG_GLOBAL_ADD != 0
+ fp_atomics.fp32flags & oneL0.ZE_DEVICE_FP_ATOMIC_EXT_FLAG_GLOBAL_ADD != 0
@test occursin("OpAtomicFAddEXT", spirv)
end
# without the capability, a compare-and-swap loop is used
atomics = oneAPI.SPIRVAtomics()
- spirv = sprint(io -> oneAPI.code_spirv(io, kernel, tt; kernel=true, atomics))
+ spirv = sprint(io -> oneAPI.code_spirv(io, kernel, tt; kernel = true, atomics))
@test !occursin("OpAtomicFAddEXT", spirv)
@test occursin("OpAtomicCompareExchange", spirv)
- @oneapi items=256 atomics=atomics kernel(a, 1f0)
+ @oneapi items = 256 atomics = atomics kernel(a, 1.0f0)
@test Array(a)[1] == 256
end
@@ -450,8 +450,10 @@ end
# and the last element of an odd-sized array needs the allocation to be padded
function kernel(a, x)
i = (get_global_id() - 1) % length(a) + 1
- UnsafeAtomics.modify!(pointer(a, i), +, x, UnsafeAtomics.monotonic,
- UnsafeAtomics.device)
+ UnsafeAtomics.modify!(
+ pointer(a, i), +, x, UnsafeAtomics.monotonic,
+ UnsafeAtomics.device
+ )
return
end
@@ -463,7 +465,7 @@ end
@test sizeof(a.data[]) % 4 == 0
# 1020 work-items, so that every element is incremented as often
- @oneapi items=204 groups=5 kernel(a, one(T))
+ @oneapi items = 204 groups = 5 kernel(a, one(T))
k = 1020 ÷ n
@test Array(a) == fill(T <: Integer ? k % T : T(k), n)
end
diff --git a/test/level-zero.jl b/test/level-zero.jl
index c210021..1f2bdfd 100644
--- a/test/level-zero.jl
+++ b/test/level-zero.jl
@@ -47,7 +47,7 @@ show(devnull, MIME("text/plain"), dev)
properties(dev)
compute_properties(dev)
module_properties(dev)
-float_atomic_properties(dev)
+ float_atomic_properties(dev)
memory_properties(dev)
memory_access_properties(dev)
cache_properties(dev) |
GPUCompiler 2.13 legalizes atomics for SPIR-V itself and selects native floating-point additions only where the target's SPIRVAtomics descriptor allows them, declaring the extensions they need. Fill that descriptor from ZE_extension_float_atomics, per precision and address space, ignoring half- and double-precision flags on devices without that arithmetic (Xe-LP reports double-precision atomics without supporting Float64), and take 64-bit integer atomics from the module properties. This replaces declaring SPV_EXT_shader_atomic_float_add for every device. SPIRVIntrinsics 1.4 is required because older versions emit the float atomic builtins directly, which would now lack that extension.
GPUCompiler implements 8- and 16-bit atomics as 32-bit atomics on the containing aligned word, which requires that word to be accessible. Round the size of device, shared and host allocations up to a multiple of 4 bytes and align them to at least 4 bytes; array dimensions are unaffected. Document that memory not allocated by oneAPI.jl has to satisfy the same requirement, and test atomics on odd-sized Int8, UInt16 and Float16 arrays.
The low-level Float32 atomic tests were skipped on integrated GPUs. With GPUCompiler 2.13 handling these atomics they pass on an Iris Xe (Xe-LP), with both the LLVM back-end and the translator.
Allow overriding the device-derived SPIRVAtomics descriptor with `@oneapi atomics=...`, `zefunction` and the code reflection functions, e.g. to force compare-and-swap loops or to enable an operation the device doesn't report. Document that enabling unsupported operations can fail compilation or terminate the driver's compiler.
…yword. Half-precision additions can be native rather than a compare-and-swap loop on the containing word, plain stores to other elements of that word race with the atomic operation, and disabling 64-bit integer atomics rejects 64-bit operations rather than falling back to compare-and-swap.
maleadt
force-pushed
the
tb/atomics-descriptor
branch
from
October 7, 2026 14:02
122bcb3 to
333853b
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #668 +/- ##
==========================================
+ Coverage 79.00% 79.08% +0.07%
==========================================
Files 56 56
Lines 4063 4088 +25
==========================================
+ Hits 3210 3233 +23
- Misses 853 855 +2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
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.
GPUCompiler 2.13 lowers atomics for SPIR-V itself and only selects floating-point addition instructions where
SPIRVCompilerTarget.atomicsallows them, adding the SPIR-V extensions they need. This fills that descriptor fromZE_extension_float_atomics(per precision, global and local memory), ignoring half- and double-precision bits when the device lacks that arithmetic (an Iris Xe reports fp64 atomics without fp64 support). 64-bit integer atomics come fromZE_DEVICE_MODULE_FLAG_INT64_ATOMICS. Without the extension, every float add uses compare-and-swap.SPV_EXT_shader_atomic_float_addis no longer declared for every device. Requires GPUCompiler 2.13 and SPIRVIntrinsics 1.4.The descriptor can be overridden with the new
atomicscompiler keyword (@oneapi atomics=oneAPI.SPIRVAtomics(...),zefunction, code reflection), which replaces the device-derived one. Enabling operations the device or toolchain doesn't support can fail compilation or terminate the process: on Xe-LP, IGC rejects a half-precision atomic add and exits.GPUCompiler implements 8- and 16-bit atomics on the containing aligned 32-bit word. So that word is always part of the allocation, device, shared and host allocations are now padded to a multiple of 4 bytes and aligned to at least 4 bytes; array dimensions are unchanged. The docs state the requirements for memory oneAPI.jl didn't allocate, and that adjacent elements must not be modified with plain stores while such an atomic may be in flight.
Also un-skips the low-level Float32 atomic tests on integrated GPUs, and adds tests for the
atomicskeyword and for odd-sized Int8/UInt16/Float16 arrays under neighbour contention.Tested on an Intel Iris Xe (Xe-LP) only, NEO 26.18.38308, Julia 1.12.7, GPUCompiler 2.13.0 and a local SPIRVIntrinsics 1.4.0, with both the LLVM SPIR-V back-end and
ONEAPI_LTS=1(translator):device/intrinsics,kernelabstractionsandlevel-zeropass (device/intrinsicsagain after the keyword change). IGC dumps showOpAtomicFAddEXTfor Float32 add per the descriptor (Xe-LP itself compiles it to a compare-and-swap loop) and a 32-bit compare-and-swap for Float16. Not tested on Arc, Data Center GPU Max, or the actual LTS driver stack.CI can't resolve until SPIRVIntrinsics 1.4.0 is registered (JuliaGPU/OpenCL.jl#542).