|
| 1 | +using CassetteBase |
| 2 | + |
| 3 | +abstract type AnalysisPass end |
| 4 | +function getconstructor end |
| 5 | +function getjetconfigs end |
| 6 | + |
| 7 | +struct JETRuntimeError <: Exception |
| 8 | + mi::MethodInstance |
| 9 | + res::JETCallResult |
| 10 | +end |
| 11 | +function Base.showerror(io::IO, err::JETRuntimeError) |
| 12 | + n = length(get_reports(err.res)) |
| 13 | + print(io, "JETRuntimeError raised by `$(err.res.source)`:") |
| 14 | + println(io) |
| 15 | + show(io, err.res) |
| 16 | +end |
| 17 | + |
| 18 | +function make_runtime_analysis_generator(selfname::Symbol, fargsname::Symbol) |
| 19 | + function runtime_analysis_generator(world::UInt, source::LineNumberNode, passtype, fargtypes) |
| 20 | + @nospecialize passtype fargtypes |
| 21 | + try |
| 22 | + return analyze_and_generate_ex(world, source, passtype, fargtypes, |
| 23 | + selfname, fargsname) |
| 24 | + catch err |
| 25 | + # internal error happened - return an expression to raise the special exception |
| 26 | + return generate_internalerr_ex( |
| 27 | + err, #=bt=#catch_backtrace(), #=context=#:runtime_analysis_generator, world, source, |
| 28 | + #=argnames=#Core.svec(selfname, fargsname), #=spnames=#Core.svec(), |
| 29 | + #=metadata=#(; world, source, passtype, fargtypes)) |
| 30 | + end |
| 31 | + end |
| 32 | +end |
| 33 | + |
| 34 | +function analyze_and_generate_ex(world::UInt, source::LineNumberNode, passtype, fargtypes, |
| 35 | + selfname::Symbol, fargsname::Symbol, ) |
| 36 | + @nospecialize passtype fargtypes |
| 37 | + tt = Base.to_tuple_type(fargtypes) |
| 38 | + match = Base._which(tt; raise=false, world) |
| 39 | + match === nothing && return nothing # method match failed – the fallback implementation will raise a proper MethodError |
| 40 | + mi = specialize_method(match) |
| 41 | + |
| 42 | + Analyzer = getconstructor(passtype) |
| 43 | + jetconfigs = getjetconfigs(passtype) |
| 44 | + analyzer = Analyzer(world; jetconfigs...) |
| 45 | + analyzer, result = analyze_method_instance!(analyzer, mi) |
| 46 | + analyzername = nameof(typeof(analyzer)) |
| 47 | + sig = LazyPrinter(io::IO->Base.show_tuple_as_call(io, Symbol(""), tt)) |
| 48 | + src = lazy"$analyzername: $sig" |
| 49 | + res = JETCallResult(result, analyzer, src; jetconfigs...) |
| 50 | + if !isempty(get_reports(res)) |
| 51 | + # JET found some problems - return an expression to raise it to the user |
| 52 | + throw_ex = :(throw($JETRuntimeError($mi, $res))) |
| 53 | + argnames = Core.svec(selfname, fargsname) |
| 54 | + return generate_lambda_ex(world, source, argnames, #=spnames=#Core.svec(), throw_ex) |
| 55 | + end |
| 56 | + |
| 57 | + src = retrieve_code_info(mi, world) |
| 58 | + src === nothing && return nothing # code generation failed - the fallback implementation will re-raise it |
| 59 | + return cassette_transform!(src, mi, length(fargtypes), selfname, fargsname) |
| 60 | +end |
| 61 | + |
| 62 | +macro analysispass(args...) |
| 63 | + isempty(args) && throw(ArgumentError("`@analysispass` expected more than one argument.")) |
| 64 | + analyzertype = args[1] |
| 65 | + params = Expr(:parameters) |
| 66 | + append!(params.args, args[2:end]) |
| 67 | + jetconfigs = Expr(:tuple, params) |
| 68 | + |
| 69 | + PassName = esc(gensym(string(analyzertype))) |
| 70 | + |
| 71 | + blk = quote |
| 72 | + let analyzertypetype = Core.Typeof($(esc(analyzertype))) |
| 73 | + if !(analyzertypetype <: Type{<:$(@__MODULE__).AbstractAnalyzer}) |
| 74 | + throw(ArgumentError( |
| 75 | + "`@analysispass` expected a subtype of `JET.AbstractAnalyzer`, but got object of `$analyzertypetype`.")) |
| 76 | + end |
| 77 | + end |
| 78 | + |
| 79 | + struct $PassName <: $AnalysisPass end |
| 80 | + |
| 81 | + $(@__MODULE__).getconstructor(::Type{$PassName}) = $(esc(analyzertype)) |
| 82 | + $(@__MODULE__).getjetconfigs(::Type{$PassName}) = $(esc(jetconfigs)) |
| 83 | + |
| 84 | + @inline function (::$PassName)(f::Union{Core.Builtin,Core.IntrinsicFunction}, args...) |
| 85 | + @nospecialize f args |
| 86 | + return f(args...) |
| 87 | + end |
| 88 | + @inline function (self::$PassName)(::typeof(Core.Compiler.return_type), tt::DataType) |
| 89 | + # return Core.Compiler.return_type(self, tt) |
| 90 | + return Core.Compiler.return_type(tt) |
| 91 | + end |
| 92 | + @inline function (self::$PassName)(::typeof(Core.Compiler.return_type), f, tt::DataType) |
| 93 | + newtt = Base.signature_type(f, tt) |
| 94 | + # return Core.Compiler.return_type(self, newtt) |
| 95 | + return Core.Compiler.return_type(newtt) |
| 96 | + end |
| 97 | + @inline function (self::$PassName)(::typeof(Core._apply_iterate), iterate, f, args...) |
| 98 | + @nospecialize args |
| 99 | + return Core.Compiler._apply_iterate(iterate, self, (f,), args...) |
| 100 | + end |
| 101 | + |
| 102 | + function (pass::$PassName)(fargs...) |
| 103 | + $(Expr(:meta, :generated, make_runtime_analysis_generator(:pass, :fargs))) |
| 104 | + # also include a fallback implementation that will be used when this method |
| 105 | + # is dynamically dispatched with `!isdispatchtuple` signatures. |
| 106 | + return first(fargs)(Base.tail(fargs)...) |
| 107 | + end |
| 108 | + |
| 109 | + return $PassName() |
| 110 | + end |
| 111 | + |
| 112 | + return Expr(:toplevel, blk.args...) |
| 113 | +end |
0 commit comments