@@ -15,23 +15,6 @@ safe_fn(fn::String) = replace(fn, r"[^aA-zZ0-9_]"=>"_")
15
15
safe_fn (f:: Core.Function ) = safe_fn (String (typeof (f). name. mt. name))
16
16
safe_fn (f:: LLVM.Function ) = safe_fn (LLVM. name (f))
17
17
18
- # NOTE: we remove `throw_...` functions in the ThrowRemoval pass, but that relies on
19
- # function names which is fragile. Remove actual calls to `jl_throw` here, to be safe.
20
- const exception_arguments = Vector {LLVM.Value} ()
21
- function raise_exception (insblock:: BasicBlock , ex:: Value )
22
- fun = LLVM. parent (insblock)
23
- mod = LLVM. parent (fun)
24
-
25
- let builder = Builder (JuliaContext ())
26
- position! (builder, insblock)
27
- emit_exception! (builder, " unknown" , ex)
28
- dispose (builder)
29
- end
30
-
31
- # mark arguments that passed to `throw` for removal, as they are often GC-allocated.
32
- push! (exception_arguments, ex)
33
- end
34
-
35
18
# generate a pseudo-backtrace from a stack of methods being emitted
36
19
function backtrace (ctx:: CompilerContext , method_stack:: Vector{Core.MethodInstance} )
37
20
bt = StackTraces. StackFrame[]
@@ -57,11 +40,6 @@ function compile_linfo(ctx::CompilerContext, linfo::Core.MethodInstance, world)
57
40
ref = convert (LLVM. API. LLVMModuleRef, ref)
58
41
module_setup (LLVM. Module (ref))
59
42
end
60
- function hook_raise_exception (insblock:: Ptr{Cvoid} , ex:: Ptr{Cvoid} )
61
- insblock = convert (LLVM. API. LLVMValueRef, insblock)
62
- ex = convert (LLVM. API. LLVMValueRef, ex)
63
- raise_exception (BasicBlock (insblock), Value (ex))
64
- end
65
43
dependencies = Vector {LLVM.Module} ()
66
44
function hook_module_activation (ref:: Ptr{Cvoid} )
67
45
ref = convert (LLVM. API. LLVMModuleRef, ref)
@@ -103,7 +81,6 @@ function compile_linfo(ctx::CompilerContext, linfo::Core.MethodInstance, world)
103
81
prefer_specsig = true ,
104
82
module_setup = hook_module_setup,
105
83
module_activation = hook_module_activation,
106
- raise_exception = hook_raise_exception,
107
84
emit_function = hook_emit_function,
108
85
emitted_function = hook_emitted_function)
109
86
@@ -189,15 +166,6 @@ function irgen(ctx::CompilerContext)
189
166
end
190
167
end
191
168
192
- # HACK: remove unused arguments to exception functions
193
- for val in exception_arguments
194
- if isa (val, LLVM. Instruction) && isempty (uses (val))
195
- bb = LLVM. parent (val)
196
- unsafe_delete! (bb, val)
197
- end
198
- end
199
- empty! (exception_arguments)
200
-
201
169
# rename the entry point
202
170
llvmfn = replace (LLVM. name (entry), r" _\d +$" => " " )
203
171
# # append a global unique counter
@@ -211,6 +179,7 @@ function irgen(ctx::CompilerContext)
211
179
global global_ctx
212
180
global_ctx = ctx
213
181
182
+ add! (pm, ModulePass (" LowerThrow" , lower_throw!))
214
183
add! (pm, FunctionPass (" HideUnreachable" , hide_unreachable!))
215
184
add! (pm, FunctionPass (" HideTrap" , hide_trap!))
216
185
always_inliner! (pm)
@@ -221,6 +190,75 @@ function irgen(ctx::CompilerContext)
221
190
return mod, entry
222
191
end
223
192
193
+ # this pass lowers `jl_throw` and friends to GPU-compatible exceptions.
194
+ # this isn't strictly necessary, but has a couple of advantages:
195
+ # - we can kill off unused exception arguments that otherwise would allocate or invoke
196
+ # - we can fake debug information (lacking a stack unwinder)
197
+ #
198
+ # once we have thorough inference (ie. discarding `@nospecialize` and thus supporting
199
+ # exception arguments) and proper debug info to unwind the stack, this pass can go.
200
+ function lower_throw! (mod:: LLVM.Module )
201
+ ctx = global_ctx:: CompilerContext
202
+ changed = false
203
+
204
+ throw_functions = Dict {String,String} (
205
+ " jl_throw" => " exception" ,
206
+ " jl_error" => " error" ,
207
+ " jl_too_few_args" => " too few arguments exception" ,
208
+ " jl_too_many_args" => " too many arguments exception" ,
209
+ " jl_type_error_rt" => " type error" ,
210
+ " jl_undefined_var_error" => " undefined variable error" ,
211
+ " jl_bounds_error" => " bounds error" ,
212
+ " jl_bounds_error_v" => " bounds error" ,
213
+ " jl_bounds_error_int" => " bounds error" ,
214
+ " jl_bounds_error_tuple_int" => " bounds error" ,
215
+ " jl_bounds_error_unboxed_int" => " bounds error" ,
216
+ " jl_bounds_error_ints" => " bounds error" ,
217
+ " jl_eof_error" => " EOF error"
218
+ )
219
+
220
+ for (fn, name) in throw_functions
221
+ if haskey (functions (mod), fn)
222
+ f = functions (mod)[fn]
223
+
224
+ for use in uses (f)
225
+ call = user (use):: LLVM.CallInst
226
+
227
+ # replace the throw with a PTX-compatible exception
228
+ let builder = Builder (JuliaContext ())
229
+ position! (builder, call)
230
+ emit_exception! (builder, name, call)
231
+ dispose (builder)
232
+ end
233
+
234
+ # remove the call
235
+ call_args = collect (operands (call))[1 : end - 1 ] # last arg is function itself
236
+ unsafe_delete! (LLVM. parent (call), call)
237
+
238
+ # HACK: kill the exceptions' unused arguments
239
+ for arg in call_args
240
+ # peek through casts
241
+ if isa (arg, LLVM. AddrSpaceCastInst)
242
+ cast = arg
243
+ arg = first (operands (cast))
244
+ isempty (uses (cast)) && unsafe_delete! (LLVM. parent (cast), cast)
245
+ end
246
+
247
+ if isa (arg, LLVM. Instruction) && isempty (uses (arg))
248
+ unsafe_delete! (LLVM. parent (arg), arg)
249
+ end
250
+ end
251
+
252
+ changed = true
253
+ end
254
+
255
+ @compiler_assert isempty (uses (f)) ctx
256
+ end
257
+ end
258
+
259
+ return changed
260
+ end
261
+
224
262
# report an exception in a GPU-compatible manner
225
263
#
226
264
# the exact behavior depends on the debug level. in all cases, a `trap` will be emitted, On
0 commit comments