Skip to content

Commit

Permalink
adding attempt to force inbounds at the kernel level
Browse files Browse the repository at this point in the history
Co-authored-by: Valentin Churavy <vchuravy@users.noreply.github.com>
  • Loading branch information
leios and vchuravy committed Nov 2, 2023
1 parent c1c6887 commit ddb25a5
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 11 deletions.
36 changes: 26 additions & 10 deletions src/KernelAbstractions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -53,28 +53,44 @@ synchronize(backend)
```
"""
macro kernel(expr)
__kernel(expr, #=generate_cpu=#true)
__kernel(expr, #=generate_cpu=#true, #=force_inbounds=#false)
end

"""
@kernel cpu=false function f(args) end
@kernel config function f(args) end
Disable code-generation of the CPU function. This relaxes semantics such that
KernelAbstractions primitives can be used in non-kernel functions.
This allows for two different configurations:
1. `cpu={true, false}`: Disables code-generation of the CPU function. This relaxes semantics such that KernelAbstractions primitives can be used in non-kernel functions.
2. `inbounds={false, true}`: Enables a forced `@inbounds` macro around the function definition in the case the user is using too many `@inbounds` already in their kernel. Note that this can lead to incorrect results, crashes, etc and is fundamentally unsafe. Be careful!
- [`@context`](@ref)
!!! warn
This is an experimental feature.
"""
macro kernel(config, expr)
if config isa Expr && config.head == :(=) &&
config.args[1] == :cpu && config.args[2] isa Bool
generate_cpu = config.args[2]
macro kernel(ex...)
if length(ex) == 1
__kernel(ex[1], true, false)
else
error("Configuration should be of form `cpu=false` got $config")
generate_cpu = true
force_inbounds = false
for i = 1:length(ex)-1
if ex[i] isa Expr && ex[i].head == :(=) &&
ex[i].args[1] == :cpu && ex[i].args[2] isa Bool
generate_cpu = ex[i].args[2]
elseif ex[i] isa Expr && ex[i].head == :(=) &&
ex[i].args[1] == :inbounds && ex[i].args[2] isa Bool
force_inbounds = ex[i].args[2]
else
error("Configuration should be of form:\n"*
"* `cpu=true`\n"*
"* `inbounds=false`\n"*
"got `", ex[i], "`")
end
end
__kernel(ex[end], generate_cpu, force_inbounds)
end
__kernel(expr, generate_cpu)
end

"""
Expand Down
8 changes: 7 additions & 1 deletion src/macros.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@ function find_return(stmt)
end

# XXX: Proper errors
function __kernel(expr, generate_cpu=true)
function __kernel(expr, generate_cpu=true, force_inbounds=false)
def = splitdef(expr)
name = def[:name]
args = def[:args]
if force_inbounds
body_qt = quote
@inbounds $(def[:body])
end
def[:body] = body_qt
end

find_return(expr) && error("Return statement not permitted in a kernel function $name")

Expand Down
16 changes: 16 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,22 @@ kern_static(CPU(static=true), (1,))(A, ndrange=length(A))
end
@test_throws ErrorException("This kernel is unavailable for backend CPU") my_no_cpu_kernel(CPU())

# testing multiple configurations at the same time
@kernel cpu=false inbounds=false function my_no_cpu_kernel2(a)
end
@test_throws ErrorException("This kernel is unavailable for backend CPU") my_no_cpu_kernel2(CPU())

if Base.JLOptions().check_bounds == 0 || Base.JLOptions().check_bounds == 1
# testing bounds errors
@kernel inbounds=false my_bounded_kernel(a) = a[1]
@test_throws BoundsError(Int64[],(1,)) my_bounded_kernel(CPU())(Int[], ndrange=1)
end

if Base.JLOptions().check_bounds == 0 || Base.JLOptions().check_bounds == 2
@kernel inbounds=true my_inbounds_kernel(a) = a[1]
@test nothing == my_inbounds_kernel(CPU())(Int[], ndrange=1)
end

struct NewBackend <: KernelAbstractions.GPU end
@testset "Default host implementation" begin
backend = NewBackend()
Expand Down

0 comments on commit ddb25a5

Please sign in to comment.