JuliaGPU / JuliaGPU/KernelAbstractions.jl
Variable scoping issue leading to unexpected UndefVarError on CPU
- Dominant language
- Julia
- Stars
- 523
- Forks
- 88
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 25
Description
I have encountered what I think is a variable scoping issue that causes one of my KernelAbstractions kernels to fail when executing on the CPU. (GPU execution is fine.) I'm using KernelAbstractions v0.9.6 in Julia 1.9.2. Here's a minimal example that triggers the problem:
```julia
using KernelAbstractions
@kernel function mykernel(x)
i = @index(Global, Linear)
_, Nblocks = @ndrange()
@inbounds begin
id = Nblocks
@synchronize
x[i] = 1.0
end
end
x = ones(256, 1)
backend = get_backend(x)
kernel! = mykernel(backend, (256,))
kernel!(x, ndrange = (256, 1))
```
When I run this code, it fails with:
```
ERROR: LoadError: UndefVarError: `Nblocks` not defined
Stacktrace:
[1] cpu_mykernel
@ ~/.julia/packages/KernelAbstractions/lhhMo/src/macros.jl:276 [inlined]
[2] cpu_mykernel(__ctx__::KernelAbstractions.CompilerMetadata{KernelAbstractions.NDIteration.DynamicSize, KernelAbstractions.NDIteration.NoDynamicCheck, CartesianIndex{2}, CartesianIndices{2, Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}}, KernelAbstractions.NDIteration.NDRange{2, KernelAbstractions.NDIteration.DynamicSize, KernelAbstractions.NDIteration.StaticSize{(256, 1)}, CartesianIndices{2, Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}}, Nothing}}, x::Matrix{Float64})
@ Main ./none:0
[3] __thread_run(tid::Int64, len::Int64, rem::Int64, obj::KernelAbstractions.Kernel{CPU, KernelAbstractions.NDIteration.StaticSize{(256,)}, KernelAbstractions.NDIteration.DynamicSize, typeof(cpu_mykernel)}, ndrange::Tuple{Int64, Int64}, iterspace::KernelAbstractions.NDIteration.NDRange{2, KernelAbstractions.NDIteration.DynamicSize, KernelAbstractions.NDIteration.StaticSize{(256, 1)}, CartesianIndices{2, Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}}, Nothing}, args::Tuple{Matrix{Float64}}, dynamic::KernelAbstractions.NDIteration.NoDynamicCheck)
@ KernelAbstractions ~/.julia/packages/KernelAbstractions/lhhMo/src/cpu.jl:115
[4] __run(obj::KernelAbstractions.Kernel{CPU, KernelAbstractions.NDIteration.StaticSize{(256,)}, KernelAbstractions.NDIteration.DynamicSize, typeof(cpu_mykernel)}, ndrange::Tuple{Int64, Int64}, iterspace::KernelAbstractions.NDIteration.NDRange{2, KernelAbstractions.NDIteration.DynamicSize, KernelAbstractions.NDIteration.StaticSize{(256, 1)}, CartesianIndices{2, Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}}, Nothing}, args::Tuple{Matrix{Float64}}, dynamic::KernelAbstractions.NDIteration.NoDynamicCheck, static_threads::Bool)
@ KernelAbstractions ~/.julia/packages/KernelAbstractions/lhhMo/src/cpu.jl:82
[5] (::KernelAbstractions.Kernel{CPU, KernelAbstractions.NDIteration.StaticSize{(256,)}, KernelAbstractions.NDIteration.DynamicSize, typeof(cpu_mykernel)})(args::Matrix{Float64}; ndrange::Tuple{Int64, Int64}, workgroupsize::Nothing)
@ KernelAbstractions ~/.julia/packages/KernelAbstractions/lhhMo/src/cpu.jl:44
[6] top-level scope
@ ~/debug.jl:19
[7] include(fname::String)
@ Base.MainInclude ./client.jl:478
[8] top-level scope
@ REPL[1]:1
```
The compiler thinks that the variable `Nblocks` in the `id = Nblocks` line is not defined, even though it clearly is defined via the call to `@ndrange`. When I inspect the generated kernel code with `code_lowered()`, I see:
```
julia> code_lowered(kernel!.f)
1-element Vector{Core.CodeInfo}:
CodeInfo(
[...]
5 ── i@_14 = KernelAbstractions.__index_Global_Linear(__ctx__, I#301)
│ %25 = (KernelAbstractions.ndrange)(__ctx__)
│ %26 = (size)(%25)
│ %27 = Base.indexed_iterate(%26, 1)
│ Core.getfield(%27, 1)
│ @_11 = Core.getfield(%27, 2)
│ %30 = Base.indexed_iterate(%26, 2, @_11)
└─── Nblocks = Core.getfield(%30, 1)
[...]
12 ─ i@_17 = KernelAbstractions.__index_Global_Linear(__ctx__, I#303)
└─── id = Main.Nblocks
[...]
)
```
The code in block 5 shows that `Nblocks` is getting set OK, but the code in block 12 shows that when the `id = Nblocks` line gets translated, the compiler looks for a definition of `Nblocks` in the `Main` module, where it does not exist. (I redacted this listing for readability. I'm happy to provide the full listing if that would be helpful.)
The issue disappears if I remove the call to `@synchronize`.
Any thoughts here?
EDIT: This is probably related to (maybe even a duplicate of) #274. Also, another way I can get the issue to disappear is to move the call to `@ndrange` that defines `Nblocks` inside the `@inbounds begin ... end` block.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with the minimal Julia reproducer and inspect the generated code around src/macros.jl:276, especially the interaction between @ndrange, @inbounds, and @synchronize. Compare the CPU path at src/cpu.jl:115 and related issue #274; done means the reproducer runs on the CPU without UndefVarError when @ndrange remains outside the @inbounds block.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- julia
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100