JuliaGPU / JuliaGPU/KernelAbstractions.jl

Variable scoping issue leading to unexpected UndefVarError on CPU

Open
#413 6 comments 0 reactions 0 assignees View on GitHub
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.