Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

alternative for #46, hybrid approach: #47

Merged
merged 10 commits into from
Oct 8, 2020
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ version = "0.0.0"

[deps]
FileWatching = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"
JuliaInterpreter = "aa1ae85d-cabe-5617-a682-6adf51b2e16a"
LoweredCodeUtils = "6f1432cf-f94c-5a45-995e-cdbf5db27b0b"
Requires = "ae029012-a4dd-5104-9daa-d747884805df"

[extras]
Expand Down
67 changes: 49 additions & 18 deletions src/TypeProfiler.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,19 @@ import Base:
parse_input_line, to_tuple_type, Fix1, Fix2, IdSet

import Base.Meta:
isexpr, _parse_string
isexpr, _parse_string, lower

import Base.Iterators:
flatten

using LoweredCodeUtils, JuliaInterpreter

import LoweredCodeUtils:
istypedef, ismethod

import JuliaInterpreter:
evaluate_call_recurse!, bypass_builtins, maybe_evaluate_builtin, collect_args, is_return

using FileWatching, Requires

const CC = Core.Compiler
Expand Down Expand Up @@ -123,6 +131,9 @@ function report_errors(actualmod, text, filename; filter_native_remarks = true)
gen_postprocess(virtualmod, actualmod)
end

gen_virtual_module(actualmod = Main) =
return Core.eval(actualmod, :(module $(gensym(:TypeProfilerVirtualModule)) end))::Module

# fix virtual module printing based on string manipulation; the "actual" modules may not be
# loaded into this process
function gen_postprocess(virtualmod, actualmod)
Expand All @@ -133,12 +144,28 @@ function gen_postprocess(virtualmod, actualmod)
Fix2(replace, virtual => actual)
end

# this dummy module will be used by `istoplevel` to check if the current inference frame is
# created by `profile_toplevel!` or not (i.e. `@generated` function)
module toplevel end

function profile_toplevel!(interp::TPInterpreter, mod::Module, src::CodeInfo)
# construct toplevel `MethodInstance`
mi = ccall(:jl_new_method_instance_uninit, Ref{Core.MethodInstance}, ());
mi.uninferred = src
mi.specTypes = Tuple{}
mi.def = mod

result = InferenceResult(mi);
frame = InferenceState(result, src, #= cached =# true, interp);

mi.def = toplevel # set to the dummy module

return profile_frame!(interp, frame)
end

# TODO:
# - handle multiple applicable methods ?
# - `profile_call_builtin!` ?

profile_call_gf(@nospecialize(tt::Type{<:Tuple}), world::UInt = get_world_counter(); kwargs...) =
return profile_call_gf!(TPInterpreter(world; kwargs...), tt)
function profile_call_gf!(interp::TPInterpreter,
@nospecialize(tt::Type{<:Tuple}),
world::UInt = get_world_counter(interp)
Expand All @@ -154,14 +181,31 @@ function profile_call_gf!(interp::TPInterpreter,

frame = InferenceState(result, #= cached =# true, interp)

return profile_frame(interp, frame)
return profile_frame!(interp, frame)
end

# testing, interactive session
# ----------------------------

# profile from call expression
macro profile_call(ex, kwargs...)
@assert isexpr(ex, :call) "function call expression should be given"
f = first(ex.args)
args = ex.args[2:end]

return quote let
argtypes = $(typeof′).(($(map(esc, args)...),))
interp, frame = $(profile_call)($(esc(f)), argtypes)
$(print_reports)(stdout::IO, interp.reports; $(map(esc, kwargs)...))
$(get_result)(frame) # maybe want to widen const ?
end end
end

@nospecialize

profile_call_gf(tt::Type{<:Tuple}, world::UInt = get_world_counter(); kwargs...) =
return profile_call_gf!(TPInterpreter(world; kwargs...), tt)

function profile_call(f, argtypes::Type...; kwargs...)
tt = to_tuple_type([typeof′(f), argtypes...])
return profile_call_gf(tt; kwargs...)
Expand All @@ -174,19 +218,6 @@ typeof′(x::Type{T}) where {T} = Type{T}

@specialize

macro profile_call(ex, kwargs...)
@assert isexpr(ex, :call) "function call expression should be given"
f = first(ex.args)
args = ex.args[2:end]

return quote let
argtypes = $(typeof′).(($(map(esc, args)...),))
interp, frame = $(profile_call)($(esc(f)), argtypes)
$(print_reports)(stdout::IO, interp.reports; $(map(esc, kwargs)...))
$(get_result)(frame) # maybe want to widen const ?
end end
end

# exports
# -------

Expand Down
Loading