Skip to content

Commit 7e4bbe4

Browse files
authored
Errors are fine! (#97)
Hooray!
1 parent 25f6123 commit 7e4bbe4

5 files changed

Lines changed: 147 additions & 46 deletions

File tree

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ GPUCompiler = "61eb1bfa-7361-4325-ad38-22787b887f55"
99
LLVM = "929cbde3-209d-540e-8aea-75f648917ca0"
1010
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
1111
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
12+
StaticTools = "86c06d3c-3f03-46de-9781-57580aa96d0a"
1213

1314
[compat]
1415
GPUCompiler = "0.16, 0.17"
1516
LLVM = "4.8"
17+
StaticTools ="0.8"
1618
julia = "1.7"

src/StaticCompiler.jl

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ using Libdl: Libdl, dlsym, dlopen
88
using Base: RefValue
99
using Serialization: serialize, deserialize
1010
using Clang_jll: clang
11+
using StaticTools
12+
using StaticTools: @symbolcall, @c_str, println
1113

1214
export compile, load_function, compile_shlib, compile_executable
1315
export native_code_llvm, native_code_typed, native_llvm_module, native_code_native
@@ -16,6 +18,7 @@ include("target.jl")
1618
include("pointer_patching.jl")
1719
include("code_loading.jl")
1820
include("optimize.jl")
21+
include("quirks.jl")
1922

2023

2124
"""
@@ -95,7 +98,7 @@ function compile(f, _tt, path::String = tempname(); name = GPUCompiler.safe_nam
9598
isconcretetype(rt) || error("$f on $_tt did not infer to a concrete type. Got $rt")
9699

97100
f_wrap!(out::Ref, args::Ref{<:Tuple}) = (out[] = f(args[]...); nothing)
98-
_, _, table = generate_obj(f_wrap!, Tuple{RefValue{rt}, RefValue{tt}}, path, name; opt_level, strip_llvm, strip_asm, filename, kwargs...)
101+
_, _, table = generate_obj(f_wrap!, Tuple{RefValue{rt}, RefValue{tt}}, false, path, name; opt_level, strip_llvm, strip_asm, filename, kwargs...)
99102

100103
lf = LazyStaticCompiledFunction{rt, tt}(Symbol(f), path, name, filename, table)
101104
cjl_path = joinpath(path, "$filename.cjl")
@@ -131,15 +134,15 @@ shell> tree \$path
131134
0 directories, 1 file
132135
```
133136
"""
134-
function generate_obj(f, tt, path::String = tempname(), name = GPUCompiler.safe_name(repr(f)), filenamebase::String="obj";
137+
function generate_obj(f, tt, external = true, path::String = tempname(), name = GPUCompiler.safe_name(repr(f)), filenamebase::String="obj";
135138
strip_llvm = false,
136139
strip_asm = true,
137140
opt_level=3,
138141
kwargs...)
139142
mkpath(path)
140143
obj_path = joinpath(path, "$filenamebase.o")
141-
tm = GPUCompiler.llvm_machine(NativeCompilerTarget())
142-
job, kwargs = native_job(f, tt; name, kwargs...)
144+
tm = GPUCompiler.llvm_machine(external ? ExternalNativeCompilerTarget() : NativeCompilerTarget())
145+
job, kwargs = native_job(f, tt, external; name, kwargs...)
143146
#Get LLVM to generated a module of code for us. We don't want GPUCompiler's optimization passes.
144147
mod, meta = GPUCompiler.JuliaContext() do context
145148
GPUCompiler.codegen(:llvm, job; strip=strip_llvm, only_entry=false, validate=false, optimize=false, ctx=context)
@@ -267,7 +270,7 @@ function compile_shlib(f, types=(), path::String="./", name=GPUCompiler.safe_nam
267270

268271
# Would be nice to use a compiler pass or something to check if there are any heap allocations or references to globals
269272
# Keep an eye on https://github.com/JuliaLang/julia/pull/43747 for this
270-
generate_shlib(f, tt, path, name, filename; cflags=cflags, kwargs...)
273+
generate_shlib(f, tt, true, path, name, filename; cflags=cflags, kwargs...)
271274

272275
joinpath(abspath(path), filename * "." * Libdl.dlext)
273276
end
@@ -276,7 +279,7 @@ function generate_shlib_fptr(f, tt, path::String=tempname(), name = GPUCompiler.
276279
temp::Bool=true,
277280
kwargs...)
278281

279-
generate_shlib(f, tt, path, name; kwargs...)
282+
generate_shlib(f, tt, false, path, name; kwargs...)
280283
lib_path = joinpath(abspath(path), "$filename.$(Libdl.dlext)")
281284
ptr = Libdl.dlopen(lib_path, Libdl.RTLD_LOCAL)
282285
fptr = Libdl.dlsym(ptr, "julia_$name")
@@ -358,7 +361,7 @@ function generate_executable(f, tt, path=tempname(), name=GPUCompiler.safe_name(
358361
mkpath(path)
359362
obj_path = joinpath(path, "$filename.o")
360363
exec_path = joinpath(path, filename)
361-
job, kwargs = native_job(f, tt; name, kwargs...)
364+
job, kwargs = native_job(f, tt, true; name, kwargs...)
362365
obj, _ = GPUCompiler.codegen(:obj, job; strip=true, only_entry=false, validate=false)
363366

364367
# Write to file
@@ -425,15 +428,15 @@ julia> ccall(StaticCompiler.generate_shlib_fptr(path, name), Float64, (Int64,),
425428
5.256496109495593
426429
```
427430
"""
428-
function generate_shlib(f, tt, path=tempname(), name=GPUCompiler.safe_name(repr(f)), filename=name;
431+
function generate_shlib(f, tt, external = true, path=tempname(), name=GPUCompiler.safe_name(repr(f)), filename=name;
429432
cflags=``,
430433
kwargs...
431434
)
432435

433436
mkpath(path)
434437
obj_path = joinpath(path, "$filename.o")
435438
lib_path = joinpath(path, "$filename.$(Libdl.dlext)")
436-
job, kwargs = native_job(f, tt; name, kwargs...)
439+
job, kwargs = native_job(f, tt, external; name, kwargs...)
437440
obj, _ = GPUCompiler.codegen(:obj, job; strip=true, only_entry=false, validate=false)
438441

439442
open(obj_path, "w") do io
@@ -449,26 +452,26 @@ function generate_shlib(f, tt, path=tempname(), name=GPUCompiler.safe_name(repr(
449452
end
450453

451454
function native_code_llvm(@nospecialize(func), @nospecialize(types); kwargs...)
452-
job, kwargs = native_job(func, types; kwargs...)
455+
job, kwargs = native_job(func, types, true; kwargs...)
453456
GPUCompiler.code_llvm(stdout, job; kwargs...)
454457
end
455458

456459
function native_code_typed(@nospecialize(func), @nospecialize(types); kwargs...)
457-
job, kwargs = native_job(func, types; kwargs...)
460+
job, kwargs = native_job(func, types, true; kwargs...)
458461
GPUCompiler.code_typed(job; kwargs...)
459462
end
460463

461464
# Return an LLVM module
462465
function native_llvm_module(f, tt, name = GPUCompiler.safe_name(repr(f)); kwargs...)
463-
job, kwargs = native_job(f, tt; name, kwargs...)
466+
job, kwargs = native_job(f, tt, true; name, kwargs...)
464467
m, _ = GPUCompiler.JuliaContext() do context
465468
GPUCompiler.codegen(:llvm, job; strip=true, only_entry=false, validate=false, ctx=context)
466469
end
467470
return m
468471
end
469472

470473
function native_code_native(@nospecialize(f), @nospecialize(tt), name = GPUCompiler.safe_name(repr(f)); kwargs...)
471-
job, kwargs = native_job(f, tt; name, kwargs...)
474+
job, kwargs = native_job(f, tt, true; name, kwargs...)
472475
GPUCompiler.code_native(stdout, job; kwargs...)
473476
end
474477

@@ -498,7 +501,7 @@ function native_llvm_module(funcs::Array; demangle = false, kwargs...)
498501
return mod
499502
end
500503

501-
function generate_obj(funcs::Array, path::String = tempname(), filenamebase::String="obj";
504+
function generate_obj(funcs::Array, external, path::String = tempname(), filenamebase::String="obj";
502505
demangle =false,
503506
strip_llvm = false,
504507
strip_asm = true,
@@ -507,7 +510,7 @@ function generate_obj(funcs::Array, path::String = tempname(), filenamebase::Str
507510
f,tt = funcs[1]
508511
mkpath(path)
509512
obj_path = joinpath(path, "$filenamebase.o")
510-
fakejob, kwargs = native_job(f,tt, kwargs...)
513+
fakejob, kwargs = native_job(f,tt, external, kwargs...)
511514
mod = native_llvm_module(funcs; demangle = demangle, kwargs...)
512515
obj, _ = GPUCompiler.emit_asm(fakejob, mod; strip=strip_asm, validate=false, format=LLVM.API.LLVMObjectFile)
513516
open(obj_path, "w") do io
@@ -516,15 +519,15 @@ function generate_obj(funcs::Array, path::String = tempname(), filenamebase::Str
516519
path, obj_path
517520
end
518521

519-
function generate_shlib(funcs::Array, path::String = tempname(), filename::String="libfoo";
522+
function generate_shlib(funcs::Array, external = true, path::String = tempname(), filename::String="libfoo";
520523
demangle=false,
521524
cflags=``,
522525
kwargs...
523526
)
524527

525528
lib_path = joinpath(path, "$filename.$(Libdl.dlext)")
526529

527-
_,obj_path = generate_obj(funcs, path, filename; demangle=demangle, kwargs...)
530+
_,obj_path = generate_obj(funcs, external, path, filename; demangle=demangle, kwargs...)
528531
# Pick a Clang
529532
cc = Sys.isapple() ? `cc` : clang()
530533
# Compile!
@@ -550,7 +553,7 @@ function compile_shlib(funcs::Array, path::String="./";
550553
# Would be nice to use a compiler pass or something to check if there are any heap allocations or references to globals
551554
# Keep an eye on https://github.com/JuliaLang/julia/pull/43747 for this
552555

553-
generate_shlib(funcs, path, filename; demangle=demangle, cflags=cflags, kwargs...)
556+
generate_shlib(funcs, true, path, filename; demangle=demangle, cflags=cflags, kwargs...)
554557

555558
joinpath(abspath(path), filename * "." * Libdl.dlext)
556559
end

src/pointer_patching.jl

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
function relocation_table!(mod)
22
i64 = LLVM.IntType(64; ctx=LLVM.context(mod))
33
d = IdDict{Any, Tuple{String, LLVM.GlobalVariable}}()
4-
4+
55
for func LLVM.functions(mod), bb LLVM.blocks(func), inst LLVM.instructions(bb)
6-
if isa(inst, LLVM.LoadInst) && occursin("inttoptr", string(inst))
6+
if isa(inst, LLVM.LoadInst) && occursin("inttoptr", string(inst))
77
get_pointers!(d, mod, inst)
8-
elseif isa(inst, LLVM.StoreInst) && occursin("inttoptr", string(inst))
8+
elseif isa(inst, LLVM.StoreInst) && occursin("inttoptr", string(inst))
99
@debug "Relocating StoreInst" inst
1010
get_pointers!(d, mod, inst)
1111
elseif inst isa LLVM.RetInst && occursin("inttoptr", string(inst))
@@ -101,7 +101,7 @@ function relocation_table!(mod)
101101
end
102102
end
103103

104-
if length(fn) > 1 && fromC
104+
if length(fn) > 1 && fromC
105105
mod = LLVM.parent(LLVM.parent(LLVM.parent(inst)))
106106
lfn = LLVM.API.LLVMGetNamedFunction(mod, fn)
107107

@@ -139,7 +139,7 @@ function get_pointers!(d, mod, inst)
139139
else
140140
gv_name = GPUCompiler.safe_name(String(gensym(repr(Core.Typeof(val)))))
141141
gv = LLVM.GlobalVariable(mod, llvmeltype(arg), gv_name, LLVM.addrspace(llvmtype(arg)))
142-
142+
143143
LLVM.extinit!(gv, true)
144144
LLVM.API.LLVMSetOperand(inst, i-1, gv)
145145

@@ -157,23 +157,23 @@ llvmeltype(x::LLVM.Value) = eltype(LLVM.llvmtype(x))
157157

158158
function pointer_patching_diff(f, tt, path1=tempname(), path2=tempname(); show_reloc_table=false)
159159
tm = GPUCompiler.llvm_machine(NativeCompilerTarget())
160-
job, kwargs = native_job(f, tt; name=GPUCompiler.safe_name(repr(f)))
160+
job, kwargs = native_job(f, tt, false; name=GPUCompiler.safe_name(repr(f)))
161161
#Get LLVM to generated a module of code for us. We don't want GPUCompiler's optimization passes.
162162
mod, meta = GPUCompiler.JuliaContext() do context
163163
GPUCompiler.codegen(:llvm, job; strip=true, only_entry=false, validate=false, optimize=false, ctx=context)
164164
end
165165
# Use Enzyme's annotation and optimization pipeline
166166
annotate!(mod)
167167
optimize!(mod, tm)
168-
168+
169169
s1 = string(mod)
170170
write(path1, s1)
171-
171+
172172
d = StaticCompiler.relocation_table!(mod)
173173
if show_reloc_table
174174
@show d
175175
end
176-
176+
177177
s2 = string(mod)
178178
write(path2, s2)
179179

src/quirks.jl

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
libcexit(x::Int32) = @symbolcall exit(x::Int32)::Nothing
2+
macro print_and_throw(err)
3+
quote
4+
println($err)
5+
libcexit(Int32(1))
6+
end
7+
end
8+
9+
# math.jl
10+
@device_override @noinline Base.Math.throw_complex_domainerror(f::Symbol, x) =
11+
@print_and_throw c"This operation requires a complex input to return a complex result"
12+
@device_override @noinline Base.Math.throw_exp_domainerror(f::Symbol, x) =
13+
@print_and_throw c"Exponentiation yielding a complex result requires a complex argument"
14+
15+
# intfuncs.jl
16+
@device_override @noinline Base.throw_domerr_powbysq(::Any, p) =
17+
@print_and_throw c"Cannot raise an integer to a negative power"
18+
@device_override @noinline Base.throw_domerr_powbysq(::Integer, p) =
19+
@print_and_throw c"Cannot raise an integer to a negative power"
20+
@device_override @noinline Base.throw_domerr_powbysq(::AbstractMatrix, p) =
21+
@print_and_throw c"Cannot raise an integer to a negative power"
22+
@device_override @noinline Base.__throw_gcd_overflow(a, b) =
23+
@print_and_throw c"gcd overflow"
24+
25+
# checked.jl
26+
@device_override @noinline Base.Checked.throw_overflowerr_binaryop(op, x, y) =
27+
@print_and_throw c"Binary operation overflowed"
28+
@device_override @noinline Base.Checked.throw_overflowerr_negation(op, x, y) =
29+
@print_and_throw c"Negation overflowed"
30+
@device_override function Base.Checked.checked_abs(x::Base.Checked.SignedInt)
31+
r = ifelse(x < 0, -x, x)
32+
r < 0 && @print_and_throw(c"checked arithmetic: cannot compute |x|")
33+
r
34+
end
35+
36+
# boot.jl
37+
@device_override @noinline Core.throw_inexacterror(f::Symbol, ::Type{T}, val) where {T} =
38+
@print_and_throw c"Inexact conversion"
39+
40+
# abstractarray.jl
41+
@device_override @noinline Base.throw_boundserror(A, I) =
42+
@print_and_throw c"Out-of-bounds array access"
43+
44+
# trig.jl
45+
@device_override @noinline Base.Math.sincos_domain_error(x) =
46+
@print_and_throw c"sincos(x) is only defined for finite x."

src/target.jl

Lines changed: 69 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,39 @@
1-
Base.@kwdef struct NativeCompilerTarget <: GPUCompiler.AbstractCompilerTarget
2-
cpu::String=(LLVM.version() < v"8") ? "" : unsafe_string(LLVM.API.LLVMGetHostCPUName())
3-
features::String=(LLVM.version() < v"8") ? "" : unsafe_string(LLVM.API.LLVMGetHostCPUFeatures())
1+
@static if isdefined(Base.Experimental, Symbol("@overlay"))
2+
Base.Experimental.@MethodTable(method_table)
3+
else
4+
const method_table = nothing
45
end
56

6-
GPUCompiler.llvm_triple(::NativeCompilerTarget) = Sys.MACHINE
7+
const overrides = quote end
78

8-
function GPUCompiler.llvm_machine(target::NativeCompilerTarget)
9-
triple = GPUCompiler.llvm_triple(target)
109

11-
t = LLVM.Target(triple=triple)
10+
macro device_override(ex)
11+
ex = macroexpand(__module__, ex)
12+
if Meta.isexpr(ex, :call)
13+
@show ex = eval(ex)
14+
error()
15+
end
16+
code = quote
17+
$GPUCompiler.@override(StaticCompiler.method_table, $ex)
18+
end
19+
if isdefined(Base.Experimental, Symbol("@overlay"))
20+
return esc(code)
21+
else
22+
push!(overrides, code)
23+
return
24+
end
25+
end
1226

13-
tm = LLVM.TargetMachine(t, triple, target.cpu, target.features, reloc=LLVM.API.LLVMRelocPIC)
14-
GPUCompiler.asm_verbosity!(tm, true)
1527

16-
return tm
28+
Base.@kwdef struct NativeCompilerTarget <: GPUCompiler.AbstractCompilerTarget
29+
cpu::String=(LLVM.version() < v"8") ? "" : unsafe_string(LLVM.API.LLVMGetHostCPUName())
30+
features::String=(LLVM.version() < v"8") ? "" : unsafe_string(LLVM.API.LLVMGetHostCPUFeatures())
1731
end
1832

19-
GPUCompiler.runtime_slug(job::GPUCompiler.CompilerJob{NativeCompilerTarget}) = "native_$(job.target.cpu)-$(hash(job.target.features))"
33+
Base.@kwdef struct ExternalNativeCompilerTarget <: GPUCompiler.AbstractCompilerTarget
34+
cpu::String=(LLVM.version() < v"8") ? "" : unsafe_string(LLVM.API.LLVMGetHostCPUName())
35+
features::String=(LLVM.version() < v"8") ? "" : unsafe_string(LLVM.API.LLVMGetHostCPUFeatures())
36+
end
2037

2138
module StaticRuntime
2239
# the runtime library
@@ -30,17 +47,50 @@ end
3047

3148
struct StaticCompilerParams <: GPUCompiler.AbstractCompilerParams end
3249

33-
GPUCompiler.runtime_module(::GPUCompiler.CompilerJob{<:Any,StaticCompilerParams}) = StaticRuntime
34-
GPUCompiler.runtime_module(::GPUCompiler.CompilerJob{NativeCompilerTarget}) = StaticRuntime
35-
GPUCompiler.runtime_module(::GPUCompiler.CompilerJob{NativeCompilerTarget, StaticCompilerParams}) = StaticRuntime
50+
for target in (:NativeCompilerTarget, :ExternalNativeCompilerTarget)
51+
@eval begin
52+
GPUCompiler.llvm_triple(::$target) = Sys.MACHINE
53+
54+
function GPUCompiler.llvm_machine(target::$target)
55+
triple = GPUCompiler.llvm_triple(target)
56+
57+
t = LLVM.Target(triple=triple)
58+
59+
tm = LLVM.TargetMachine(t, triple, target.cpu, target.features, reloc=LLVM.API.LLVMRelocPIC)
60+
GPUCompiler.asm_verbosity!(tm, true)
61+
62+
return tm
63+
end
64+
65+
GPUCompiler.runtime_slug(job::GPUCompiler.CompilerJob{$target}) = "native_$(job.target.cpu)-$(hash(job.target.features))"
66+
67+
GPUCompiler.runtime_module(::GPUCompiler.CompilerJob{$target}) = StaticRuntime
68+
GPUCompiler.runtime_module(::GPUCompiler.CompilerJob{$target, StaticCompilerParams}) = StaticRuntime
69+
70+
71+
GPUCompiler.can_throw(job::GPUCompiler.CompilerJob{$target, StaticCompilerParams}) = true
72+
GPUCompiler.can_throw(job::GPUCompiler.CompilerJob{$target}) = true
73+
end
74+
end
75+
76+
GPUCompiler.method_table(@nospecialize(job::GPUCompiler.CompilerJob{ExternalNativeCompilerTarget})) = method_table
77+
GPUCompiler.method_table(@nospecialize(job::GPUCompiler.CompilerJob{ExternalNativeCompilerTarget, StaticCompilerParams})) = method_table
78+
79+
function native_job(@nospecialize(func::Function), @nospecialize(types::Type), external::Bool;
80+
name = GPUCompiler.safe_name(repr(func)),
81+
kernel::Bool = false,
82+
kwargs...
83+
)
84+
source = GPUCompiler.FunctionSpec(func, types, kernel, name)
85+
target = external ? ExternalNativeCompilerTarget() : NativeCompilerTarget()
86+
params = StaticCompilerParams()
87+
GPUCompiler.CompilerJob(target, source, params), kwargs
88+
end
3689

37-
GPUCompiler.can_throw(job::GPUCompiler.CompilerJob{<:Any,StaticCompilerParams}) = true
38-
GPUCompiler.can_throw(job::GPUCompiler.CompilerJob{NativeCompilerTarget, StaticCompilerParams}) = true
39-
GPUCompiler.can_throw(job::GPUCompiler.CompilerJob{NativeCompilerTarget}) = true
4090

41-
function native_job(@nospecialize(func), @nospecialize(types); kernel::Bool=false, name=GPUCompiler.safe_name(repr(func)), kwargs...)
91+
function native_job(@nospecialize(func), @nospecialize(types), external; kernel::Bool=false, name=GPUCompiler.safe_name(repr(func)), kwargs...)
4292
source = GPUCompiler.FunctionSpec(func, Base.to_tuple_type(types), kernel, name)
43-
target = NativeCompilerTarget()
93+
target = external ? ExternalNativeCompilerTarget() : NativeCompilerTarget()
4494
params = StaticCompilerParams()
4595
GPUCompiler.CompilerJob(target, source, params), kwargs
4696
end

0 commit comments

Comments
 (0)