Skip to content

Commit

Permalink
separate Slot into SlotNumber and TypedSlot to save space
Browse files Browse the repository at this point in the history
serialize SlotNumber and GenSym more efficiently
  • Loading branch information
JeffBezanson committed Apr 25, 2016
1 parent 07b197f commit 50a06e3
Show file tree
Hide file tree
Showing 15 changed files with 125 additions and 95 deletions.
8 changes: 4 additions & 4 deletions base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export
StackOverflowError, SegmentationFault, UndefRefError, UndefVarError, TypeError,
# AST representation
Expr, GotoNode, LabelNode, LineNumberNode, QuoteNode, TopNode,
GlobalRef, NewvarNode, GenSym, Slot,
GlobalRef, NewvarNode, GenSym, Slot, SlotNumber, TypedSlot,
# object model functions
fieldtype, getfield, setfield!, nfields, throw, tuple, is, ===, isdefined, eval,
# sizeof # not exported, to avoid conflicting with Base.sizeof
Expand Down Expand Up @@ -277,13 +277,13 @@ _new(typ::Symbol, argty::Symbol) = eval(:((::Type{$typ})(n::$argty) = $(Expr(:ne
_new(:LabelNode, :Int)
_new(:GotoNode, :Int)
_new(:TopNode, :Symbol)
_new(:NewvarNode, :Slot)
_new(:NewvarNode, :SlotNumber)
_new(:QuoteNode, :ANY)
_new(:GenSym, :Int)
eval(:((::Type{LineNumberNode})(f::Symbol, l::Int) = $(Expr(:new, :LineNumberNode, :f, :l))))
eval(:((::Type{GlobalRef})(m::Module, s::Symbol) = $(Expr(:new, :GlobalRef, :m, :s))))
eval(:((::Type{Slot})(n::Int) = $(Expr(:new, :Slot, :n, Any))))
eval(:((::Type{Slot})(n::Int, t::ANY) = $(Expr(:new, :Slot, :n, :t))))
eval(:((::Type{SlotNumber})(n::Int) = $(Expr(:new, :SlotNumber, :n))))
eval(:((::Type{TypedSlot})(n::Int, t::ANY) = $(Expr(:new, :TypedSlot, :n, :t))))

Module(name::Symbol=:anonymous, std_imports::Bool=true) = ccall(:jl_f_new_module, Ref{Module}, (Any, Bool), name, std_imports)

Expand Down
4 changes: 1 addition & 3 deletions base/expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,14 @@ copy(e::Expr) = (n = Expr(e.head);
n.args = astcopy(e.args);
n.typ = e.typ;
n)
copy(s::Slot) = Slot(s.id, s.typ)

# copy parts of an AST that the compiler mutates
astcopy(x::Union{Slot,Expr}) = copy(x)
astcopy(x::Expr) = copy(x)
astcopy(x::Array{Any,1}) = Any[astcopy(a) for a in x]
astcopy(x) = x

==(x::Expr, y::Expr) = x.head === y.head && isequal(x.args, y.args)
==(x::QuoteNode, y::QuoteNode) = isequal(x.value, y.value)
==(x::Slot, y::Slot) = x.id === y.id && x.typ === y.typ

expand(x::ANY) = ccall(:jl_expand, Any, (Any,), x)
macroexpand(x::ANY) = ccall(:jl_macroexpand, Any, (Any,), x)
Expand Down
1 change: 0 additions & 1 deletion base/hashing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ else
end

hash(x::QuoteNode, h::UInt) = hash(x.value, hash(QuoteNode, h))
hash(x::Slot, h::UInt) = hash(x.id, hash(x.typ, hash(Slot, h)))

# hashing ranges by component at worst leads to collisions for very similar ranges
const hashr_seed = UInt === UInt64 ? 0x80707b6821b70087 : 0x21b70087
Expand Down
95 changes: 46 additions & 49 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1915,28 +1915,14 @@ function finish(me::InferenceState)
nothing
end

function record_var_type(s::Slot, t::ANY, decls)
t = widenconst(t)
otherTy = decls[s.id]
# keep track of whether a variable is always the same type
if !is(otherTy,NF)
if !typeseq(otherTy, t)
decls[s.id] = Any
end
else
decls[s.id] = t
end
end

function eval_annotate(e::ANY, vtypes::ANY, sv::InferenceState, decls, undefs)
function eval_annotate(e::ANY, vtypes::ANY, sv::InferenceState, undefs)
if isa(e, Slot)
t = abstract_eval(e, vtypes, sv)
s = vtypes[e.id]
if s.undef
undefs[e.id] = true
end
record_var_type(e, t, decls)
return t === e.typ ? e : Slot(e.id, t)
return t === sv.linfo.slottypes[e.id] ? e : TypedSlot(e.id, t)
end

if !isa(e,Expr)
Expand All @@ -1947,28 +1933,15 @@ function eval_annotate(e::ANY, vtypes::ANY, sv::InferenceState, decls, undefs)
head = e.head
if is(head,:static_typeof) || is(head,:line) || is(head,:const)
return e
#elseif is(head,:gotoifnot) || is(head,:return)
# e.typ = Any
elseif is(head,:(=))
# e.typ = Any
s = e.args[1]
# assignment LHS not subject to all-same-type variable checking,
# but the type of the RHS counts as one of its types.
e.args[2] = eval_annotate(e.args[2], vtypes, sv, decls, undefs)
if isa(s,Slot)
# TODO: if this def does not reach any uses, maybe don't do this
rhstype = exprtype(e.args[2], sv)
if !is(rhstype,Bottom)
record_var_type(s, rhstype, decls)
end
end
e.args[2] = eval_annotate(e.args[2], vtypes, sv, undefs)
return e
end
i0 = is(head,:method) ? 2 : 1
for i=i0:length(e.args)
subex = e.args[i]
if !(isa(subex,Number) || isa(subex,AbstractString))
e.args[i] = eval_annotate(subex, vtypes, sv, decls, undefs)
e.args[i] = eval_annotate(subex, vtypes, sv, undefs)
end
end
return e
Expand All @@ -1989,21 +1962,36 @@ end
# annotate types of all symbols in AST
function type_annotate!(linfo::LambdaInfo, states::Array{Any,1}, sv::ANY, rettype::ANY, nargs)
nslots = length(states[1])
decls = Any[ NF for i = 1:nslots ]
undefs = fill(false, nslots)
# initialize decls with argument types
for i = 1:nargs
decls[i] = widenconst(states[1][i].typ)
for i = 1:nslots
linfo.slottypes[i] = Bottom
end
undefs = fill(false, nslots)
body = linfo.code::Array{Any,1}
nexpr = length(body)
for i=1:nexpr
# identify variables always used as the same type
st_i = states[i]
if st_i !== ()
for j = 1:nslots
vt = widenconst(st_i[j].typ)
if vt !== Bottom
otherTy = linfo.slottypes[j]
if otherTy === Bottom
linfo.slottypes[j] = vt
elseif otherTy !== Any && !typeseq(otherTy, vt)
linfo.slottypes[j] = Any
end
end
end
end
end
i = 1
optimize = sv.optimize::Bool
while i <= nexpr
st_i = states[i]
if st_i !== ()
# st_i === () => unreached statement (see issue #7836)
body[i] = eval_annotate(body[i], st_i, sv, decls, undefs)
body[i] = eval_annotate(body[i], st_i, sv, undefs)
elseif optimize
expr = body[i]
if isa(expr, Expr) && expr_cannot_delete(expr::Expr)
Expand All @@ -2020,11 +2008,8 @@ function type_annotate!(linfo::LambdaInfo, states::Array{Any,1}, sv::ANY, rettyp
i += 1
end

# add declarations for variables that are always the same type
# mark used-undef variables
for i = 1:nslots
if decls[i] !== NF
linfo.slottypes[i] = decls[i]
end
if undefs[i]
linfo.slotflags[i] |= 32
end
Expand All @@ -2034,7 +2019,7 @@ end

# widen all Const elements in type annotations
_widen_all_consts(x::ANY) = x
_widen_all_consts(x::Slot) = Slot(x.id, widenconst(x.typ))
_widen_all_consts(x::TypedSlot) = TypedSlot(x.id, widenconst(x.typ))
function _widen_all_consts(x::Expr)
x.typ = widenconst(x.typ)
for i = 1:length(x.args)
Expand All @@ -2059,7 +2044,11 @@ function substitute!(e::ANY, na, argexprs, spvals, offset)
if 1 <= e.id <= na
return argexprs[e.id]
end
return Slot(e.id+offset, e.typ)
if isa(e, SlotNumber)
return SlotNumber(e.id+offset)
else
return TypedSlot(e.id+offset, e.typ)
end
end
if isa(e,NewvarNode)
return NewvarNode(substitute!(e.slot, na, argexprs, spvals, offset))
Expand Down Expand Up @@ -2100,7 +2089,9 @@ end
function exprtype(x::ANY, sv::InferenceState)
if isa(x,Expr)
return (x::Expr).typ
elseif isa(x,Slot)
elseif isa(x,SlotNumber)
return sv.linfo.slottypes[x.id]
elseif isa(x,TypedSlot)
return (x::Slot).typ
elseif isa(x,GenSym)
return abstract_eval_gensym(x::GenSym, sv)
Expand Down Expand Up @@ -2975,12 +2966,14 @@ function inlining_pass(e::Expr, sv, linfo)
return (e,stmts)
end

const compiler_temp_sym = symbol("#temp#")

function add_slot!(linfo::LambdaInfo, typ, is_sa)
id = length(linfo.slotnames)+1
push!(linfo.slotnames, :__temp__)
push!(linfo.slotnames, compiler_temp_sym)
push!(linfo.slottypes, typ)
push!(linfo.slotflags, 2+16*is_sa)
Slot(id, typ)
SlotNumber(id)
end

function is_known_call(e::Expr, func, sv)
Expand Down Expand Up @@ -3049,7 +3042,8 @@ function remove_redundant_temp_vars(linfo, sa, T)
# (from inlining improved type inference information)
# and this transformation would worsen the type information
# everywhere later in the function
if init.typ (T===GenSym ? gensym_types[v+1] : linfo.slottypes[v])
ityp = isa(init,TypedSlot) ? init.typ : linfo.slottypes[init.id]
if ityp (T===GenSym ? gensym_types[v+1] : linfo.slottypes[v])
delete_var!(linfo, v, T)
slot_replace!(linfo, v, init, T)
end
Expand Down Expand Up @@ -3279,8 +3273,11 @@ function replace_getfield!(linfo::LambdaInfo, e::Expr, tupname, vals, field_name
# the tuple element expression that's replacing it.
if isa(val,Slot)
val = val::Slot
if a.typ val.typ && !(val.typ a.typ)
val.typ = a.typ
valtyp = isa(val,TypedSlot) ? val.typ : linfo.slottypes[val.id]
if a.typ valtyp && !(valtyp a.typ)
if isa(val,TypedSlot)
val = TypedSlot(val.id, a.typ)
end
linfo.slottypes[val.id] = widenconst(a.typ)
end
elseif isa(val,GenSym)
Expand Down
4 changes: 2 additions & 2 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ show_unquoted(io::IO, ex::TopNode, ::Int, ::Int) = print(io,"top(",ex.nam
show_unquoted(io::IO, ex::GlobalRef, ::Int, ::Int) = print(io, ex.mod, '.', ex.name)

function show_unquoted(io::IO, ex::Slot, ::Int, ::Int)
typ = ex.typ
typ = isa(ex,TypedSlot) ? ex.typ : Any
slotid = ex.id
li = get(io, :LAMBDAINFO, false)
if isa(li, LambdaInfo)
Expand All @@ -545,7 +545,7 @@ function show_unquoted(io::IO, ex::Slot, ::Int, ::Int)
print(io, "_", slotid)
end
emphstate = typeemphasize(io)
if emphstate || typ !== Any
if emphstate || (typ !== Any && isa(ex,TypedSlot))
show_expr_type(io, typ, emphstate)
end
end
Expand Down
4 changes: 4 additions & 0 deletions src/alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,7 @@ UIBOX_FUNC(uint16, uint16_t, 1)
UIBOX_FUNC(uint32, uint32_t, 1)
UIBOX_FUNC(char, uint32_t, 1)
UIBOX_FUNC(gensym, size_t, 1)
UIBOX_FUNC(slotnumber, size_t, 1)
#ifdef _P64
SIBOX_FUNC(int64, int64_t, 1)
UIBOX_FUNC(uint64, uint64_t, 1)
Expand Down Expand Up @@ -1079,8 +1080,10 @@ void jl_init_int32_int64_cache(void)
boxed_int64_cache[i] = jl_box64(jl_int64_type, i-NBOX_C/2);
#ifdef _P64
boxed_gensym_cache[i] = jl_box64(jl_gensym_type, i);
boxed_slotnumber_cache[i] = jl_box64(jl_slotnumber_type, i);
#else
boxed_gensym_cache[i] = jl_box32(jl_gensym_type, i);
boxed_slotnumber_cache[i] = jl_box32(jl_slotnumber_type, i);
#endif
}
for(i=0; i < 256; i++) {
Expand Down Expand Up @@ -1119,6 +1122,7 @@ void jl_mark_box_caches(void)
jl_gc_setmark(boxed_char_cache[i]);
jl_gc_setmark(boxed_uint64_cache[i]);
jl_gc_setmark(boxed_gensym_cache[i]);
jl_gc_setmark(boxed_slotnumber_cache[i]);
}
}

Expand Down
11 changes: 3 additions & 8 deletions src/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -443,13 +443,8 @@ static jl_value_t *scm_to_julia_(fl_context_t *fl_ctx, value_t e, int eo)
hd = car_(e);
if (hd == jl_ast_ctx(fl_ctx)->jlgensym_sym)
return jl_box_gensym(numval(car_(cdr_(e))));
else if (hd == jl_ast_ctx(fl_ctx)->slot_sym) {
jl_value_t *slotnum = jl_box_long(numval(car_(cdr_(e))));
JL_GC_PUSH1(&slotnum);
jl_value_t *res = jl_new_struct(jl_slot_type, slotnum, jl_any_type);
JL_GC_POP();
return res;
}
else if (hd == jl_ast_ctx(fl_ctx)->slot_sym)
return jl_box_slotnumber(numval(car_(cdr_(e))));
else if (hd == jl_ast_ctx(fl_ctx)->null_sym && llength(e) == 1)
return jl_nothing;
}
Expand Down Expand Up @@ -677,7 +672,7 @@ static value_t julia_to_scm_(fl_context_t *fl_ctx, jl_value_t *v)
// GC Note: jl_fieldref(v, 0) allocate for LabelNode, GotoNode
// but we don't need a GC root here because julia_to_list2
// shouldn't allocate in this case.
if (jl_typeis(v, jl_slot_type))
if (jl_is_slot(v))
return julia_to_list2(fl_ctx, (jl_value_t*)slot_sym, jl_fieldref(v,0));
if (jl_typeis(v, jl_labelnode_type))
return julia_to_list2(fl_ctx, (jl_value_t*)label_sym, jl_fieldref(v,0));
Expand Down
4 changes: 3 additions & 1 deletion src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -1140,7 +1140,9 @@ void jl_init_primitives(void)
add_builtin("TypeMapLevel", (jl_value_t*)jl_typemap_level_type);
add_builtin("Symbol", (jl_value_t*)jl_sym_type);
add_builtin("GenSym", (jl_value_t*)jl_gensym_type);
add_builtin("Slot", (jl_value_t*)jl_slot_type);
add_builtin("Slot", (jl_value_t*)jl_abstractslot_type);
add_builtin("SlotNumber", (jl_value_t*)jl_slotnumber_type);
add_builtin("TypedSlot", (jl_value_t*)jl_typedslot_type);
add_builtin("IntrinsicFunction", (jl_value_t*)jl_intrinsic_type);
add_builtin("Function", (jl_value_t*)jl_function_type);
add_builtin("Builtin", (jl_value_t*)jl_builtin_type);
Expand Down
12 changes: 9 additions & 3 deletions src/cgutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -857,8 +857,14 @@ static jl_value_t *expr_type(jl_value_t *e, jl_codectx_t *ctx)
jl_array_t *gensym_types = (jl_array_t*)ctx->linfo->gensymtypes;
return jl_cellref(gensym_types, idx);
}
if (jl_typeis(e, jl_slot_type)) {
jl_value_t *typ = jl_slot_get_type(e);
if (jl_typeis(e, jl_slotnumber_type)) {
jl_array_t *slot_types = (jl_array_t*)ctx->linfo->slottypes;
if (!jl_is_array(slot_types))
return (jl_value_t*)jl_any_type;
return jl_cellref(slot_types, jl_slot_number(e)-1);
}
if (jl_typeis(e, jl_typedslot_type)) {
jl_value_t *typ = jl_typedslot_get_type(e);
if (jl_is_typevar(typ))
typ = ((jl_tvar_t*)typ)->ub;
return typ;
Expand Down Expand Up @@ -1558,7 +1564,7 @@ static void emit_setfield(jl_datatype_t *sty, const jl_cgval_t &strct, size_t id

static bool might_need_root(jl_value_t *ex)
{
return (!jl_is_symbol(ex) && !jl_typeis(ex, jl_slot_type) && !jl_is_gensym(ex) &&
return (!jl_is_symbol(ex) && !jl_is_slot(ex) && !jl_is_gensym(ex) &&
!jl_is_bool(ex) && !jl_is_quotenode(ex) && !jl_is_byte_string(ex) &&
!jl_is_globalref(ex));
}
Expand Down
4 changes: 2 additions & 2 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1521,7 +1521,7 @@ jl_value_t *jl_static_eval(jl_value_t *ex, void *ctx_, jl_module_t *mod,
return jl_get_global(mod, sym);
return NULL;
}
if (jl_typeis(ex,jl_slot_type))
if (jl_is_slot(ex))
return NULL;
if (jl_is_gensym(ex)) {
ssize_t idx = ((jl_gensym_t*)ex)->id;
Expand Down Expand Up @@ -2889,7 +2889,7 @@ static jl_cgval_t emit_local(jl_value_t *slotload, jl_codectx_t *ctx)
jl_value_t *typ;
if (ctx->linfo->inferred) {
// use the better type from inference for this load
typ = jl_slot_get_type(slotload);
typ = expr_type(slotload, ctx);
if (jl_is_typevar(typ))
typ = ((jl_tvar_t*)typ)->ub;
}
Expand Down
Loading

0 comments on commit 50a06e3

Please sign in to comment.