Skip to content

Commit

Permalink
Make the native target extendable, and use it to cover error tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
maleadt committed Apr 27, 2020
1 parent 1efe11f commit bcdbe8a
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 286 deletions.
11 changes: 2 additions & 9 deletions src/native.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,13 @@ function llvm_machine(target::NativeCompilerTarget)
return tm
end

module NativeRuntime
malloc(sz) = return
report_oom(sz) = return
end

runtime_module(target::NativeCompilerTarget) = NativeRuntime


## job

export NativeCompilerJob

Base.@kwdef struct NativeCompilerJob <: AbstractCompilerJob
target::NativeCompilerTarget
target::AbstractCompilerTarget
source::FunctionSpec
end

Expand All @@ -50,6 +43,6 @@ function Base.show(io::IO, job::NativeCompilerJob)
print(io, " for $(target(job).cpu) $(target(job).features)")
end

runtime_slug(job::NativeCompilerJob) = "native_$(target(job).cpu)-$(hash(target(job).features))"
runtime_slug(job::NativeCompilerJob) = "native_$(Base.parent(target(job)).cpu)-$(hash(Base.parent(target(job)).features))"

add_lowering_passes!(::NativeCompilerJob, pm::LLVM.PassManager) = return
137 changes: 0 additions & 137 deletions test/gcn.jl
Original file line number Diff line number Diff line change
Expand Up @@ -232,143 +232,6 @@ end

end


############################################################################################

@testset "errors" begin

# some validation happens in the emit_function hook, which is called by gcn_code_llvm

@testset "base intrinsics" begin
foobar(i) = sin(i)

# NOTE: we don't use test_logs in order to test all of the warning (exception, backtrace)
logs, _ = Test.collect_test_logs(min_level=Info) do
withenv("JULIA_DEBUG" => nothing) do
gcn_code_llvm(devnull, foobar, Tuple{Int})
end
end
@test length(logs) == 1
record = logs[1]
@test record.level == Base.CoreLogging.Warn
@test record.message == "calls to Base intrinsics might be GPU incompatible"
@test haskey(record.kwargs, :exception)
err,bt = record.kwargs[:exception]
err_msg = sprint(showerror, err)
@test occursin(Regex("You called sin(.+) in Base.Math .+, maybe you intended to call sin(.+) in $TestRuntime .+ instead?"), err_msg)
bt_msg = sprint(Base.show_backtrace, bt)
@test occursin("[1] sin", bt_msg)
@test occursin(r"\[2\] .+foobar", bt_msg)
end

# some validation happens in `compile`

@eval Main begin
struct CleverType{T}
x::T
end
Base.unsafe_trunc(::Type{Int}, x::CleverType) = unsafe_trunc(Int, x.x)
end

@testset "non-isbits arguments" begin
foobar(i) = (sink(unsafe_trunc(Int,i)); return)

@test_throws_message(KernelError,
gcn_code_llvm(foobar, Tuple{BigInt}; strict=true)) do msg
occursin("passing and using non-bitstype argument", msg) &&
occursin("BigInt", msg)
end

# test that we can handle abstract types
@test_throws_message(KernelError,
gcn_code_llvm(foobar, Tuple{Any}; strict=true)) do msg
occursin("passing and using non-bitstype argument", msg) &&
occursin("Any", msg)
end

@test_throws_message(KernelError,
gcn_code_llvm(foobar, Tuple{Union{Int32, Int64}}; strict=true)) do msg
occursin("passing and using non-bitstype argument", msg) &&
occursin("Union{Int32, Int64}", msg)
end

@test_throws_message(KernelError,
gcn_code_llvm(foobar, Tuple{Union{Int32, Int64}}; strict=true)) do msg
occursin("passing and using non-bitstype argument", msg) &&
occursin("Union{Int32, Int64}", msg)
end

# test that we get information about fields and reason why something is not isbits
@test_throws_message(KernelError,
gcn_code_llvm(foobar, Tuple{CleverType{BigInt}}; strict=true)) do msg
occursin("passing and using non-bitstype argument", msg) &&
occursin("CleverType", msg) &&
occursin("BigInt", msg)
end
end

@testset "invalid LLVM IR" begin
foobar(i) = println(i)

@test_throws_message(InvalidIRError,
gcn_code_llvm(foobar, Tuple{Int}; strict=true)) do msg
occursin("invalid LLVM IR", msg) &&
occursin(GPUCompiler.RUNTIME_FUNCTION, msg) &&
occursin("[1] println", msg) &&
occursin(r"\[2\] .+foobar", msg)
end
end

@testset "invalid LLVM IR (ccall)" begin
foobar(p) = (unsafe_store!(p, ccall(:time, Cint, ())); nothing)

@test_throws_message(InvalidIRError,
gcn_code_llvm(foobar, Tuple{Ptr{Int}}; strict=true)) do msg
occursin("invalid LLVM IR", msg) &&
occursin(GPUCompiler.POINTER_FUNCTION, msg) &&
occursin(r"\[1\] .+foobar", msg)
end
end

@testset "delayed bindings" begin
kernel() = (undefined; return)

@test_throws_message(InvalidIRError,
gcn_code_llvm(kernel, Tuple{}; strict=true)) do msg
occursin("invalid LLVM IR", msg) &&
occursin(GPUCompiler.DELAYED_BINDING, msg) &&
occursin("use of 'undefined'", msg) &&
occursin(r"\[1\] .+kernel", msg)
end
end

@testset "dynamic call (invoke)" begin
@eval @noinline nospecialize_child(@nospecialize(i)) = i
kernel(a, b) = (unsafe_store!(b, nospecialize_child(a)); return)

@test_throws_message(InvalidIRError,
gcn_code_llvm(kernel, Tuple{Int,Ptr{Int}}; strict=true)) do msg
occursin("invalid LLVM IR", msg) &&
occursin(GPUCompiler.DYNAMIC_CALL, msg) &&
occursin("call to nospecialize_child", msg) &&
occursin(r"\[1\] .+kernel", msg)
end
end

@testset "dynamic call (apply)" begin
func() = pointer(1)

@test_throws_message(InvalidIRError,
gcn_code_llvm(func, Tuple{}; strict=true)) do msg
occursin("invalid LLVM IR", msg) &&
occursin(GPUCompiler.DYNAMIC_CALL, msg) &&
occursin("call to pointer", msg) &&
occursin("[1] func", msg)
end
end

end

############################################################################################

end
167 changes: 163 additions & 4 deletions test/native.jl
Original file line number Diff line number Diff line change
@@ -1,17 +1,40 @@
@testset "native" begin

# create a native test compiler, and generate reflection methods for it

struct NativeTestCompilerTarget <: CompositeCompilerTarget
parent::NativeCompilerTarget

NativeTestCompilerTarget() = new(NativeCompilerTarget())
end

Base.parent(target::NativeTestCompilerTarget) = target.parent

struct NativeTestCompilerJob <: CompositeCompilerJob
parent::AbstractCompilerJob
end

GPUCompiler.runtime_module(target::NativeTestCompilerTarget) = TestRuntime

NativeTestCompilerJob(target::AbstractCompilerTarget, source::FunctionSpec; kwargs...) =
NativeTestCompilerJob(NativeCompilerJob(target, source; kwargs...))

Base.similar(job::NativeTestCompilerJob, source::FunctionSpec; kwargs...) =
NativeTestCompilerJob(similar(job.parent, source; kwargs...))

Base.parent(job::NativeTestCompilerJob) = job.parent

for method in (:code_typed, :code_warntype, :code_llvm, :code_native)
# only code_typed doesn't take a io argument
args = method == :code_typed ? (:job,) : (:io, :job)
native_method = Symbol("native_$(method)")

@eval begin
function $native_method(io::IO, @nospecialize(func), @nospecialize(types);
kernel::Bool=false, minthreads=nothing, maxthreads=nothing,
blocks_per_sm=nothing, maxregs=nothing, kwargs...)
kernel::Bool=false, kwargs...)
source = FunctionSpec(func, Base.to_tuple_type(types), kernel)
target = NativeCompilerTarget()
job = NativeCompilerJob(target=target, source=source)
target = NativeTestCompilerTarget()
job = NativeTestCompilerJob(target, source)
GPUCompiler.$method($(args...); kwargs...)
end
$native_method(@nospecialize(func), @nospecialize(types); kwargs...) =
Expand Down Expand Up @@ -145,4 +168,140 @@ end

############################################################################################

@testset "errors" begin

# some validation happens in the emit_function hook, which is called by code_llvm

@testset "base intrinsics" begin
foobar(i) = sin(i)

# NOTE: we don't use test_logs in order to test all of the warning (exception, backtrace)
logs, _ = Test.collect_test_logs(min_level=Info) do
withenv("JULIA_DEBUG" => nothing) do
native_code_llvm(devnull, foobar, Tuple{Int})
end
end
@test length(logs) == 1
record = logs[1]
@test record.level == Base.CoreLogging.Warn
@test record.message == "calls to Base intrinsics might be GPU incompatible"
@test haskey(record.kwargs, :exception)
err,bt = record.kwargs[:exception]
err_msg = sprint(showerror, err)
@test occursin(Regex("You called sin(.+) in Base.Math .+, maybe you intended to call sin(.+) in $TestRuntime .+ instead?"), err_msg)
bt_msg = sprint(Base.show_backtrace, bt)
@test occursin("[1] sin", bt_msg)
@test occursin(r"\[2\] .+foobar", bt_msg)
end

# some validation happens in `compile`

@eval Main begin
struct CleverType{T}
x::T
end
Base.unsafe_trunc(::Type{Int}, x::CleverType) = unsafe_trunc(Int, x.x)
end

@testset "non-isbits arguments" begin
foobar(i) = (sink(unsafe_trunc(Int,i)); return)

@test_throws_message(KernelError,
native_code_llvm(foobar, Tuple{BigInt}; strict=true)) do msg
occursin("passing and using non-bitstype argument", msg) &&
occursin("BigInt", msg)
end

# test that we can handle abstract types
@test_throws_message(KernelError,
native_code_llvm(foobar, Tuple{Any}; strict=true)) do msg
occursin("passing and using non-bitstype argument", msg) &&
occursin("Any", msg)
end

@test_throws_message(KernelError,
native_code_llvm(foobar, Tuple{Union{Int32, Int64}}; strict=true)) do msg
occursin("passing and using non-bitstype argument", msg) &&
occursin("Union{Int32, Int64}", msg)
end

@test_throws_message(KernelError,
native_code_llvm(foobar, Tuple{Union{Int32, Int64}}; strict=true)) do msg
occursin("passing and using non-bitstype argument", msg) &&
occursin("Union{Int32, Int64}", msg)
end

# test that we get information about fields and reason why something is not isbits
@test_throws_message(KernelError,
native_code_llvm(foobar, Tuple{CleverType{BigInt}}; strict=true)) do msg
occursin("passing and using non-bitstype argument", msg) &&
occursin("CleverType", msg) &&
occursin("BigInt", msg)
end
end

@testset "invalid LLVM IR" begin
foobar(i) = println(i)

@test_throws_message(InvalidIRError,
native_code_llvm(foobar, Tuple{Int}; strict=true)) do msg
occursin("invalid LLVM IR", msg) &&
occursin(GPUCompiler.RUNTIME_FUNCTION, msg) &&
occursin("[1] println", msg) &&
occursin(r"\[2\] .+foobar", msg)
end
end

@testset "invalid LLVM IR (ccall)" begin
foobar(p) = (unsafe_store!(p, ccall(:time, Cint, ())); nothing)

@test_throws_message(InvalidIRError,
native_code_llvm(foobar, Tuple{Ptr{Int}}; strict=true)) do msg
occursin("invalid LLVM IR", msg) &&
occursin(GPUCompiler.POINTER_FUNCTION, msg) &&
occursin(r"\[1\] .+foobar", msg)
end
end

@testset "delayed bindings" begin
kernel() = (undefined; return)

@test_throws_message(InvalidIRError,
native_code_llvm(kernel, Tuple{}; strict=true)) do msg
occursin("invalid LLVM IR", msg) &&
occursin(GPUCompiler.DELAYED_BINDING, msg) &&
occursin("use of 'undefined'", msg) &&
occursin(r"\[1\] .+kernel", msg)
end
end

@testset "dynamic call (invoke)" begin
@eval @noinline nospecialize_child(@nospecialize(i)) = i
kernel(a, b) = (unsafe_store!(b, nospecialize_child(a)); return)

@test_throws_message(InvalidIRError,
native_code_llvm(kernel, Tuple{Int,Ptr{Int}}; strict=true)) do msg
occursin("invalid LLVM IR", msg) &&
occursin(GPUCompiler.DYNAMIC_CALL, msg) &&
occursin("call to nospecialize_child", msg) &&
occursin(r"\[1\] .+kernel", msg)
end
end

@testset "dynamic call (apply)" begin
func() = pointer(1)

@test_throws_message(InvalidIRError,
native_code_llvm(func, Tuple{}; strict=true)) do msg
occursin("invalid LLVM IR", msg) &&
occursin(GPUCompiler.DYNAMIC_CALL, msg) &&
occursin("call to pointer", msg) &&
occursin("[1] func", msg)
end
end

end

############################################################################################

end
Loading

0 comments on commit bcdbe8a

Please sign in to comment.