Skip to content

Commit

Permalink
split SourceInfo out of LambdaInfo
Browse files Browse the repository at this point in the history
due to other recent changes, LambdaInfo is now
much more expensive to create and difficult to destroy

it is also hard to keep track of where modification is allowed

the SourceInfo type represents an atomic chunk of information
that needs to be immutable after construction

this sets up some of the work needed for #265,
and decreases the size of the sysimg :)
  • Loading branch information
vtjnash committed Sep 13, 2016
1 parent 47ec910 commit f1d6c17
Show file tree
Hide file tree
Showing 35 changed files with 1,462 additions and 1,351 deletions.
2 changes: 2 additions & 0 deletions base/essentials.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# This file is a part of Julia. License is MIT: http:https://julialang.org/license

using Core: SourceInfo

typealias Callable Union{Function,DataType}

const Bottom = Union{}
Expand Down
704 changes: 352 additions & 352 deletions base/inference.jl

Large diffs are not rendered by default.

21 changes: 11 additions & 10 deletions base/interactiveutil.jl
Original file line number Diff line number Diff line change
Expand Up @@ -316,22 +316,23 @@ See [Manual](:ref:`man-code-warntype`) for more information.
"""
function code_warntype(io::IO, f, t::ANY)
emph_io = IOContext(io, :TYPEEMPHASIZE => true)
for li in code_typed(f, t)
for (src, rettype) in code_typed(f, t)
println(emph_io, "Variables:")
slotnames = lambdainfo_slotnames(li)
slotnames = sourceinfo_slotnames(src)
for i = 1:length(slotnames)
print(emph_io, " ", slotnames[i])
if isa(li.slottypes,Array)
show_expr_type(emph_io, li.slottypes[i], true)
if isa(src.slottypes, Array)
show_expr_type(emph_io, src.slottypes[i], true)
end
print(emph_io, '\n')
end
print(emph_io, "\nBody:\n ")
body = Expr(:body); body.args = uncompressed_ast(li)
body.typ = li.rettype
body = Expr(:body)
body.args = src.code
body.typ = rettype
# Fix slot names and types in function body
show_unquoted(IOContext(IOContext(emph_io, :LAMBDAINFO => li),
:LAMBDA_SLOTNAMES => slotnames),
show_unquoted(IOContext(IOContext(emph_io, :SOURCEINFO => src),
:SOURCE_SLOTNAMES => slotnames),
body, 2)
print(emph_io, '\n')
end
Expand Down Expand Up @@ -709,12 +710,12 @@ whos(pat::Regex) = whos(STDOUT, current_module(), pat)
#################################################################################

"""
Base.summarysize(obj; exclude=Union{Module,Function,DataType,TypeName}) -> Int
Base.summarysize(obj; exclude=Union{Module,DataType,TypeName}) -> Int
Compute the amount of memory used by all unique objects reachable from the argument.
Keyword argument `exclude` specifies a type of objects to exclude from the traversal.
"""
summarysize(obj; exclude = Union{Module,Function,DataType,TypeName}) =
summarysize(obj; exclude = Union{Module,DataType,TypeName}) =
summarysize(obj, ObjectIdDict(), exclude)

summarysize(obj::Symbol, seen, excl) = 0
Expand Down
27 changes: 16 additions & 11 deletions base/methodshow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# Method and method table pretty-printing

function argtype_decl(env, n, sig, i, nargs, isva) # -> (argname, argtype)
function argtype_decl(env, n, sig::DataType, i::Int, nargs, isva::Bool) # -> (argname, argtype)
t = sig.parameters[i]
if i == nargs && isva && !isvarargtype(t)
t = Vararg{t,length(sig.parameters)-nargs+1}
Expand Down Expand Up @@ -39,15 +39,19 @@ function arg_decl_parts(m::Method)
else
tv = Any[tv...]
end
li = m.lambda_template
file, line = "", 0
if li !== nothing && isdefined(li, :slotnames)
argnames = li.slotnames[1:li.nargs]
decls = Any[argtype_decl(:tvar_env => tv, argnames[i], m.sig, i, li.nargs, li.isva)
for i = 1:li.nargs]
if isdefined(li, :def)
file, line = li.def.file, li.def.line
end
if m.isstaged
src = m.unspecialized.inferred
elseif isdefined(m, :source)
src = m.source
else
src = nothing
end
file = m.file
line = m.line
if src !== nothing && src.slotnames !== nothing
argnames = src.slotnames[1:m.nargs]
decls = Any[argtype_decl(:tvar_env => tv, argnames[i], m.sig, i, m.nargs, m.isva)
for i = 1:m.nargs]
else
decls = Any[("", "") for i = 1:length(m.sig.parameters)]
end
Expand All @@ -59,7 +63,8 @@ function kwarg_decl(sig::ANY, kwtype::DataType)
kwli = ccall(:jl_methtable_lookup, Any, (Any, Any), kwtype.name.mt, sig)
if kwli !== nothing
kwli = kwli::Method
kws = filter(x->!('#' in string(x)), kwli.lambda_template.slotnames[kwli.lambda_template.nargs+1:end])
src = kwli.isstaged ? kwli.unspecialized.inferred : kwli.source
kws = filter(x->!('#' in string(x)), src.slotnames[kwli.nargs+1:end])
# ensure the kwarg... is always printed last. The order of the arguments are not
# necessarily the same as defined in the function
i = findfirst(x -> endswith(string(x), "..."), kws)
Expand Down
60 changes: 36 additions & 24 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,13 @@ tt_cons(t::ANY, tup::ANY) = (@_pure_meta; Tuple{t, (isa(tup, Type) ? tup.paramet
Returns an array of lowered ASTs for the methods matching the given generic function and type signature.
"""
code_lowered(f, t::ANY=Tuple) = map(m -> (m::Method).lambda_template, methods(f, t))
function code_lowered(f, t::ANY=Tuple)
asts = map(methods(f, t)) do m
m = m::Method
return uncompressed_ast(m, m.isstaged ? m.unspecialized.inferred : m.source)
end
return asts
end

# low-level method lookup functions used by the compiler

Expand All @@ -345,6 +351,7 @@ function _methods(f::ANY,t::ANY,lim)
tt = isa(t,Type) ? Tuple{ft, t.parameters...} : Tuple{ft, t...}
return _methods_by_ftype(tt, lim)
end

function _methods_by_ftype(t::ANY, lim)
tp = t.parameters::SimpleVector
nu = 1
Expand All @@ -359,6 +366,7 @@ function _methods_by_ftype(t::ANY, lim)
# XXX: the following can return incorrect answers that the above branch would have corrected
return ccall(:jl_matching_methods, Any, (Any,Cint,Cint), t, lim, 0)
end

function _methods(t::Array,i,lim::Integer,matching::Array{Any,1})
if i == 0
new = ccall(:jl_matching_methods, Any, (Any,Cint,Cint), Tuple{t...}, lim, 0)
Expand Down Expand Up @@ -401,7 +409,7 @@ function MethodList(mt::MethodTable)
visit(mt) do m
push!(ms, m)
end
MethodList(ms, mt)
return MethodList(ms, mt)
end

"""
Expand Down Expand Up @@ -470,9 +478,14 @@ function length(mt::MethodTable)
end
isempty(mt::MethodTable) = (mt.defs === nothing)

uncompressed_ast(l::Method) = uncompressed_ast(l.lambda_template)
uncompressed_ast(l::LambdaInfo) =
isa(l.code,Array{UInt8,1}) ? ccall(:jl_uncompress_ast, Array{Any,1}, (Any,Any), l, l.code) : l.code
uncompressed_ast(m::Method) = uncompressed_ast(m, m.source)
function uncompressed_ast(m::Method, s::SourceInfo)
if isa(s.code, Array{UInt8,1})
s = ccall(:jl_copy_source_info, Ref{SourceInfo}, (Any,), s)
s.code = ccall(:jl_uncompress_ast, Array{Any,1}, (Any, Any), m, s.code)
end
return s
end

# Printing code representations in IR and assembly
function _dump_function(f::ANY, t::ANY, native::Bool, wrapper::Bool, strip_ir_metadata::Bool, dump_module::Bool)
Expand All @@ -487,9 +500,8 @@ function _dump_function(f::ANY, t::ANY, native::Bool, wrapper::Bool, strip_ir_me
tt = Tuple{ft, t.parameters...}
(ti, env) = ccall(:jl_match_method, Any, (Any, Any, Any),
tt, meth.sig, meth.tvars)::SimpleVector
li = func_for_method_checked(meth, tt)
# try to infer it
(linfo, ty, inf) = Core.Inference.typeinf(li, ti, env, true)
meth = func_for_method_checked(meth, tt)
linfo = ccall(:jl_specializations_get_linfo, Ref{LambdaInfo}, (Any, Any, Any), meth, tt, env)
# get the code for it
return _dump_function(linfo, native, wrapper, strip_ir_metadata, dump_module)
end
Expand Down Expand Up @@ -540,7 +552,7 @@ code_native(io::IO, f::ANY, types::ANY=Tuple) =
code_native(f::ANY, types::ANY=Tuple) = code_native(STDOUT, f, types)

# give a decent error message if we try to instantiate a staged function on non-leaf types
function func_for_method_checked(m::Method, types)
function func_for_method_checked(m::Method, types::ANY)
if m.isstaged && !isleaftype(types)
error("cannot call @generated function `", m, "` ",
"with abstract argument types: ", types)
Expand All @@ -564,16 +576,16 @@ function code_typed(f::ANY, types::ANY=Tuple; optimize=true)
types = to_tuple_type(types)
asts = []
for x in _methods(f,types,-1)
linfo = func_for_method_checked(x[3], types)
meth = func_for_method_checked(x[3], types)
if optimize
(li, ty, inf) = Core.Inference.typeinf(linfo, x[1], x[2], true)
(code, ty, inf) = Core.Inference.typeinf(meth, x[1], x[2], true)
else
(li, ty, inf) = Core.Inference.typeinf_uncached(linfo, x[1], x[2], optimize=false)
(code, ty, inf) = Core.Inference.typeinf_uncached(meth, x[1], x[2], optimize=false)
end
inf || error("inference not successful") # Inference disabled
push!(asts, li)
push!(asts, uncompressed_ast(meth, code) => ty)

This comment has been minimized.

Copy link
@JeffBezanson

JeffBezanson Oct 11, 2016

Member

Docs should be updated to say that this returns an array of Pairs.

end
asts
return asts
end

function return_types(f::ANY, types::ANY=Tuple)
Expand All @@ -584,12 +596,12 @@ function return_types(f::ANY, types::ANY=Tuple)
types = to_tuple_type(types)
rt = []
for x in _methods(f,types,-1)
linfo = func_for_method_checked(x[3], types)
(_li, ty, inf) = Core.Inference.typeinf(linfo, x[1], x[2])
meth = func_for_method_checked(x[3], types)
(code, ty, inf) = Core.Inference.typeinf(meth, x[1], x[2])
inf || error("inference not successful") # Inference disabled
push!(rt, ty)
end
rt
return rt
end

"""
Expand Down Expand Up @@ -635,7 +647,7 @@ function which_module(m::Module, s::Symbol)
if !isdefined(m, s)
error("\"$s\" is not defined in module $m")
end
binding_module(m, s)
return binding_module(m, s)
end

# function reflection
Expand All @@ -658,7 +670,7 @@ function functionloc(m::Method)
if ln <= 0
error("could not determine location of method definition")
end
(find_source_file(string(m.file)), ln)
return (find_source_file(string(m.file)), ln)
end

"""
Expand All @@ -668,10 +680,10 @@ Returns a tuple `(filename,line)` giving the location of a generic `Function` de
"""
functionloc(f::ANY, types::ANY) = functionloc(which(f,types))

function functionloc(f)
function functionloc(f::ANY)
mt = methods(f)
if isempty(mt)
if isa(f,Function)
if isa(f, Function)
error("function has no definitions")
else
error("object is not callable")
Expand All @@ -680,7 +692,7 @@ function functionloc(f)
if length(mt) > 1
error("function has multiple methods; please specify a type signature")
end
functionloc(first(mt))
return functionloc(first(mt))
end

"""
Expand All @@ -696,12 +708,12 @@ function_module(f::Function) = datatype_module(typeof(f))
Determine the module containing a given definition of a generic function.
"""
function function_module(f, types::ANY)
function function_module(f::ANY, types::ANY)
m = methods(f, types)
if isempty(m)
error("no matching methods")
end
first(m).module
return first(m).module
end

"""
Expand Down
17 changes: 0 additions & 17 deletions base/replutil.jl
Original file line number Diff line number Diff line change
Expand Up @@ -117,23 +117,6 @@ function show(io::IO, ::MIME"text/plain", f::Function)
end
end

function show(io::IO, ::MIME"text/plain", l::LambdaInfo)
show(io, l)
# Fix slot names and types in function body
ast = uncompressed_ast(l)
if ast !== nothing
println(io)
lambda_io = IOContext(io, :LAMBDAINFO => l)
if isdefined(l, :slotnames)
lambda_io = IOContext(lambda_io, :LAMBDA_SLOTNAMES => lambdainfo_slotnames(l))
end
body = Expr(:body)
body.args = ast
body.typ = l.rettype
show(lambda_io, body)
end
end

function show(io::IO, ::MIME"text/plain", r::LinSpace)
# show for linspace, e.g.
# linspace(1,3,7)
Expand Down
Loading

0 comments on commit f1d6c17

Please sign in to comment.