Skip to content

Commit

Permalink
compress AST in memory, not just on disk
Browse files Browse the repository at this point in the history
sysimg size will be exactly the same,
but seems to be pretty significant savings in memory and initial GC time
(about 10% on each)
  • Loading branch information
vtjnash committed Mar 23, 2017
1 parent 2a5b3d8 commit e2f5701
Show file tree
Hide file tree
Showing 15 changed files with 269 additions and 145 deletions.
58 changes: 29 additions & 29 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -278,23 +278,19 @@ end
# create copies of the CodeInfo definition, and any fields that type-inference might modify
# TODO: post-inference see if we can swap back to the original arrays
function get_source(li::MethodInstance)
src = ccall(:jl_copy_code_info, Ref{CodeInfo}, (Any,), li.def.source)
if isa(src.code, Array{UInt8,1})
src.code = ccall(:jl_uncompress_ast, Any, (Any, Any), li.def, src.code)
if isa(li.def.source, Array{UInt8,1})
src = ccall(:jl_uncompress_ast, Any, (Any, Any), li.def, li.def.source)
else
src = ccall(:jl_copy_code_info, Ref{CodeInfo}, (Any,), li.def.source)
src.code = copy_exprargs(src.code)
src.slotnames = copy(src.slotnames)
src.slotflags = copy(src.slotflags)
end
src.slotnames = copy(src.slotnames)
src.slotflags = copy(src.slotflags)
return src
end

function get_staged(li::MethodInstance)
src = ccall(:jl_code_for_staged, Any, (Any,), li)::CodeInfo
if isa(src.code, Array{UInt8,1})
src.code = ccall(:jl_uncompress_ast, Any, (Any, Any), li.def, src.code)
end
return src
return ccall(:jl_code_for_staged, Any, (Any,), li)::CodeInfo
end


Expand Down Expand Up @@ -1579,7 +1575,7 @@ function pure_eval_call(f::ANY, argtypes::ANY, atype::ANY, sv::InferenceState)
meth = meth[1]::SimpleVector
method = meth[3]::Method
# TODO: check pure on the inferred thunk
if method.isstaged || !method.source.pure
if method.isstaged || !method.pure
return false
end

Expand Down Expand Up @@ -3027,7 +3023,7 @@ function finish(me::InferenceState)
end
if me.const_api
# use constant calling convention
inferred_result = inferred_const
inferred_result = nothing
else
inferred_result = me.src
end
Expand All @@ -3040,7 +3036,7 @@ function finish(me::InferenceState)
inferred_result = nothing
else
# compress code for non-toplevel thunks
inferred_result.code = ccall(:jl_compress_ast, Any, (Any, Any), me.linfo.def, inferred_result.code)
inferred_result = ccall(:jl_compress_ast, Any, (Any, Any), me.linfo.def, inferred_result)
end
end
end
Expand Down Expand Up @@ -3827,10 +3823,10 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
if f === return_type
if isconstType(e.typ)
return inline_as_constant(e.typ.parameters[1], argexprs, sv, invoke_data)
elseif isa(e.typ,Const)
elseif isa(e.typ, Const)
return inline_as_constant(e.typ.val, argexprs, sv, invoke_data)
end
elseif method.source.pure && isa(e.typ,Const) && e.typ.actual
elseif method.pure && isa(e.typ, Const) && e.typ.actual
return inline_as_constant(e.typ.val, argexprs, sv, invoke_data)
end
end
Expand Down Expand Up @@ -3889,14 +3885,14 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
if linfo.jlcall_api == 2
# in this case function can be inlined to a constant
add_backedge(linfo, sv)
return inline_as_constant(linfo.inferred, argexprs, sv, invoke_data)
return inline_as_constant(linfo.inferred_const, argexprs, sv, invoke_data)
end

# see if the method has a current InferenceState frame
# or existing inferred code info
frame = nothing # Union{Void, InferenceState}
src = nothing # Union{Void, CodeInfo}
if force_infer && la>2 && isa(atypes[3], Const)
inferred = nothing # Union{Void, CodeInfo}
if force_infer && la > 2 && isa(atypes[3], Const)
# Since we inferred this with the information that atypes[3]::Const,
# must inline with that same information.
# We do that by overriding the argument type,
Expand All @@ -3909,9 +3905,9 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
frame.stmt_types[1][3] = VarState(atypes[3], false)
typeinf_loop(frame)
else
if isdefined(linfo, :inferred) && isa(linfo.inferred, CodeInfo) && (linfo.inferred::CodeInfo).inferred
if isdefined(linfo, :inferred) && linfo.inferred !== nothing
# use cache
src = linfo.inferred
inferred = linfo.inferred
elseif linfo.inInference
# use WIP
frame = typeinf_active(linfo)
Expand All @@ -3928,7 +3924,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
if isa(frame, InferenceState)
frame = frame::InferenceState
linfo = frame.linfo
src = frame.src
inferred = frame.src
if frame.const_api # handle like jlcall_api == 2
if frame.inferred || !frame.cached
add_backedge(frame.linfo, sv)
Expand All @@ -3949,19 +3945,23 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
end

# check that the code is inlineable
isa(src, CodeInfo) || return invoke_NF(argexprs0, e.typ, atypes, sv,
atype_unlimited, invoke_data)
src = src::CodeInfo
ast = src.code
if !src.inferred || !src.inlineable
if inferred === nothing
src_inferred = src_inlineable = false
else
src_inferred = ccall(:jl_ast_flag_inferred, Bool, (Any,), inferred)
src_inlineable = ccall(:jl_ast_flag_inlineable, Bool, (Any,), inferred)
end
if !src_inferred || !src_inlineable
return invoke_NF(argexprs0, e.typ, atypes, sv, atype_unlimited,
invoke_data)
end

if !isa(ast, Array{Any,1})
ast = ccall(:jl_uncompress_ast, Any, (Any, Any), method, ast)
if isa(inferred, CodeInfo)
src = inferred
ast = copy_exprargs(inferred.code)
else
ast = copy_exprargs(ast)
src = ccall(:jl_uncompress_ast, Any, (Any, Any), method, inferred)::CodeInfo
ast = src.code
end
ast = ast::Array{Any,1}

Expand Down
18 changes: 7 additions & 11 deletions base/methodshow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,11 @@ function arg_decl_parts(m::Method)
push!(tv, sig.var)
sig = sig.body
end
if 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]
if isdefined(m, :source)
argnames = Vector{Any}(m.nargs)
ccall(:jl_fill_argnames, Void, (Any, Any), m.source, argnames)
show_env = ImmutableDict{Symbol, Any}()
for t in tv
show_env = ImmutableDict(show_env, :unionall_env => t)
Expand All @@ -75,14 +71,14 @@ function kwarg_decl(m::Method, kwtype::DataType)
kwli = ccall(:jl_methtable_lookup, Any, (Any, Any, UInt), kwtype.name.mt, sig, max_world(m))
if kwli !== nothing
kwli = kwli::Method
src = kwli.source
kws = filter(x->!('#' in string(x)), src.slotnames[kwli.nargs+1:end])
src = uncompressed_ast(kwli, 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)
i==0 && return kws
i == 0 && return kws
push!(kws, kws[i])
return deleteat!(kws,i)
return deleteat!(kws, i)
end
return ()
end
Expand Down
12 changes: 3 additions & 9 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -483,8 +483,7 @@ Returns an array of lowered ASTs for the methods matching the given generic func
"""
function code_lowered(f::ANY, t::ANY=Tuple)
asts = map(methods(f, t)) do m
m = m::Method
return uncompressed_ast(m, m.source)
return uncompressed_ast(m::Method)
end
return asts
end
Expand Down Expand Up @@ -641,13 +640,8 @@ end
isempty(mt::MethodTable) = (mt.defs === nothing)

uncompressed_ast(m::Method) = uncompressed_ast(m, m.source)
function uncompressed_ast(m::Method, s::CodeInfo)
if isa(s.code, Array{UInt8,1})
s = ccall(:jl_copy_code_info, Ref{CodeInfo}, (Any,), s)
s.code = ccall(:jl_uncompress_ast, Array{Any,1}, (Any, Any), m, s.code)
end
return s
end
uncompressed_ast(m::Method, s::CodeInfo) = s
uncompressed_ast(m::Method, s::Array{UInt8,1}) = ccall(:jl_uncompress_ast, Any, (Any, Any), m, s)::CodeInfo

# this type mirrors jl_cghooks_t (documented in julia.h)
struct CodegenHooks
Expand Down
1 change: 1 addition & 0 deletions base/serialize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,7 @@ function deserialize(s::AbstractSerializer, ::Type{Method})
meth.isva = isva
# TODO: compress template
meth.source = template
meth.pure = template.pure
if isstaged
linfo = ccall(:jl_new_method_instance_uninit, Ref{Core.MethodInstance}, ())
linfo.specTypes = Tuple
Expand Down
16 changes: 6 additions & 10 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -323,17 +323,13 @@ end
function show(io::IO, src::CodeInfo)
# Fix slot names and types in function body
print(io, "CodeInfo(")
if isa(src.code, Array{Any,1})
lambda_io = IOContext(io, :SOURCEINFO => src)
if src.slotnames !== nothing
lambda_io = IOContext(lambda_io, :SOURCE_SLOTNAMES => sourceinfo_slotnames(src))
end
body = Expr(:body)
body.args = src.code
show(lambda_io, body)
else
print(io, "<compressed>")
lambda_io = IOContext(io, :SOURCEINFO => src)
if src.slotnames !== nothing
lambda_io = IOContext(lambda_io, :SOURCE_SLOTNAMES => sourceinfo_slotnames(src))
end
body = Expr(:body)
body.args = src.code
show(lambda_io, body)
print(io, ")")
end

Expand Down
6 changes: 3 additions & 3 deletions doc/src/devdocs/ast.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,14 +214,14 @@ A unique'd container describing the shared metadata for a single method.

* `source`

The original source code (compressed).
The original source code (usually compressed).

* `roots`

Pointers to non-AST things that have been interpolated into the AST, required by
compression of the AST, type-inference, or the generation of native code.

* `nargs`, `isva`, `called`, `isstaged`
* `nargs`, `isva`, `called`, `isstaged`, `pure`

Descriptive bit-fields for the source code of this Method.

Expand Down Expand Up @@ -288,7 +288,7 @@ A temporary container for holding lowered source code.

* `code`

An `Any` array of statements, or a `UInt8` array with a compressed representation of the code.
An `Any` array of statements

* `slotnames`

Expand Down
2 changes: 1 addition & 1 deletion doc/src/devdocs/debuggingtips.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ While the addresses of many variables, like singletons, can be be useful to prin
there are a number of additional variables (see julia.h for a complete list) that are even more
useful.

* (when in `jl_apply_generic`) `f->linfo` and `jl_uncompress_ast(f->linfo, f->linfo->ast)` :: for
* (when in `jl_apply_generic`) `mfunc` and `jl_uncompress_ast(mfunc->def, mfunc->code)` :: for
figuring out a bit about the call-stack
* `jl_lineno` and `jl_filename` :: for figuring out what line in a test to go start debugging from
(or figure out how far into a file has been parsed)
Expand Down
54 changes: 37 additions & 17 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1145,6 +1145,8 @@ jl_llvm_functions_t jl_compile_linfo(jl_method_instance_t **pli, jl_code_info_t
// we waited at the lock).
if (li->def == NULL) {
src = (jl_code_info_t*)li->inferred;
if (src && (jl_value_t*)src != jl_nothing)
src = jl_uncompress_ast(li->def, (jl_array_t*)src);
if (decls.functionObject != NULL || !src || !jl_is_code_info(src) || li->jlcall_api == 2) {
goto locked_out;
}
Expand All @@ -1159,6 +1161,8 @@ jl_llvm_functions_t jl_compile_linfo(jl_method_instance_t **pli, jl_code_info_t
// see if it is inferred
src = (jl_code_info_t*)li->inferred;
if (src) {
if ((jl_value_t*)src != jl_nothing)
src = jl_uncompress_ast(li->def, (jl_array_t*)src);
if (!jl_is_code_info(src)) {
src = jl_type_infer(pli, world, 0);
li = *pli;
Expand All @@ -1176,6 +1180,10 @@ jl_llvm_functions_t jl_compile_linfo(jl_method_instance_t **pli, jl_code_info_t
// decl (unless compile fails), even if jlcall_api == 2
goto locked_out;
}
else {
if ((jl_value_t*)src != jl_nothing)
src = jl_uncompress_ast(li->def, (jl_array_t*)src);
}
assert(jl_is_code_info(src));

// Step 2: setup global state
Expand Down Expand Up @@ -1241,14 +1249,25 @@ jl_llvm_functions_t jl_compile_linfo(jl_method_instance_t **pli, jl_code_info_t
jl_finalize_module(m.release(), !toplevel);
}

if (world && li->jlcall_api != 2) {
// if not inlineable, code won't be needed again
if (JL_DELETE_NON_INLINEABLE && jl_options.debug_level <= 1 &&
li->def && li->inferred && jl_is_code_info(li->inferred) &&
!((jl_code_info_t*)li->inferred)->inlineable &&
li != li->def->unspecialized && !imaging_mode) {
li->inferred = jl_nothing;
}
// if not inlineable, code won't be needed again
if (JL_DELETE_NON_INLINEABLE &&
// don't delete code when debugging level >= 2
jl_options.debug_level <= 1 &&
// don't delete toplevel code
li->def &&
// don't change inferred state
li->inferred &&
// and there is something to delete (test this before calling jl_ast_flag_inlineable)
li->inferred != jl_nothing &&
// don't delete the code for the generator
li != li->def->generator &&
// don't delete inlineable code, unless it is constant
(li->jlcall_api == 2 || !jl_ast_flag_inlineable((jl_array_t*)li->inferred)) &&
// don't delete code when generating a precompile file
!imaging_mode &&
// don't delete code when it's not actually directly being used
world) {
li->inferred = jl_nothing;
}

// Step 6: Done compiling: Restore global state
Expand Down Expand Up @@ -1413,7 +1432,7 @@ jl_generic_fptr_t jl_generate_fptr(jl_method_instance_t *li, void *_F, size_t wo
// and return its fptr instead
if (!unspec)
unspec = jl_get_unspecialized(li); // get-or-create the unspecialized version to cache the result
jl_code_info_t *src = unspec->def->isstaged ? jl_code_for_staged(unspec) : unspec->def->source;
jl_code_info_t *src = unspec->def->isstaged ? jl_code_for_staged(unspec) : (jl_code_info_t*)unspec->def->source;
fptr.fptr = unspec->fptr;
fptr.jlcall_api = unspec->jlcall_api;
if (fptr.fptr && fptr.jlcall_api) {
Expand Down Expand Up @@ -1547,11 +1566,14 @@ void *jl_get_llvmf_defn(jl_method_instance_t *linfo, size_t world, bool getwrapp

jl_code_info_t *src = (jl_code_info_t*)linfo->inferred;
JL_GC_PUSH1(&src);
if (!src || !jl_is_code_info(src)) {
if (!src || (jl_value_t*)src == jl_nothing) {
src = jl_type_infer(&linfo, world, 0);
if (!src)
src = linfo->def->isstaged ? jl_code_for_staged(linfo) : linfo->def->source;
src = linfo->def->isstaged ? jl_code_for_staged(linfo) : (jl_code_info_t*)linfo->def->source;
}
if (!src || (jl_value_t*)src == jl_nothing)
jl_error("source not found for function");
src = jl_uncompress_ast(linfo->def, (jl_array_t*)src);

// Backup the info for the nested compile
JL_LOCK(&codegen_lock);
Expand Down Expand Up @@ -1634,7 +1656,7 @@ void *jl_get_llvmf_decl(jl_method_instance_t *linfo, size_t world, bool getwrapp
jl_code_info_t *src = NULL;
src = jl_type_infer(&linfo, world, 0);
if (!src) {
src = linfo->def->isstaged ? jl_code_for_staged(linfo) : linfo->def->source;
src = linfo->def->isstaged ? jl_code_for_staged(linfo) : (jl_code_info_t*)linfo->def->source;
}
decls = jl_compile_linfo(&linfo, src, world, &params);
linfo->functionObjectsDecls = decls;
Expand Down Expand Up @@ -3287,8 +3309,8 @@ static jl_cgval_t emit_invoke(jl_expr_t *ex, jl_codectx_t *ctx)
assert(jl_is_method_instance(li));
jl_llvm_functions_t decls = jl_compile_linfo(&li, NULL, ctx->world, ctx->params);
if (li->jlcall_api == 2) {
assert(li->inferred);
return mark_julia_const(li->inferred);
assert(li->inferred_const);
return mark_julia_const(li->inferred_const);
}
if (decls.functionObject) {
int jlcall_api = jl_jlcall_api(decls.functionObject);
Expand Down Expand Up @@ -4468,7 +4490,7 @@ static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_t
Function *gf_thunk = NULL;
if (specsig) {
if (lam->jlcall_api == 2) {
retval = mark_julia_const(lam->inferred);
retval = mark_julia_const(lam->inferred_const);
}
else {
assert(theFptr);
Expand Down Expand Up @@ -5032,8 +5054,6 @@ static std::unique_ptr<Module> emit_function(
jl_codectx_t ctx = {};
JL_GC_PUSH2(&ctx.code, &ctx.roots);
ctx.code = (jl_array_t*)src->code;
if (!jl_typeis(ctx.code, jl_array_any_type))
ctx.code = jl_uncompress_ast(lam->def, ctx.code);

//jl_static_show(JL_STDOUT, (jl_value_t*)ast);
//jl_printf(JL_STDOUT, "\n");
Expand Down
Loading

0 comments on commit e2f5701

Please sign in to comment.