Skip to content

Commit 3ceb6fc

Browse files
committed
wip: runtime analysis
1 parent aff5a72 commit 3ceb6fc

File tree

5 files changed

+154
-4
lines changed

5 files changed

+154
-4
lines changed

Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ authors = ["Shuhei Kadowaki <aviatesk@gmail.com>"]
44
version = "0.9.4"
55

66
[deps]
7+
CassetteBase = "6dd3e646-b1c5-42c7-94be-00277fa12e22"
78
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
89
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
910
JuliaInterpreter = "aa1ae85d-cabe-5617-a682-6adf51b2e16a"

src/JET.jl

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ export
1111
# optanalyzer
1212
@report_opt, report_opt, @test_opt, test_opt,
1313
# configurations
14-
LastFrameModule, AnyFrameModule
14+
LastFrameModule, AnyFrameModule,
15+
@analysispass
1516

1617
let README = normpath(dirname(@__DIR__), "README.md")
1718
s = read(README, String)
@@ -42,8 +43,8 @@ using .CC: @nospecs, ⊑,
4243
OptimizationState, OptimizationParams, OverlayMethodTable, StmtInfo, UnionSplitInfo,
4344
UnionSplitMethodMatches, VarState, VarTable, WorldRange, WorldView,
4445
argextype, argtype_by_index, argtypes_to_type, hasintersect, ignorelimited,
45-
instanceof_tfunc, istopfunction, singleton_type, slot_id, specialize_method,
46-
tmeet, tmerge, typeinf_lattice, widenconst, widenlattice
46+
instanceof_tfunc, istopfunction, retrieve_code_info, singleton_type, slot_id,
47+
specialize_method, tmeet, tmerge, typeinf_lattice, widenconst, widenlattice
4748

4849
using Base: IdSet, get_world_counter
4950

@@ -1213,4 +1214,6 @@ using PrecompileTools
12131214
end
12141215
end
12151216

1217+
include("runtime.jl")
1218+
12161219
end # module JET

src/runtime.jl

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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

test/runtests.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ using Test, JET
3232
@testset "OptAnalyzer" include("analyzers/test_optanalyzer.jl")
3333
end
3434

35-
@testset "performance" include("performance.jl")
35+
@testset "runtime" include("runtime.jl")
36+
37+
@testset "performance" include("test_performance.jl")
3638

3739
@testset "sanity check" include("sanity_check.jl")
3840

test/test_runtime.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
module test_runtime
2+
3+
using JET, Test
4+
5+
call_xs(f, xs) = f(xs[])
6+
7+
@test_throws "Type{$Int}" @analysispass Int
8+
9+
pass1 = @analysispass JET.OptAnalyzer
10+
@test pass1() do
11+
call_xs(sin, Ref(42))
12+
end == sin(42)
13+
@test_throws JET.JETRuntimeError pass1() do
14+
call_xs(sin, Ref{Any}(42))
15+
end
16+
17+
function_filter(@nospecialize f) = f !== sin
18+
pass2 = @analysispass JET.OptAnalyzer function_filter
19+
@test pass2() do
20+
call_xs(sin, Ref(42))
21+
end == sin(42)
22+
@test pass2() do
23+
call_xs(sin, Ref{Any}(42))
24+
end
25+
26+
pass3 = @analysispass JET.JETAnalyzer
27+
@test pass3() do
28+
collect(1:10)
29+
end == collect(1:10)
30+
31+
end # module test_runtime

0 commit comments

Comments
 (0)