From 4b04917fd61f82390d18b14e2ea1cdd09cd61550 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Sun, 6 Mar 2016 23:15:17 -0500 Subject: [PATCH 1/6] make label ids equal their indexes this avoids needing to repeatedly look up labels when interpreting or analyzing code. --- base/inference.jl | 50 +++++++++++++++++++++++++------------------- src/interpreter.c | 18 +++------------- src/julia-syntax.scm | 42 +++++++++++++++++++++++++++++++------ 3 files changed, 68 insertions(+), 42 deletions(-) diff --git a/base/inference.jl b/base/inference.jl index 9a76eb1e21eb5..96c09f31d0715 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -36,7 +36,6 @@ type InferenceState # info on the state of inference and the linfo linfo::LambdaInfo args::Vector{Any} - labels::Vector{Int} stmt_types::Vector{Any} # return type bestguess #::Type @@ -81,14 +80,6 @@ type InferenceState sp = sparams end - labels = zeros(Int, nl) - for i = 1:length(body) - b = body[i] - if isa(b,LabelNode) - labels[b.label + 1] = i - end - end - n = length(body) s = Any[ () for i=1:n ] # initial types @@ -156,7 +147,7 @@ type InferenceState frame = new( atypes, ast, body, sp, vars, gensym_types, vinflist, nl, Dict{GenSym, Bool}(), linfo.module, 0, false, - linfo, args, labels, s, Union{}, W, n, + linfo, args, s, Union{}, W, n, cur_hand, handler_at, n_handlers, gensym_uses, gensym_init, ObjectIdDict(), #Dict{InferenceState, Vector{LineNum}}(), @@ -1474,14 +1465,6 @@ end #### helper functions for typeinf initialization and looping #### -function findlabel(labels, l) - i = l+1 > length(labels) ? 0 : labels[l+1] - if i == 0 - error("label ",l," not found") - end - return i -end - function label_counter(body) l = -1 for b in body @@ -1787,13 +1770,13 @@ function typeinf_frame(frame) end end elseif isa(stmt, GotoNode) - pc´ = findlabel(frame.labels, (stmt::GotoNode).label) + pc´ = (stmt::GotoNode).label elseif isa(stmt, Expr) stmt = stmt::Expr hd = stmt.head if is(hd, :gotoifnot) condexpr = stmt.args[1] - l = findlabel(frame.labels, stmt.args[2]::Int) + l = stmt.args[2]::Int # constant conditions if is(condexpr, true) elseif is(condexpr, false) @@ -1842,7 +1825,7 @@ function typeinf_frame(frame) unmark_fixedpoint(frame) end elseif is(hd, :enter) - l = findlabel(frame.labels, stmt.args[1]::Int) + l = stmt.args[1]::Int frame.cur_hand = (l, frame.cur_hand) # propagate type info to exception handler l = frame.cur_hand[1] @@ -1997,6 +1980,7 @@ function finish(me::InferenceState) end alloc_elim_pass(fulltree, me) getfield_elim_pass(fulltree.args[3], me) + reindex_labels!(fulltree.args[3], me) end # finalize and record the linfo result @@ -3576,6 +3560,30 @@ function replace_getfield!(ast, e::Expr, tupname, vals, field_names, sv, i0) end end +# fix label numbers to always equal the statement index of the label +function reindex_labels!(e, sv) + mapping = zeros(Int, sv.label_counter) + for i = 1:length(e.args) + el = e.args[i] + if isa(el,LabelNode) + mapping[el.label] = i + e.args[i] = LabelNode(i) + end + end + for i = 1:length(e.args) + el = e.args[i] + if isa(el,GotoNode) + e.args[i] = GotoNode(mapping[el.label]) + elseif isa(el,Expr) + if el.head === :gotoifnot + el.args[2] = mapping[el.args[2]] + elseif el.head === :enter + el.args[1] = mapping[el.args[1]] + end + end + end +end + #tfunc(f,t) = methods(f,t)[1].func.code.tfunc diff --git a/src/interpreter.c b/src/interpreter.c index af2dc188af829..bfb873f9b9c21 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -453,18 +453,6 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, size_t nl, size_t ng return (jl_value_t*)jl_nothing; } -static int label_idx(long ltgt, jl_array_t *stmts) -{ - size_t j; - for(j=0; j < stmts->nrows; j++) { - jl_value_t *l = jl_cellref(stmts,j); - if (jl_is_labelnode(l) && jl_labelnode_label(l)==ltgt) - break; - } - assert(j < stmts->nrows); - return j; -} - jl_value_t *jl_toplevel_eval_body(jl_array_t *stmts) { ssize_t ngensym = 0; @@ -495,7 +483,7 @@ static jl_value_t *eval_body(jl_array_t *stmts, jl_value_t **locals, size_t nl, jl_error("`body` expression must terminate in `return`. Use `block` instead."); jl_value_t *stmt = jl_cellref(stmts,i); if (jl_is_gotonode(stmt)) { - i = label_idx(jl_gotonode_label(stmt), stmts); + i = jl_gotonode_label(stmt)-1; continue; } if (jl_is_expr(stmt)) { @@ -503,7 +491,7 @@ static jl_value_t *eval_body(jl_array_t *stmts, jl_value_t **locals, size_t nl, if (head == goto_ifnot_sym) { jl_value_t *cond = eval(jl_exprarg(stmt,0), locals, nl, ngensym); if (cond == jl_false) { - i = label_idx(jl_unbox_long(jl_exprarg(stmt, 1)), stmts); + i = jl_unbox_long(jl_exprarg(stmt, 1))-1; continue; } else if (cond != jl_true) { @@ -528,7 +516,7 @@ static jl_value_t *eval_body(jl_array_t *stmts, jl_value_t **locals, size_t nl, if (jl_exception_in_transit == jl_stackovf_exception) _resetstkoflw(); #endif - i = label_idx(jl_unbox_long(jl_exprarg(stmt,0)), stmts); + i = jl_unbox_long(jl_exprarg(stmt,0))-1; continue; } } diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index c0b6e4dd8a5b6..be2b4469844b6 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -2731,7 +2731,7 @@ f(x) = yt(x) (else (let* ((exprs (lift-toplevel (convert-lambda lam2 '|#anon| #t '()))) (top-stmts (cdr exprs)) - (newlam (renumber-jlgensym (linearize (car exprs)))) + (newlam (renumber-things (renumber-jlgensym (linearize (car exprs))))) (vi (lam:vinfo newlam)) ;; insert `list` expression heads to make the lambda vinfo ;; lists quotable @@ -3225,14 +3225,44 @@ f(x) = yt(x) (define (renumber-jlgensym e) (renumber-jlgensym- e #f error)) +(define (label-to-idx-map body) + (let ((tbl (table))) + (let loop ((stmts (cdr body)) + (i 1)) + (if (pair? stmts) + (let ((el (car stmts))) + (if (and (pair? el) (eq? (car el) 'label)) + (put! tbl (cadr el) i)) + (loop (cdr stmts) (+ i 1))))) + tbl)) + +(define (renumber-labels! body label2idx) + (let loop ((stmts (cdr body))) + (if (pair? stmts) + (let ((el (car stmts))) + (if (pair? el) + (case (car el) + ((label goto enter) (set-car! (cdr el) (get label2idx (cadr el)))) + ((gotoifnot) (set-car! (cddr el) (get label2idx (caddr el)))) + (else #f))) + (loop (cdr stmts)))))) + +(define (renumber-things ex) + (if (atom? ex) ex + (begin (if (eq? (car ex) 'lambda) + (renumber-labels! (lam:body ex) (label-to-idx-map (lam:body ex)))) + (for-each renumber-things (cdr ex)))) + ex) + ;; expander entry point (define (julia-expand1 ex) - (renumber-jlgensym - (linearize - (closure-convert - (analyze-variables! - (resolve-scopes ex)))))) + (renumber-things + (renumber-jlgensym + (linearize + (closure-convert + (analyze-variables! + (resolve-scopes ex))))))) (define julia-expand0 expand-forms) From 0547ab4498395d95018da124dcd2a24aa96d019b Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 7 Mar 2016 03:37:06 -0500 Subject: [PATCH 2/6] replace local vars with (slot n) wrap static parameters with (static-parameter ) --- base/boot.jl | 14 +- base/expr.jl | 6 +- base/hashing.jl | 2 +- base/inference.jl | 739 +++++++++++++++---------------------------- base/serialize.jl | 6 +- base/show.jl | 6 +- src/alloc.c | 76 +++-- src/ast.c | 43 +-- src/builtins.c | 5 +- src/ccall.cpp | 4 +- src/cgutils.cpp | 61 ++-- src/codegen.cpp | 363 ++++++++++----------- src/dump.c | 10 +- src/init.c | 1 - src/interpreter.c | 209 +++++------- src/jltypes.c | 19 +- src/julia-syntax.scm | 52 ++- src/julia.h | 20 +- src/julia_internal.h | 3 - test/hashing.jl | 6 +- test/inline.jl | 12 +- 21 files changed, 672 insertions(+), 985 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index cb0c658693493..00a2064735897 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -139,8 +139,8 @@ export InterruptException, OutOfMemoryError, ReadOnlyMemoryError, OverflowError, StackOverflowError, SegmentationFault, UndefRefError, UndefVarError, TypeError, # AST representation - Expr, GotoNode, LabelNode, LineNumberNode, QuoteNode, SymbolNode, TopNode, - GlobalRef, NewvarNode, GenSym, + Expr, GotoNode, LabelNode, LineNumberNode, QuoteNode, TopNode, + GlobalRef, NewvarNode, GenSym, Slot, # object model functions fieldtype, getfield, setfield!, nfields, throw, tuple, is, ===, isdefined, eval, # sizeof # not exported, to avoid conflicting with Base.sizeof @@ -217,12 +217,6 @@ type TypeError <: Exception got end -type SymbolNode - name::Symbol - typ - SymbolNode(name::Symbol, t::ANY) = new(name, t) -end - abstract DirectIndexString <: AbstractString immutable ASCIIString <: DirectIndexString @@ -283,11 +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, :Symbol) +_new(:NewvarNode, :Slot) _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)))) Module(name::Symbol=:anonymous, std_imports::Bool=true) = ccall(:jl_f_new_module, Any, (Any, Bool), name, std_imports)::Module diff --git a/base/expr.jl b/base/expr.jl index bf5f522786726..875bf5b56f965 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -35,16 +35,16 @@ copy(e::Expr) = (n = Expr(e.head); n.args = astcopy(e.args); n.typ = e.typ; n) -copy(s::SymbolNode) = SymbolNode(s.name, s.typ) +copy(s::Slot) = Slot(s.id, s.typ) # copy parts of an AST that the compiler mutates -astcopy(x::Union{SymbolNode,Expr}) = copy(x) +astcopy(x::Union{Slot,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 && x.args == y.args ==(x::QuoteNode, y::QuoteNode) = x.value == y.value -==(x::SymbolNode, y::SymbolNode) = x.name === y.name && x.typ === y.typ +==(x::Slot, y::Slot) = x.id === y.id && x.typ === y.typ expand(x) = ccall(:jl_expand, Any, (Any,), x) macroexpand(x) = ccall(:jl_macroexpand, Any, (Any,), x) diff --git a/base/hashing.jl b/base/hashing.jl index cc5d6be6007c1..0ea43d5fce927 100644 --- a/base/hashing.jl +++ b/base/hashing.jl @@ -63,7 +63,7 @@ else end hash(x::QuoteNode, h::UInt) = hash(x.value, hash(QuoteNode, h)) -hash(x::SymbolNode, h::UInt) = hash(x.name, hash(x.typ, hash(SymbolNode, 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 diff --git a/base/inference.jl b/base/inference.jl index 96c09f31d0715..199e78976f3b8 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -11,8 +11,7 @@ const MAX_TUPLE_DEPTH = 4 immutable NotFound end const NF = NotFound() typealias LineNum Int -typealias VarTable ObjectIdDict - +typealias VarTable Array{Any,1} type VarState typ @@ -24,7 +23,6 @@ type InferenceState ast #::Expr body::Array{Any,1} # ast body sp::SimpleVector # static parameters - vars::Array{Any,1} # names of args and locals gensym_types::Array{Any,1} # types of the GenSym's in this function vinfo::Array{Any,1} # variable properties label_counter::Int # index of the current highest label for this function @@ -35,7 +33,7 @@ type InferenceState # info on the state of inference and the linfo linfo::LambdaInfo - args::Vector{Any} + nargs::Int stmt_types::Vector{Any} # return type bestguess #::Type @@ -68,7 +66,7 @@ type InferenceState end assert(is(ast.head,:lambda)) vinflist = ast.args[2][1]::Array{Any,1} - vars = map(vi->vi[1], vinflist) + nslots = length(vinflist) body = (ast.args[3].args)::Array{Any,1} nl = label_counter(body)+1 @@ -83,13 +81,9 @@ type InferenceState n = length(body) s = Any[ () for i=1:n ] # initial types - s[1] = ObjectIdDict() - for v in vars - s[1][v] = VarState(Bottom,true) - end + s[1] = Any[ VarState(Bottom,true) for i=1:nslots ] - args = f_argnames(ast) - la = length(args) + la = length(ast.args[1]) if la > 0 lastarg = ast.args[1][la] if is_rest_arg(lastarg) @@ -97,9 +91,9 @@ type InferenceState if la > 1 atypes = Tuple{Any[Any for i=1:la-1]..., Tuple.parameters[1]} end - s[1][args[la]] = VarState(Tuple,false) + s[1][la] = VarState(Tuple,false) else - s[1][args[la]] = VarState(limit_tuple_depth(tupletype_tail(atypes,la)),false) + s[1][la] = VarState(limit_tuple_depth(tupletype_tail(atypes,la)),false) end la -= 1 end @@ -123,10 +117,10 @@ type InferenceState if isa(atyp, TypeVar) atyp = atyp.ub end - s[1][args[i]] = VarState(atyp, false) + s[1][i] = VarState(atyp, false) end for i=laty+1:la - s[1][args[i]] = VarState(lastatype, false) + s[1][i] = VarState(lastatype, false) end else @assert la == 0 # wrong number of arguments @@ -145,9 +139,9 @@ type InferenceState push!(W, 1) #initial pc to visit frame = new( - atypes, ast, body, sp, vars, gensym_types, vinflist, nl, Dict{GenSym, Bool}(), linfo.module, 0, false, + atypes, ast, body, sp, gensym_types, vinflist, nl, Dict{GenSym, Bool}(), linfo.module, 0, false, - linfo, args, s, Union{}, W, n, + linfo, la, s, Union{}, W, n, cur_hand, handler_at, n_handlers, gensym_uses, gensym_init, ObjectIdDict(), #Dict{InferenceState, Vector{LineNum}}(), @@ -176,16 +170,6 @@ function _any(f::ANY, a) return false end -function is_static_parameter(sv::InferenceState, s::Symbol) - sp = sv.linfo.sparam_syms - for i=1:length(sp) - if is(sp[i],s) - return true - end - end - return false -end - function contains_is(itr, x::ANY) for y in itr if is(y,x) @@ -195,10 +179,6 @@ function contains_is(itr, x::ANY) return false end -is_local(sv::InferenceState, s::GenSym) = true -is_local(sv::InferenceState, s::Symbol) = contains_is(sv.vars, s) -is_global(sv::InferenceState, s::Symbol) = !is_local(sv,s) && !is_static_parameter(sv,s) - function _iisconst(s::Symbol, sv) m = sv.mod isdefined(m,s) && (ccall(:jl_is_const, Int32, (Any, Any), m, s) != 0) @@ -549,24 +529,18 @@ const apply_type_tfunc = function (A::ANY, args...) if val !== Bottom push!(tparams, val) continue - elseif isa(A[i],Symbol) - sp = global_sv.linfo.sparam_syms - s = A[i] + elseif isa(A[i],Expr) && A[i].head === :static_parameter + n = A[i].args[1] + sp = global_sv.sp found = false - for j=1:length(sp) - if is(sp[j],s) - # static parameter - val = global_sv.sp[j] - if valid_tparam(val) - push!(tparams, val) - found = true - break - end + if n <= length(sp) + val = sp[n] + if valid_tparam(val) + push!(tparams, val) + found = true end end - if found - continue - end + found && continue end end if !istuple && i-1 > length(headtype.parameters) @@ -704,13 +678,11 @@ function isconstantref(f::ANY, sv::InferenceState) end if isa(f,QuoteNode) return f - elseif isa(f,GenSym) + elseif isa(f,GenSym) || isa(f,Slot) return false - elseif isa(f,SymbolNode) - f = f.name end if isa(f,Symbol) - return is_global(sv, f) && _iisconst(f, sv) && f + return _iisconst(f, sv) && f elseif !isa(f,Expr) return f end @@ -1189,14 +1161,14 @@ function abstract_eval(e::ANY, vtypes::VarTable, sv::InferenceState) if isa(e,QuoteNode) v = (e::QuoteNode).value return type_typeof(v) + elseif isa(e,GenSym) + return abstract_eval_gensym(e::GenSym, sv) + elseif isa(e,Slot) + return vtypes[e.id].typ elseif isa(e,TopNode) return abstract_eval_global(_topmod(sv), (e::TopNode).name) elseif isa(e,Symbol) return abstract_eval_symbol(e::Symbol, vtypes, sv) - elseif isa(e,SymbolNode) - return abstract_eval_symbol((e::SymbolNode).name, vtypes, sv) - elseif isa(e,GenSym) - return abstract_eval_gensym(e::GenSym, sv) elseif isa(e,GlobalRef) return abstract_eval_global(e.mod, e.name) end @@ -1224,6 +1196,22 @@ function abstract_eval(e::ANY, vtypes::VarTable, sv::InferenceState) elseif is(e.head,:&) abstract_eval(e.args[1], vtypes, sv) t = Any + elseif is(e.head, :static_parameter) + n = e.args[1] + t = Any + if n <= length(sv.sp) + val = sv.sp[n] + if isa(val,TypeVar) + # static param bound to typevar + # if the tvar does not refer to anything more specific than Any, + # the static param might actually be an integer, symbol, etc. + if !(Any <: val.ub) + t = Type{val} + end + else + t = abstract_eval_constant(val) + end + end elseif is(e.head,:static_typeof) var = e.args[1] t = abstract_eval(var, vtypes, sv) @@ -1305,53 +1293,19 @@ function abstract_eval_gensym(s::GenSym, sv::InferenceState) return typ end -function abstract_eval_symbol(s::Symbol, vtypes::ObjectIdDict, sv::InferenceState) - t = get(vtypes,s,NF) - if is(t,NF) - sp = sv.linfo.sparam_syms - for i=1:length(sp) - if is(sp[i],s) - # static parameter - val = sv.sp[i] - if isa(val,TypeVar) - # static param bound to typevar - if Any <: val.ub - # if the tvar does not refer to anything more specific - # than Any, the static param might actually be an - # integer, symbol, etc. - return Any - end - return Type{val} - end - return abstract_eval_constant(val) - end - end - if s in sv.vars - # local variable use not reached - return Bottom - end - # global - return abstract_eval_global(sv.mod, s) - end - return t.typ +function abstract_eval_symbol(s::Symbol, vtypes, sv::InferenceState) + return abstract_eval_global(sv.mod, s) end #### handling for statement-position expressions #### type StateUpdate - var::Union{Symbol,GenSym} + var::Union{Slot,GenSym} vtype state::VarTable end -function getindex(x::StateUpdate, s::Symbol) - if is(x.var,s) - return x.vtype - end - return get(x.state,s,NF) -end - function abstract_interpret(e::ANY, vtypes::VarTable, sv::InferenceState) !isa(e,Expr) && return vtypes # handle assignment @@ -1359,10 +1313,7 @@ function abstract_interpret(e::ANY, vtypes::VarTable, sv::InferenceState) t = abstract_eval(e.args[2], vtypes, sv) t === Bottom && return () lhs = e.args[1] - if isa(lhs,SymbolNode) - lhs = lhs.name - end - if isa(lhs,Symbol) || isa(lhs,GenSym) + if isa(lhs,Slot) || isa(lhs,GenSym) # don't bother for GlobalRef return StateUpdate(lhs, VarState(t,false), vtypes) end @@ -1374,7 +1325,7 @@ function abstract_interpret(e::ANY, vtypes::VarTable, sv::InferenceState) t === Bottom && return () elseif is(e.head,:method) fname = e.args[1] - if isa(fname,Symbol) + if isa(fname,Slot) return StateUpdate(fname, VarState(Any,false), vtypes) end end @@ -1435,33 +1386,68 @@ end tchanged(n::ANY, o::ANY) = is(o,NF) || (!is(n,NF) && !(n <: o)) schanged(n::ANY, o::ANY) = is(o,NF) || (!is(n,NF) && !issubstate(n, o)) -stupdate(state::Tuple{}, changes::VarTable, vars) = copy(changes) -stupdate(state::Tuple{}, changes::StateUpdate, vars) = stupdate(ObjectIdDict(), changes, vars) +function stupdate!(state::Tuple{}, changes::StateUpdate) + newst = copy(changes.state) + if isa(changes.var, Slot) + newst[changes.var.id] = changes.vtype + end + newst +end -function stupdate(state::VarTable, changes::Union{StateUpdate,VarTable}, vars) - for i = 1:length(vars) - v = vars[i] - newtype = changes[v] - oldtype = get(state::ObjectIdDict,v,NF) +function stupdate!(state::VarTable, change::StateUpdate) + for i = 1:length(state) + if isa(change.var,Slot) && i == change.var.id + newtype = change.vtype + else + newtype = change.state[i] + end + oldtype = state[i] if schanged(newtype, oldtype) - state[v] = smerge(oldtype, newtype) + state[i] = smerge(oldtype, newtype) end end - state + return state end -function stchanged(new::Union{StateUpdate,VarTable}, old, vars) - if is(old,()) - return true - end - for v in vars - if schanged(new[v], get(old,v,NF)) - return true +stupdate!(state::Tuple{}, changes::VarTable) = copy(changes) + +function stupdate!(state::VarTable, changes::VarTable) + for i = 1:length(state) + newtype = changes[i] + oldtype = state[i] + if schanged(newtype, oldtype) + state[i] = smerge(oldtype, newtype) end end + return state +end + +stchanged(new::Tuple{}, old::Tuple{}) = false +stchanged(new, old::Tuple{}) = true + +function stchanged(new::VarTable, old::VarTable) + is(old,()) && return true + for i = 1:length(new) + newtype = new[i] + oldtype = old[i] + schanged(newtype, oldtype) && return true + end return false end +function stchanged(new::StateUpdate, old::VarTable) + is(old,()) && return true + for i = 1:length(new.state) + if isa(new.var,Slot) && i == new.var.id + newtype = new.vtype + else + newtype = new.state[i] + end + oldtype = old[i] + schanged(newtype, oldtype) && return true + end + return false +end #### helper functions for typeinf initialization and looping #### @@ -1518,9 +1504,6 @@ function newvar!(sv::InferenceState, typ) return GenSym(id) end -f_argnames(ast) = - Any[(isa(x,Expr) ? x.args[1] : x) for x in ast.args[1]::Array{Any,1}] - is_rest_arg(arg::ANY) = (ccall(:jl_is_rest_arg,Int32,(Any,), arg) != 0) @@ -1558,6 +1541,9 @@ function typeinf_edge(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector, nee if !needtree return (nothing, code, true) end + elseif !isa(code,Tuple) && !needtree + # TODO: temporary hack + return (nothing, code.bestguess, false) else inftree, inftyp = code if linfo.inInference @@ -1565,6 +1551,11 @@ function typeinf_edge(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector, nee linfo.ast = inftree linfo.rettype = inftyp linfo.inInference = false + if !isa(inftree,Expr) + inftree = ccall(:jl_uncompress_ast, Any, (Any,Any), linfo, inftree) + end + linfo.nslots = length(inftree.args[2][1]) + linfo.ngensym = length(inftree.args[2][3]) end return (inftree, inftyp, true) # code is a tuple (ast, type) end @@ -1738,7 +1729,7 @@ function typeinf_frame(frame) frame.cur_hand = frame.handler_at[pc] end stmt = frame.body[pc] - changes = abstract_interpret(stmt, s[pc]::ObjectIdDict, frame) + changes = abstract_interpret(stmt, s[pc]::Array{Any,1}, frame) if changes === () # if there was a Expr(:static_typeof) on this line, # need to continue to the next pc even though the return type was Bottom @@ -1749,9 +1740,9 @@ function typeinf_frame(frame) if frame.cur_hand !== () # propagate type info to exception handler l = frame.cur_hand[1] - if stchanged(changes, s[l], frame.vars) + if stchanged(changes, s[l]) push!(W, l) - s[l] = stupdate(s[l], changes, frame.vars) + s[l] = stupdate!(s[l], changes) end end pc´ = pc+1 @@ -1784,10 +1775,10 @@ function typeinf_frame(frame) else # general case frame.handler_at[l] = frame.cur_hand - if stchanged(changes, s[l], frame.vars) + if stchanged(changes, s[l]) # add else branch to active IP list push!(W, l) - s[l] = stupdate(s[l], changes, frame.vars) + s[l] = stupdate!(s[l], changes) end end elseif is(hd, :type_goto) @@ -1830,10 +1821,10 @@ function typeinf_frame(frame) # propagate type info to exception handler l = frame.cur_hand[1] old = s[l] - new = s[pc]::ObjectIdDict - if old === () || stchanged(new, old::ObjectIdDict, frame.vars) + new = s[pc]::Array{Any,1} + if old === () || stchanged(new, old::Array{Any,1}) push!(W, l) - s[l] = stupdate(old, new, frame.vars) + s[l] = stupdate!(old, new) end # if frame.handler_at[l] === 0 # frame.n_handlers += 1 @@ -1853,8 +1844,8 @@ function typeinf_frame(frame) end end if pc´<=n && (frame.handler_at[pc´] = frame.cur_hand; true) && - stchanged(changes, s[pc´], frame.vars) - s[pc´] = stupdate(s[pc´], changes, frame.vars) + stchanged(changes, s[pc´]) + s[pc´] = stupdate!(s[pc´], changes) pc = pc´ elseif pc´ in W pc = pc´ @@ -1962,7 +1953,7 @@ function finish(me::InferenceState) me.gensym_types[i] = Union{} end end - fulltree = type_annotate(me.ast, me.stmt_types, me, me.bestguess, me.args) + fulltree = type_annotate(me.ast, me.stmt_types, me, me.bestguess, me.nargs) # make sure (meta pure) is stripped from full tree @assert fulltree.args[3].head === :body @@ -1974,8 +1965,6 @@ function finish(me::InferenceState) if me.optimize if JLOptions().can_inline == 1 fulltree.args[3] = inlining_pass(fulltree.args[3], me, fulltree)[1] - # inlining can add variables - me.vars = append_any(f_argnames(fulltree), map(vi->vi[1], fulltree.args[2][1])) inbounds_meta_elim_pass(fulltree.args[3]) end alloc_elim_pass(fulltree, me) @@ -1994,6 +1983,8 @@ function finish(me::InferenceState) me.linfo.ast = compressedtree me.linfo.rettype = me.bestguess me.linfo.inInference = false + me.linfo.nslots = length(fulltree.args[2][1]) + me.linfo.ngensym = length(me.gensym_types) end if me.tfunc_idx != -1 me.linfo.def.tfunc[me.tfunc_idx + 1] = (compressedtree, me.bestguess) @@ -2012,48 +2003,27 @@ function finish(me::InferenceState) nothing end -function record_var_type(e::Symbol, t::ANY, decls) - otherTy = get(decls::ObjectIdDict, e, false) +function record_var_type(s::Slot, t::ANY, decls) + otherTy = decls[s.id] # keep track of whether a variable is always the same type - if !is(otherTy,false) + if !is(otherTy,NF) if !typeseq(otherTy, t) - decls[e] = Any + decls[s.id] = Any end else - decls[e] = t + decls[s.id] = t end end function eval_annotate(e::ANY, vtypes::ANY, sv::InferenceState, decls, undefs) - if isa(e, Symbol) - e = e::Symbol - - if !is_local(sv, e) - # can get types of globals and static params from the environment - return e - end + if isa(e, Slot) t = abstract_eval(e, vtypes, sv) - s = get(vtypes, e, NF) - if s !== NF && s.undef - undefs[e] = true + s = vtypes[e.id] + if s.undef + undefs[e.id] = true end record_var_type(e, t, decls) - return (is(t,Any) || is(t,IntrinsicFunction)) ? e : SymbolNode(e, t) - end - - if isa(e, SymbolNode) - e = e::SymbolNode - curtype = e.typ - t = abstract_eval(e.name, vtypes, sv) - s = get(vtypes, e.name, NF) - if s !== NF && s.undef - undefs[e] = true - end - if !(curtype <: t) || typeseq(curtype, t) - record_var_type(e.name, t, decls) - e.typ = t - end - return e + return t === e.typ ? e : Slot(e.id, t) end if !isa(e,Expr) @@ -2071,12 +2041,8 @@ function eval_annotate(e::ANY, vtypes::ANY, sv::InferenceState, decls, undefs) 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. - if isa(s,SymbolNode) - # we don't use types on assignment LHS - s = s.name - end e.args[2] = eval_annotate(e.args[2], vtypes, sv, decls, undefs) - if isa(s,Symbol) + 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) @@ -2096,12 +2062,13 @@ function eval_annotate(e::ANY, vtypes::ANY, sv::InferenceState, decls, undefs) end # annotate types of all symbols in AST -function type_annotate(ast::Expr, states::Array{Any,1}, sv::ANY, rettype::ANY, args) - decls = ObjectIdDict() - undefs = ObjectIdDict() +function type_annotate(ast::Expr, 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 arg in args - decls[arg] = states[1][arg].typ + for i = 1:nargs + decls[i] = states[1][i].typ end body = ast.args[3].args::Array{Any,1} for i=1:length(body) @@ -2114,15 +2081,12 @@ function type_annotate(ast::Expr, states::Array{Any,1}, sv::ANY, rettype::ANY, a ast.args[3].typ = rettype # add declarations for variables that are always the same type - for vi in ast.args[2][1]::Array{Any,1} - vi[2] = get(decls, vi[1], vi[2]) - if haskey(undefs, vi[1]) - vi[3] |= 32 + for i = 1:nslots + vi = ast.args[2][1][i] + if decls[i] !== NF + vi[2] = decls[i] end - end - for vi in ast.args[2][2]::Array{Any,1} - vi[2] = get(decls, vi[1], vi[2]) - if haskey(undefs, vi[1]) + if undefs[i] vi[3] |= 32 end end @@ -2131,65 +2095,31 @@ function type_annotate(ast::Expr, states::Array{Any,1}, sv::ANY, rettype::ANY, a return ast end -function sym_replace(e::ANY, from1, from2, to1, to2) - if isa(e,Symbol) || isa(e,GenSym) - return _sym_repl(e::Union{Symbol,GenSym}, from1, from2, to1, to2, e) - end - if isa(e,SymbolNode) - e2 = _sym_repl(e.name, from1, from2, to1, to2, e) - if isa(e2, SymbolNode) || !isa(e2, Symbol) - return e2 - else - return SymbolNode(e2, e.typ) +# replace slots 1:na with argexprs, static params with spvals, and increment +# other slots by offset. +function substitute!(e::ANY, na, argexprs, spvals, offset) + if isa(e, Slot) + if 1 <= e.id <= na + return argexprs[e.id] end + return Slot(e.id+offset, e.typ) end if isa(e,NewvarNode) - e2 = _sym_repl(e.name::Symbol, from1, from2, to1, to2, e) - if isa(e2, NewvarNode) || !isa(e2, Symbol) - return e2 - else - return NewvarNode(e2) - end - end - if !isa(e,Expr) - return e + return NewvarNode(substitute!(e.slot, na, argexprs, spvals, offset)) end - e = e::Expr - if e.head === :(=) - s = e.args[1] - if isa(s, Symbol) || isa(s, GenSym) - e2 = _sym_repl(s, from1, from2, to1, to2, s) - # remove_redundant_temp_vars can only handle Symbols - # on the LHS of assignments, so we make sure not to put - # something else there - if isa(e2, SymbolNode) - e2 = e2.name + if isa(e,Expr) + e = e::Expr + if e.head === :static_parameter + return spvals[e.args[1]] + elseif e.head !== :line + for i=1:length(e.args) + e.args[i] = substitute!(e.args[i], na, argexprs, spvals, offset) end - e.args[1] = e2::Union{Symbol,GenSym} - end - e.args[2] = sym_replace(e.args[2], from1, from2, to1, to2) - elseif e.head !== :line - for i=1:length(e.args) - e.args[i] = sym_replace(e.args[i], from1, from2, to1, to2) end end return e end -function _sym_repl(s::Union{Symbol,GenSym}, from1, from2, to1, to2, deflt) - for i=1:length(from1) - if is(from1[i],s) - return to1[i] - end - end - for i=1:length(from2) - if is(from2[i],s) - return to2[i] - end - end - return deflt -end - # count occurrences up to n+1 function occurs_more(e::ANY, pred, n) if isa(e,Expr) @@ -2204,28 +2134,23 @@ function occurs_more(e::ANY, pred, n) end return c end - if pred(e) || (isa(e,SymbolNode) && pred(e.name)) + if pred(e) return 1 end return 0 end -const emptydict = ObjectIdDict() - function exprtype(x::ANY, sv::InferenceState) if isa(x,Expr) return (x::Expr).typ - elseif isa(x,SymbolNode) - return (x::SymbolNode).typ + elseif isa(x,Slot) + return (x::Slot).typ elseif isa(x,GenSym) return abstract_eval_gensym(x::GenSym, sv) elseif isa(x,TopNode) return abstract_eval_global(_topmod(sv), (x::TopNode).name) elseif isa(x,Symbol) - if is_local(sv, x::Symbol) - return Any - end - return abstract_eval(x::Symbol, emptydict, sv) + return abstract_eval_global(sv.mod, x::Symbol) elseif isa(x,QuoteNode) v = (x::QuoteNode).value return type_typeof(v) @@ -2266,19 +2191,11 @@ end # and some affect-free calls (allow_volatile=false) -- affect_free means the call # cannot be affected by previous calls, except assignment nodes function effect_free(e::ANY, sv, allow_volatile::Bool) - if isa(e,SymbolNode) - allow_volatile && return true - if is_global(sv, (e::SymbolNode).name) - return false - end + if isa(e,Slot) return true end if isa(e,Symbol) - allow_volatile && return true - if is_global(sv, e::Symbol) - return false - end - return true + return allow_volatile end if isa(e,Number) || isa(e,AbstractString) || isa(e,GenSym) || isa(e,TopNode) || isa(e,QuoteNode) || isa(e,Type) || isa(e,Tuple) @@ -2313,12 +2230,6 @@ function effect_free(e::ANY, sv, allow_volatile::Bool) if isa(a,Symbol) return false end - if isa(a,SymbolNode) - typ = (a::SymbolNode).typ - if !isa(typ,DataType) || typ.mutable - return false - end - end if isa(a,GenSym) typ = exprtype(a,sv) if !isa(typ,DataType) || typ.mutable @@ -2360,20 +2271,6 @@ function effect_free(e::ANY, sv, allow_volatile::Bool) return false end -function ast_localvars(ast) - args = ObjectIdDict() - for argname in (ast.args[1]::Array{Any,1}) - args[argname] = true - end - locals = Any[] - for vi in (ast.args[2][1]::Array{Any,1}) - if !haskey(args, vi[1]) - push!(locals, vi[1]) - end - end - locals -end - #### post-inference optimizations #### @@ -2477,12 +2374,11 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference end linfo = linfo::LambdaInfo - spnames = Any[s for s in linfo.sparam_syms] if length(linfo.sparam_vals) > 0 spvals = Any[x for x in linfo.sparam_vals] else spvals = Any[] - for i = 1:length(spnames) + for i = 1:length(methsp) si = methsp[i] if isa(si, TypeVar) return NF @@ -2492,7 +2388,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference end for i=1:length(spvals) si = spvals[i] - if isa(si,Symbol) || isa(si,GenSym) + if isa(si,Symbol) || isa(si,GenSym) || isa(si,Slot) spvals[i] = QuoteNode(si) end end @@ -2530,10 +2426,11 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference if is(ast,nothing) || !inferred return NF end - needcopy = true + if !isa(ast,Expr) ast = ccall(:jl_uncompress_ast, Any, (Any,Any), linfo, ast) - needcopy = false + else + ast = astcopy(ast) end ast = ast::Expr vinflist = ast.args[2][1]::Array{Any,1} @@ -2553,6 +2450,8 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference istopfunction(topmod, f, :min) || istopfunction(topmod, f, :max)) && (4 <= length(argexprs) <= 10) && methsig == Tuple{ft,Any,Any,Any,Vararg{Any}} if !inline_op && !inline_worthy(body, cost) + # TODO + #= if incompletematch # inline a typeassert-based call-site, rather than a # full generic lookup, using the inliner to handle @@ -2572,42 +2471,33 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference end body.args = Any[Expr(:return, newcall)] ast = Expr(:lambda, newnames, Any[[], locals, [], 0], body) - needcopy = false else return NF end + =# + return NF end # remove empty meta - body.args = filter(x->!(isa(x,Expr) && x.head === :meta && isempty(x.args)), - body.args) + filter!(x->!(isa(x,Expr) && x.head === :meta && isempty(x.args)), + body.args) enc_vinflist = enclosing_ast.args[2][1]::Array{Any,1} - enc_locllist = ast_localvars(enclosing_ast) - locllist = ast_localvars(ast) + na = length(ast.args[1]) # check for vararg function - args = f_argnames(ast) - na = length(args) - isva = false if na > 0 && is_rest_arg(ast.args[1][na]) if length(argexprs) < na - 1 return (Expr(:call, TopNode(:error), "too few arguments"), []) end - vaname = args[na] - len_argexprs = length(argexprs) - valen = len_argexprs-na+1 - if valen>0 && !occurs_outside_getfield(body, vaname, sv, valen, ()) + # This appears to be redundant with tuple_elim_pass +#= + if false#=TODO=# && valen>0 && !occurs_outside_getfield(body, vaname, sv, valen) # argument tuple is not used as a whole, so convert function body # to one accepting the exact number of arguments we have. newnames = unique_names(ast,valen) - if needcopy - body = astcopy(body) - needcopy = false - end - replace_getfield!(ast, body, vaname, newnames, (), sv, 1) - args = vcat(args[1:na-1], newnames) - na = length(args) + replace_getfield!(ast, body, vaname, newnames, sv, 1) + na = na-1+valen # if the argument name is also used as a local variable, # we need to keep it around as a variable name @@ -2624,11 +2514,11 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference end end else - # construct tuple-forming expression for argument tail - vararg = mk_tuplecall(argexprs[na:end], sv) - argexprs = Any[argexprs[1:(na-1)]..., vararg] - isva = true - end +=# + # construct tuple-forming expression for argument tail + vararg = mk_tuplecall(argexprs[na:end], sv) + argexprs = Any[argexprs[1:(na-1)]..., vararg] + isva = true elseif na != length(argexprs) # we have a method match only because an earlier # inference step shortened our call args list, even @@ -2639,28 +2529,6 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference @assert na == length(argexprs) - if argexprs === e.args - argexprs = copy(argexprs) - end - if needcopy - body = astcopy(body) - end - - # avoid capturing free variables in enclosing function with the same name as in our function - for localval in locllist - localval = localval::Symbol - vnew = gensym(localval) - push!(spnames, localval) - push!(spvals, vnew) - push!(enc_locllist, vnew) - for vi in vinflist - if vi[1] === localval - push!(enc_vinflist, Any[vnew, vi[2], vi[3]]) - break - end - end - end - # see if each argument occurs only once in the body expression stmts = Any[] stmts_free = true # true = all entries of stmts are effect_free @@ -2679,7 +2547,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference end for i=na:-1:1 # stmts_free needs to be calculated in reverse-argument order - args_i = args[i] + #args_i = args[i] aei = argexprs[i] aeitype = argtype = exprtype(aei,sv) needtypeassert = false @@ -2722,14 +2590,9 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference islocal = false # if the argument name is also used as a local variable, # we need to keep it as a variable name - for vi in vinflist - if vi[1] === args_i && vi[3] != 0 - islocal = true - aeitype = tmerge(aeitype, vi[2]) - if aeitype === Any - break - end - end + if vinflist[i][3] != 0 + islocal = true + aeitype = tmerge(aeitype, vinflist[i][2]) end # ok for argument to occur more than once if the actual argument @@ -2739,7 +2602,6 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference vnew1 = unique_name(enclosing_ast, ast) add_variable(enclosing_ast, vnew1, aeitype, !islocal) v1 = (aeitype===Any ? vnew1 : SymbolNode(vnew1,aeitype)) - push!(spnames, args_i) push!(spvals, v1) vnew2 = unique_name(enclosing_ast, ast) v2 = (argtype===Any ? vnew2 : SymbolNode(vnew2,argtype)) @@ -2765,7 +2627,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference for j = length(body.args):-1:1 b = body.args[j] if occ < 6 - occ += occurs_more(b, x->is(x,args_i), 6) + occ += occurs_more(b, x->(isa(x,Slot)&&x.id==i), 6) end if occ > 0 && affect_free && !effect_free(b, sv, true) #TODO: we could short-circuit this test better by memoizing effect_free(b) in the for loop over i affect_free = false @@ -2783,9 +2645,8 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference vnew = newvar!(sv, aeitype) argexprs[i] = vnew else - vnew = unique_name(enclosing_ast, ast) - add_variable(enclosing_ast, vnew, aeitype, #=SSA=#false) - argexprs[i] = aeitype===Any ? vnew : SymbolNode(vnew,aeitype) + vnew = add_slot!(enclosing_ast, aeitype, #=SSA=#false) + argexprs[i] = vnew end unshift!(stmts, Expr(:(=), vnew, aei)) stmts_free &= free @@ -2821,7 +2682,8 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference end # ok, substitute argument expressions for argument names in the body - body = sym_replace(body, args, spnames, argexprs, spvals) + body = substitute!(body, na, argexprs, spvals, length(enc_vinflist)-na) + append!(enc_vinflist, vinflist[na+1:end]) # make labels / goto statements unique newlabels = zeros(Int,label_counter(body.args)+1) @@ -2851,7 +2713,8 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference # convert return statements into a series of goto's retstmt = genlabel(sv) - retval = unique_name(enclosing_ast, ast) + rettype = (ast.args[3]::Expr).typ + local retval multiret = false lastexpr = pop!(body.args) if isa(lastexpr,LabelNode) @@ -2867,6 +2730,10 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference if isa(a,Expr) a = a::Expr if a.head === :return + if !multiret + # create slot first time + retval = add_slot!(enclosing_ast, rettype, false) + end multiret = true unshift!(a.args, retval) a.head = :(=) @@ -2876,15 +2743,13 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference end if multiret - rettype = (ast.args[3]::Expr).typ - add_variable(enclosing_ast, retval, rettype, #=SSA=#false) if lastexpr !== nothing unshift!(lastexpr.args, retval) lastexpr.head = :(=) push!(stmts, lastexpr) end push!(stmts, retstmt) - expr = rettype===Any ? retval : SymbolNode(retval,rettype) + expr = retval else expr = lastexpr.args[1] end @@ -2941,7 +2806,7 @@ function inline_worthy(body::Expr, cost::Integer=1000) # precondition: 0 < cost; return false end -gensym_increment(body, incr) = body +gensym_increment(body::ANY, incr) = body gensym_increment(body::GenSym, incr) = GenSym(body.id + incr) function gensym_increment(body::Expr, incr) if body.head === :line @@ -3082,7 +2947,7 @@ function inlining_pass(e::Expr, sv, ast) if length(e.args) == 3 && isa(e.args[3],Union{Int32,Int64}) a1 = e.args[2] basenumtype = Union{corenumtype, Main.Base.Complex64, Main.Base.Complex128, Main.Base.Rational} - if isa(a1,basenumtype) || ((isa(a1,Symbol) || isa(a1,SymbolNode) || isa(a1,GenSym)) && + if isa(a1,basenumtype) || ((isa(a1,Symbol) || isa(a1,Slot) || isa(a1,GenSym)) && exprtype(a1,sv) <: basenumtype) if e.args[3]==2 e.args = Any[GlobalRef(Main.Base,:*), a1, a1] @@ -3162,73 +3027,11 @@ function inlining_pass(e::Expr, sv, ast) return (e,stmts) end -function add_variable(ast, name, typ, is_sa) - vinf = Any[name, typ, 2+16*is_sa] +function add_slot!(ast, typ, is_sa) vinflist = ast.args[2][1]::Array{Any,1} - push!(vinflist, vinf) -end - -const some_names = Symbol[:_var0, :_var1, :_var2, :_var3, :_var4, :_var5, :_var6, - :_var7, :_var8, :_var9, :_var10, :_var11, :_var12, - :_var13, :_var14, :_var15, :_var16, :_var17, :_var18, - :_var19, :_var20, :_var21, :_var22, :_var23, :_var24] -function contains_is1(vinflist::Array{Any,1}, x::Symbol) - for y in vinflist - if is(y[1],x) - return true - end - end - return false -end -function unique_name(ast) - locllist = ast.args[2][1]::Array{Any,1} - for g in some_names - if !contains_is1(locllist, g) - return g - end - end - g = gensym() - while contains_is1(locllist, g) - g = gensym() - end - g -end -function unique_name(ast1, ast2) - locllist1 = ast1.args[2][1]::Array{Any,1} - locllist2 = ast2.args[2][1]::Array{Any,1} - for g in some_names - if !contains_is1(locllist1, g) && - !contains_is1(locllist2, g) - return g - end - end - g = gensym() - while contains_is1(locllist1, g) | - contains_is1(locllist2, g) - g = gensym() - end - g -end - -function unique_names(ast, n) - ns = [] - locllist = ast.args[2][1]::Array{Any,1} - for g in some_names - if !contains_is1(locllist, g) - push!(ns, g) - if length(ns)==n - return ns - end - end - end - while length(ns)!symequal(vi[1],v), ast.args[2][1]) - end +function delete_var!(ast, id, T) filter!(x->!(isa(x,Expr) && (x.head === :(=) || x.head === :const) && - symequal(x.args[1],v)), + isa(x.args[1],T) && x.args[1].id == id), ast.args[3].args) ast end +function slot_replace!(e, id, rhs, T) + if isa(e,T) && e.id == id + return rhs + end + if isa(e,Expr) + for i = 1:length(e.args) + e.args[i] = slot_replace!(e.args[i], id, rhs, T) + end + end + return e +end + +occurs_undef(var::Int, expr, varinfo) = + varinfo[var][3]&32 != 0 && occurs_more(expr, e->(isa(e,Slot) && e.id==var), 0)>0 + # remove all single-assigned vars v in "v = x" where x is an argument # and not assigned. # "sa" is the result of find_sa_vars -function remove_redundant_temp_vars(ast, sa) +# T: Slot or Gensym +function remove_redundant_temp_vars(ast, sa, T) varinfo = ast.args[2][1] gensym_types = ast.args[2][3] body = ast.args[3] for (v,init) in sa - if ((isa(init,Symbol) || isa(init,SymbolNode)) && - _any(vi->symequal(vi[1],init), varinfo) && - !is_var_assigned(ast, init)) - + if (isa(init, Slot) && !is_var_assigned(ast, init)) # this transformation is not valid for vars used before def. # we need to preserve the point of assignment to know where to # throw errors (issue #4645). - if !occurs_undef(v, body, varinfo) - + if T===GenSym || !occurs_undef(v, body, varinfo) # the transformation is not ideal if the assignment # is present for the auto-unbox functionality # (from inlining improved type inference information) # and this transformation would worsen the type information # everywhere later in the function - if (isa(init,SymbolNode) ? (init.typ <: (isa(v,GenSym)?gensym_types[(v::GenSym).id+1]:local_typeof(v, varinfo))) : true) - delete_var!(ast, v) - sym_replace(body, Any[v], Void[], Any[init], Void[]) + if init.typ <: (T===GenSym ? gensym_types[v+1] : varinfo[v][2]) + delete_var!(ast, v, T) + slot_replace!(body, v, init, T) end end end @@ -3298,70 +3103,43 @@ function remove_redundant_temp_vars(ast, sa) ast end -function local_typeof(v, varinfo) - for (v2, typ, info) in varinfo - v === v2 && return typ - end - @assert false "v not in varinfo" -end -function var_infobits(v, varinfo) - for (v2, typ, info) in varinfo - v === v2 && return info - end - @assert false "v not in varinfo" -end - -occurs_undef(var::GenSym, expr, varinfo) = false - -occurs_undef(var, expr, varinfo) = - occurs_more(expr, e->(isa(e,SymbolNode) && symequal(var,e) && - ((var_infobits(e.name,varinfo)&32)!=0)), 0)>0 - -# compute set of vars assigned once +# compute set of slots assigned once function find_sa_vars(ast) body = ast.args[3].args av = ObjectIdDict() av2 = ObjectIdDict() + gss = ObjectIdDict() vinfos = ast.args[2][1]::Array{Any,1} + nargs = length(ast.args[1]) args = ast.args[1] for i = 1:length(body) e = body[i] if isa(e,Expr) && is(e.head,:(=)) lhs = e.args[1] - if isa(lhs,GenSym) - av[lhs] = e.args[2] - elseif isa(lhs,SymbolNode) - av2[(lhs::SymbolNode).name] = true - elseif isa(lhs, Symbol) - lhs = lhs::Symbol - if contains_is1(vinfos,lhs) && !contains_is(args,lhs) # exclude globals & args - if !haskey(av, lhs) - av[lhs] = e.args[2] + if isa(lhs, GenSym) + gss[lhs.id] = e.args[2] + elseif isa(lhs, Slot) + id = lhs.id + if id > nargs # exclude args + if !haskey(av, id) + av[id] = e.args[2] else - av2[lhs] = true + av2[id] = true end end end end end - filter!((var,_)->!haskey(av2,var), av) - for vi in vinfos - if (vi[3]&1)!=0 - # remove captured vars - delete!(av, vi[1]) - end - end - av + filter!((id,_)->!haskey(av2,id), av) + av, gss end -symequal(x::SymbolNode, y::SymbolNode) = is(x.name,y.name) -symequal(x::SymbolNode, y::Symbol) = is(x.name,y) -symequal(x::Symbol , y::SymbolNode) = is(x,y.name) -symequal(x::GenSym , y::GenSym) = is(x.id,y.id) -symequal(x::ANY , y::ANY) = is(x,y) +symequal(x::GenSym, y::GenSym) = is(x.id,y.id) +symequal(x::Slot , y::Slot) = is(x.id,y.id) +symequal(x::ANY , y::ANY) = is(x,y) function occurs_outside_getfield(e::ANY, sym::ANY, sv::InferenceState, field_count, field_names) - if is(e, sym) || (isa(e, SymbolNode) && is(e.name, sym)) + if e===sym || (isa(e,Slot) && isa(sym,Slot) && e.id == sym.id) return true end if isa(e,Expr) @@ -3475,12 +3253,14 @@ end function alloc_elim_pass(ast::Expr, sv::InferenceState) bexpr = ast.args[3]::Expr body = (ast.args[3].args)::Array{Any,1} - vs = find_sa_vars(ast) - remove_redundant_temp_vars(ast, vs) + vs, gs = find_sa_vars(ast) + remove_redundant_temp_vars(ast, vs, Slot) + remove_redundant_temp_vars(ast, gs, GenSym) i = 1 while i < length(body) e = body[i] - if !(isa(e,Expr) && is(e.head,:(=)) && (isa(e.args[1], GenSym) || haskey(vs, e.args[1]))) + if !(isa(e,Expr) && is(e.head,:(=)) && (isa(e.args[1], GenSym) || + (isa(e.args[1],Slot) && haskey(vs, e.args[1].id)))) i += 1 continue end @@ -3490,7 +3270,7 @@ function alloc_elim_pass(ast::Expr, sv::InferenceState) if !is(alloc,false) nv, field_names = alloc tup = rhs.args - if occurs_outside_getfield(bexpr, var, sv, nv, field_names) || !is_local(sv, var) + if occurs_outside_getfield(bexpr, var, sv, nv, field_names) i += 1 continue end @@ -3535,16 +3315,11 @@ function replace_getfield!(ast, e::Expr, tupname, vals, field_names, sv, i0) val = vals[idx] # original expression might have better type info than # the tuple element expression that's replacing it. - if isa(val,SymbolNode) - val = val::SymbolNode + if isa(val,Slot) + val = val::Slot if a.typ <: val.typ && !typeseq(a.typ,val.typ) val.typ = a.typ - for vi in ast.args[2][1]::Array{Any,1} - if vi[1] === val.name - vi[2] = a.typ - break - end - end + ast.args[2][1][val.id][2] = a.typ end elseif isa(val,GenSym) val = val::GenSym diff --git a/base/serialize.jl b/base/serialize.jl index 7b1a5ee9499da..a43320b237513 100644 --- a/base/serialize.jl +++ b/base/serialize.jl @@ -18,7 +18,7 @@ const TAGS = Any[ Tuple, Array, Expr, #LongSymbol, LongTuple, LongExpr, Symbol, Tuple, Expr, # dummy entries, intentionally shadowed by earlier ones - LineNumberNode, SymbolNode, LabelNode, GotoNode, + LineNumberNode, Slot, LabelNode, GotoNode, QuoteNode, TopNode, TypeVar, Core.Box, LambdaInfo, Module, #=UndefRefTag=#Symbol, Task, ASCIIString, UTF8String, UTF16String, UTF32String, Float16, @@ -563,11 +563,11 @@ function deserialize(s::SerializationState, ::Type{LambdaInfo}) line = deserialize(s) pure = deserialize(s) if makenew - linfo.ast = ast + linfo.module = mod linfo.sparam_syms = sparam_syms linfo.sparam_vals = sparam_vals + ccall(:jl_lambda_info_set_ast, Void, (Any, Any), linfo, ast) linfo.inferred = infr - linfo.module = mod linfo.roots = roots linfo.name = name linfo.file = file diff --git a/base/show.jl b/base/show.jl index 56c4e06cedfaf..f37afb0c6dca7 100644 --- a/base/show.jl +++ b/base/show.jl @@ -320,7 +320,7 @@ show(io::IO, s::Symbol) = show_unquoted_quote_expr(io, s, 0, 0) # eval(parse("Set{Int64}([2,3,1])”) # ==> An actual set # While this isn’t true of ALL show methods, it is of all ASTs. -typealias ExprNode Union{Expr, QuoteNode, SymbolNode, LineNumberNode, +typealias ExprNode Union{Expr, QuoteNode, Slot, LineNumberNode, LabelNode, GotoNode, TopNode, GlobalRef} # Operators have precedence levels from 1-N, and show_unquoted defaults to a # precedence level of 0 (the fourth argument). The top-level print and show @@ -494,8 +494,8 @@ show_unquoted(io::IO, ex::GotoNode, ::Int, ::Int) = print(io, "goto ", ex. show_unquoted(io::IO, ex::TopNode, ::Int, ::Int) = print(io,"top(",ex.name,')') show_unquoted(io::IO, ex::GlobalRef, ::Int, ::Int) = print(io, ex.mod, '.', ex.name) -function show_unquoted(io::IO, ex::SymbolNode, ::Int, ::Int) - print(io, ex.name) +function show_unquoted(io::IO, ex::Slot, ::Int, ::Int) + print(io, "_", ex.id) emphstate = typeemphasize(io) if emphstate || ex.typ !== Any show_expr_type(io, ex.typ, emphstate) diff --git a/src/alloc.c b/src/alloc.c index 24aa1d59708fc..12be244b90ada 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -32,7 +32,6 @@ jl_datatype_t *jl_weakref_type; jl_datatype_t *jl_ascii_string_type; jl_datatype_t *jl_utf8_string_type; jl_datatype_t *jl_expr_type; -jl_datatype_t *jl_symbolnode_type; jl_datatype_t *jl_globalref_type; jl_datatype_t *jl_linenumbernode_type; jl_datatype_t *jl_labelnode_type; @@ -72,7 +71,7 @@ jl_value_t *jl_memory_exception; jl_value_t *jl_readonlymemory_exception; jl_sym_t *call_sym; jl_sym_t *dots_sym; -jl_sym_t *module_sym; +jl_sym_t *module_sym; jl_sym_t *slot_sym; jl_sym_t *export_sym; jl_sym_t *import_sym; jl_sym_t *importall_sym; jl_sym_t *toplevel_sym; jl_sym_t *quote_sym; jl_sym_t *amp_sym; @@ -99,7 +98,7 @@ jl_sym_t *copyast_sym; jl_sym_t *fastmath_sym; jl_sym_t *pure_sym; jl_sym_t *simdloop_sym; jl_sym_t *meta_sym; jl_sym_t *inert_sym; jl_sym_t *vararg_sym; -jl_sym_t *unused_sym; +jl_sym_t *unused_sym; jl_sym_t *static_parameter_sym; typedef struct { int64_t a; @@ -272,6 +271,44 @@ JL_DLLEXPORT jl_value_t *jl_new_struct_uninit(jl_datatype_t *type) return jv; } +JL_DLLEXPORT void jl_lambda_info_set_ast(jl_lambda_info_t *li, jl_value_t *ast) +{ + li->ast = ast; jl_gc_wb(li, ast); + if (!jl_is_expr(ast)) + return; + jl_array_t *body = jl_lam_body((jl_expr_t*)ast)->args; + if (has_meta(body, pure_sym)) + li->pure = 1; + jl_value_t *body1 = skip_meta(body); + if (jl_is_linenode(body1)) { + li->file = jl_linenode_file(body1); + li->line = jl_linenode_line(body1); + } + else if (jl_is_expr(body1) && ((jl_expr_t*)body1)->head == line_sym) { + li->file = (jl_sym_t*)jl_exprarg(body1, 1); + li->line = jl_unbox_long(jl_exprarg(body1, 0)); + } + jl_array_t *vis = jl_lam_vinfo((jl_expr_t*)li->ast); + jl_array_t *args = jl_lam_args((jl_expr_t*)li->ast); + li->nslots = jl_array_len(vis); + jl_value_t *gensym_types = jl_lam_gensyms((jl_expr_t*)li->ast); + li->ngensym = (jl_is_array(gensym_types) ? jl_array_len(gensym_types) : jl_unbox_long(gensym_types)); + size_t narg = jl_array_len(args); + uint8_t called=0; + int i, j=0; + for(i=1; i < narg && i <= 8; i++) { + jl_value_t *ai = jl_cellref(args,i); + if (ai == (jl_value_t*)unused_sym || !jl_is_symbol(ai)) continue; + jl_value_t *vj; + do { + vj = jl_cellref(vis, j++); + } while (jl_cellref(vj,0) != ai); + if (jl_unbox_long(jl_cellref(vj,2))&64) + called |= (1<<(i-1)); + } + li->called = called; +} + JL_DLLEXPORT jl_lambda_info_t *jl_new_lambda_info(jl_value_t *ast, jl_svec_t *tvars, jl_svec_t *sparams, jl_module_t *ctx) @@ -306,37 +343,8 @@ jl_lambda_info_t *jl_new_lambda_info(jl_value_t *ast, jl_svec_t *tvars, jl_svec_ li->pure = 0; li->called = 0xff; li->needs_sparam_vals_ducttape = 2; - if (ast && jl_is_expr(ast)) { - jl_array_t *body = jl_lam_body((jl_expr_t*)ast)->args; - if (has_meta(body, pure_sym)) - li->pure = 1; - jl_value_t *body1 = skip_meta(body); - if (jl_is_linenode(body1)) { - li->file = jl_linenode_file(body1); - li->line = jl_linenode_line(body1); - } - else if (jl_is_expr(body1) && ((jl_expr_t*)body1)->head == line_sym) { - li->file = (jl_sym_t*)jl_exprarg(body1, 1); - li->line = jl_unbox_long(jl_exprarg(body1, 0)); - } - jl_array_t *vis = jl_lam_vinfo((jl_expr_t*)li->ast); - jl_array_t *args = jl_lam_args((jl_expr_t*)li->ast); - size_t narg = jl_array_len(args); - uint8_t called=0; - int i, j=0; - for(i=1; i < narg && i <= 8; i++) { - jl_value_t *ai = jl_cellref(args,i); - if (ai == (jl_value_t*)unused_sym || !jl_is_symbol(ai)) continue; - jl_value_t *vj; - do { - vj = jl_cellref(vis, j++); - } while (jl_cellref(vj,0) != ai); - - if (jl_unbox_long(jl_cellref(vj,2))&64) - called |= (1<<(i-1)); - } - li->called = called; - } + if (ast != NULL) + jl_lambda_info_set_ast(li, ast); return li; } diff --git a/src/ast.c b/src/ast.c index dfd046e7f49a9..ae8e1d47a83e8 100644 --- a/src/ast.c +++ b/src/ast.c @@ -61,6 +61,7 @@ typedef struct _jl_ast_context_t { value_t error_sym; value_t null_sym; value_t jlgensym_sym; + value_t slot_sym; arraylist_t gensym_to_flisp; jl_ast_context_list_t list; int ref; @@ -211,6 +212,7 @@ static void jl_init_ast_ctx(jl_ast_context_t *ast_ctx) jl_ast_ctx(fl_ctx)->error_sym = symbol(fl_ctx, "error"); jl_ast_ctx(fl_ctx)->null_sym = symbol(fl_ctx, "null"); jl_ast_ctx(fl_ctx)->jlgensym_sym = symbol(fl_ctx, "jlgensym"); + jl_ast_ctx(fl_ctx)->slot_sym = symbol(fl_ctx, "slot"); // Enable / disable syntax deprecation warnings // Disable in imaging mode to avoid i/o errors (#10727) @@ -439,11 +441,11 @@ static jl_value_t *scm_to_julia_(fl_context_t *fl_ctx, value_t e, int eo) } else { hd = car_(e); - if (hd == jl_ast_ctx(fl_ctx)->jlgensym_sym) { - size_t genid = numval(car_(cdr_(e))); - return jl_box_gensym(genid); - } - if (hd == jl_ast_ctx(fl_ctx)->null_sym && llength(e) == 1) + 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) + return jl_new_struct(jl_slot_type, jl_box_long(numval(car_(cdr_(e)))), jl_any_type); + else if (hd == jl_ast_ctx(fl_ctx)->null_sym && llength(e) == 1) return jl_nothing; } if (issymbol(hd)) @@ -670,6 +672,8 @@ 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)) + 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)); if (jl_typeis(v, jl_gotonode_type)) @@ -1075,32 +1079,6 @@ int has_meta(jl_array_t *body, jl_sym_t *sym) return 0; } -static int jl_in_vinfo_array(jl_array_t *a, jl_sym_t *v) -{ - size_t i, l=jl_array_len(a); - for(i=0; iast), sym) || - jl_in_sym_svec(linfo->sparam_syms, sym); -} - extern jl_value_t *jl_builtin_getfield; jl_value_t *jl_preresolve_globals(jl_value_t *expr, jl_lambda_info_t *lam) @@ -1108,8 +1086,7 @@ jl_value_t *jl_preresolve_globals(jl_value_t *expr, jl_lambda_info_t *lam) if (jl_is_symbol(expr)) { if (lam->module == NULL) return expr; - if (!jl_local_in_linfo(lam, (jl_sym_t*)expr)) - return jl_module_globalref(lam->module, (jl_sym_t*)expr); + return jl_module_globalref(lam->module, (jl_sym_t*)expr); } else if (jl_is_expr(expr)) { jl_expr_t *e = (jl_expr_t*)expr; diff --git a/src/builtins.c b/src/builtins.c index b4046aa892940..5eca771687f09 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1157,6 +1157,7 @@ void jl_init_primitives(void) add_builtin("MethodTable", (jl_value_t*)jl_methtable_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("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); @@ -1353,10 +1354,6 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, n += jl_printf(out, "GenSym(%" PRIuPTR ")", (uintptr_t)((jl_gensym_t*)v)->id); } - else if (vt == jl_symbolnode_type) { - n += jl_printf(out, "%s::", jl_symbol_name(jl_symbolnode_sym(v))); - n += jl_static_show_x(out, jl_symbolnode_type(v), depth); - } else if (vt == jl_globalref_type) { n += jl_static_show_x(out, (jl_value_t*)jl_globalref_mod(v), depth); n += jl_printf(out, ".%s", jl_symbol_name(jl_globalref_name(v))); diff --git a/src/ccall.cpp b/src/ccall.cpp index 90d5635dca699..28c47c19c6f22 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -417,9 +417,7 @@ static jl_value_t* try_eval(jl_value_t *ex, jl_codectx_t *ctx, const char *failu if (constant || jl_is_gensym(ex)) return constant; JL_TRY { - constant = jl_interpret_toplevel_expr_in(ctx->module, ex, - ctx->linfo->sparam_syms, - ctx->linfo->sparam_vals); + constant = jl_interpret_toplevel_expr_in(ctx->module, ex, ctx->linfo); } JL_CATCH { if (compiletime) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 0845b94d0e8dc..6e4affa00f061 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -848,23 +848,30 @@ static inline jl_module_t *topmod(jl_codectx_t *ctx) static jl_value_t *expr_type(jl_value_t *e, jl_codectx_t *ctx) { - if (jl_is_expr(e)) { - jl_value_t *typ = ((jl_expr_t*)e)->etype; + if (jl_is_gensym(e)) { + int idx = ((jl_gensym_t*)e)->id; + jl_value_t *gensym_types = jl_lam_gensyms(ctx->ast); + return (jl_is_array(gensym_types) ? jl_cellref(gensym_types, idx) : (jl_value_t*)jl_any_type); + } + if (jl_typeis(e, jl_slot_type)) { + jl_value_t *typ = jl_slot_get_type(e); if (jl_is_typevar(typ)) typ = ((jl_tvar_t*)typ)->ub; return typ; } - if (jl_is_symbolnode(e)) { - jl_value_t *typ = jl_symbolnode_type(e); + if (jl_is_expr(e)) { + if (((jl_expr_t*)e)->head == static_parameter_sym) { + size_t idx = jl_unbox_long(jl_exprarg(e,0))-1; + if (idx >= jl_svec_len(ctx->linfo->sparam_vals)) + return (jl_value_t*)jl_any_type; + e = jl_svecref(ctx->linfo->sparam_vals, idx); + goto type_of_constant; + } + jl_value_t *typ = ((jl_expr_t*)e)->etype; if (jl_is_typevar(typ)) typ = ((jl_tvar_t*)typ)->ub; return typ; } - if (jl_is_gensym(e)) { - int idx = ((jl_gensym_t*)e)->id; - jl_value_t *gensym_types = jl_lam_gensyms(ctx->ast); - return (jl_is_array(gensym_types) ? jl_cellref(gensym_types, idx) : (jl_value_t*)jl_any_type); - } if (jl_is_quotenode(e)) { e = jl_fieldref(e,0); goto type_of_constant; @@ -890,27 +897,6 @@ static jl_value_t *expr_type(jl_value_t *e, jl_codectx_t *ctx) } } if (jl_is_symbol(e)) { - if (jl_is_symbol(e)) { - if (is_global((jl_sym_t*)e, ctx)) { - // look for static parameter - jl_svec_t *sp = ctx->linfo->sparam_syms; - for (size_t i=0; i < jl_svec_len(sp); i++) { - assert(jl_is_symbol(jl_svecref(sp, i))); - if (e == jl_svecref(sp, i)) { - if (jl_svec_len(ctx->linfo->sparam_vals) == 0) - return (jl_value_t*)jl_any_type; - e = jl_svecref(ctx->linfo->sparam_vals, i); - goto type_of_constant; - } - } - } - else { - std::map::iterator it = ctx->vars.find((jl_sym_t*)e); - if (it != ctx->vars.end()) - return (*it).second.value.typ; - return (jl_value_t*)jl_any_type; - } - } jl_binding_t *b = jl_get_binding(ctx->module, (jl_sym_t*)e); if (!b || !b->value) return (jl_value_t*)jl_any_type; @@ -1098,14 +1084,11 @@ static Value *emit_arraysize(const jl_cgval_t &tinfo, Value *dim, jl_codectx_t * static jl_arrayvar_t *arrayvar_for(jl_value_t *ex, jl_codectx_t *ctx) { if (ex == NULL) return NULL; - jl_sym_t *aname=NULL; - if (jl_is_symbol(ex)) - aname = ((jl_sym_t*)ex); - else if (jl_is_symbolnode(ex)) - aname = jl_symbolnode_sym(ex); - if (aname && ctx->arrayvars->find(aname) != ctx->arrayvars->end()) { - return &(*ctx->arrayvars)[aname]; - } + if (!jl_is_slot(ex)) + return NULL; + int sl = jl_slot_number(ex)-1; + if (ctx->arrayvars->find(sl) != ctx->arrayvars->end()) + return &(*ctx->arrayvars)[sl]; //TODO: gensym case return NULL; } @@ -1582,7 +1565,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_is_symbolnode(ex) && !jl_is_gensym(ex) && + return (!jl_is_symbol(ex) && !jl_typeis(ex, jl_slot_type) && !jl_is_gensym(ex) && !jl_is_bool(ex) && !jl_is_quotenode(ex) && !jl_is_byte_string(ex) && !jl_is_globalref(ex)); } diff --git a/src/codegen.cpp b/src/codegen.cpp index 687bd69f4a500..416b3a9624d95 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -549,12 +549,10 @@ typedef struct { typedef struct { Function *f; // local var info. globals are not in here. - // NOTE: you must be careful not to access vars[s] before you are sure "s" is - // a local, since otherwise this will add it to the map. - std::map vars; + std::vector slots; std::vector gensym_SAvalues; std::vector gensym_assigned; - std::map *arrayvars; + std::map *arrayvars; std::map *labels; std::map *handlers; jl_module_t *module; @@ -564,7 +562,7 @@ typedef struct { Value *argArray; Value *argCount; std::string funcName; - jl_sym_t *vaName; // name of vararg argument + int vaSlot; // name of vararg argument bool vaStack; // varargs stack-allocated bool sret; int nReqArgs; @@ -593,9 +591,8 @@ typedef struct { } cFunctionList_t; static jl_cgval_t emit_expr(jl_value_t *expr, jl_codectx_t *ctx); -static int is_global(jl_sym_t *s, jl_codectx_t *ctx); -static Value* emit_local_root(jl_codectx_t *ctx, jl_varinfo_t *vi = NULL); +static Value *emit_local_root(jl_codectx_t *ctx, jl_varinfo_t *vi = NULL); static void mark_gc_use(const jl_cgval_t &v); static Value *make_jlcall(ArrayRef args, jl_codectx_t *ctx); static Value *global_binding_pointer(jl_module_t *m, jl_sym_t *s, @@ -733,19 +730,24 @@ static bool store_unboxed_p(jl_value_t *jt) jt != (jl_value_t*)jl_intrinsic_type); } -static bool store_unboxed_p(jl_sym_t *s, jl_codectx_t *ctx) +static bool store_unboxed_p(int s, jl_codectx_t *ctx) { - jl_varinfo_t &vi = ctx->vars[s]; + jl_varinfo_t &vi = ctx->slots[s]; // only store a variable unboxed if type inference has run, which // checks that the variable is not referenced undefined. return (ctx->linfo->inferred && !vi.usedUndef && // don't unbox vararg tuples - s != ctx->vaName && store_unboxed_p(vi.value.typ)); + s != ctx->vaSlot && store_unboxed_p(vi.value.typ)); } -static Value *alloc_local(jl_sym_t *s, jl_codectx_t *ctx) +static jl_sym_t *slot_symbol(int s, jl_codectx_t *ctx) { - jl_varinfo_t &vi = ctx->vars[s]; + return (jl_sym_t*)jl_cellref(jl_cellref(jl_lam_vinfo(ctx->ast), s), 0); +} + +static Value *alloc_local(int s, jl_codectx_t *ctx) +{ + jl_varinfo_t &vi = ctx->slots[s]; jl_value_t *jt = vi.value.typ; assert(store_unboxed_p(s,ctx)); Type *vtype = julia_type_to_llvm(jt); @@ -755,16 +757,16 @@ static Value *alloc_local(jl_sym_t *s, jl_codectx_t *ctx) return NULL; } // CreateAlloca is OK here because alloc_local is only called during prologue setup - Value *lv = builder.CreateAlloca(vtype, 0, jl_symbol_name(s)); + Value *lv = builder.CreateAlloca(vtype, 0, jl_symbol_name(slot_symbol(s,ctx))); vi.value = mark_julia_slot(lv, jt); - vi.value.isimmutable = false; // slots are not immutable + vi.value.isimmutable &= vi.isSA; // slot is not immutable if there are multiple assignments assert(vi.value.isboxed == false); return lv; } -static void maybe_alloc_arrayvar(jl_sym_t *s, jl_codectx_t *ctx) +static void maybe_alloc_arrayvar(int s, jl_codectx_t *ctx) { - jl_value_t *jt = ctx->vars[s].value.typ; + jl_value_t *jt = ctx->slots[s].value.typ; if (arraytype_constshape(jt)) { // TODO: this optimization does not yet work with 1-d arrays, since the // length and data pointer can change at any time via push! @@ -1617,35 +1619,14 @@ jl_value_t *jl_static_eval(jl_value_t *ex, void *ctx_, jl_module_t *mod, jl_lambda_info_t *linfo, int sparams, int allow_alloc) { jl_codectx_t *ctx = (jl_codectx_t*)ctx_; - if (jl_is_symbolnode(ex)) - ex = (jl_value_t*)jl_symbolnode_sym(ex); if (jl_is_symbol(ex)) { jl_sym_t *sym = (jl_sym_t*)ex; - bool isglob = true; - if (ctx) { - isglob = is_global(sym, ctx); - } - else if (linfo) { - isglob = !jl_local_in_linfo(linfo, sym); - } - if (isglob) { - size_t i; - if (sparams) { - for (i=0; i < jl_svec_len(linfo->sparam_syms); i++) { - if (sym == (jl_sym_t*)jl_svecref(linfo->sparam_syms, i)) { - // static parameter - if (jl_svec_len(linfo->sparam_vals) > 0) - return jl_svecref(linfo->sparam_vals, i); - else - return NULL; - } - } - } - if (jl_is_const(mod, sym)) - return jl_get_global(mod, sym); - } + if (jl_is_const(mod, sym)) + return jl_get_global(mod, sym); return NULL; } + if (jl_typeis(ex,jl_slot_type)) + return NULL; if (jl_is_gensym(ex)) { ssize_t idx = ((jl_gensym_t*)ex)->id; assert(idx >= 0); @@ -1733,8 +1714,7 @@ jl_value_t *jl_static_eval(jl_value_t *ex, void *ctx_, jl_module_t *mod, static jl_value_t *static_eval(jl_value_t *ex, jl_codectx_t *ctx, bool sparams, bool allow_alloc) { - return jl_static_eval(ex, ctx, ctx->module, ctx->linfo, - sparams, allow_alloc); + return jl_static_eval(ex, ctx, ctx->module, ctx->linfo, sparams, allow_alloc); } static bool is_constant(jl_value_t *ex, jl_codectx_t *ctx, bool sparams=true) @@ -1742,47 +1722,42 @@ static bool is_constant(jl_value_t *ex, jl_codectx_t *ctx, bool sparams=true) return static_eval(ex,ctx,sparams) != NULL; } -static bool symbol_eq(jl_value_t *e, jl_sym_t *sym) +static bool slot_eq(jl_value_t *e, int sl) { - return ((jl_is_symbol(e) && ((jl_sym_t*)e)==sym) || - (jl_is_symbolnode(e) && jl_symbolnode_sym(e)==sym)); + return jl_is_slot(e) && jl_slot_number(e)-1 == sl; } // --- find volatile variables --- // assigned in a try block and used outside that try block -static bool local_var_occurs(jl_value_t *e, jl_sym_t *s) +static bool local_var_occurs(jl_value_t *e, int sl) { - if (jl_is_symbol(e) || jl_is_symbolnode(e)) { - if (symbol_eq(e, s)) - return true; + if (slot_eq(e, sl)) { + return true; } else if (jl_is_expr(e)) { jl_expr_t *ex = (jl_expr_t*)e; size_t alength = jl_array_dim0(ex->args); for(int i=0; i < (int)alength; i++) { - if (local_var_occurs(jl_exprarg(ex,i),s)) + if (local_var_occurs(jl_exprarg(ex,i),sl)) return true; } } return false; } -static std::set assigned_in_try(jl_array_t *stmts, int s, long l, int *pend) +static std::set assigned_in_try(jl_array_t *stmts, int s, long l, int *pend) { - std::set av; + std::set av; size_t slength = jl_array_dim0(stmts); for(int i=s; i < (int)slength; i++) { jl_value_t *st = jl_cellref(stmts,i); if (jl_is_expr(st)) { if (((jl_expr_t*)st)->head == assign_sym) { jl_value_t *ar = jl_exprarg(st, 0); - if (jl_is_symbolnode(ar)) { - ar = (jl_value_t*)jl_symbolnode_sym(ar); - } - if (jl_is_symbol(ar)) { - av.insert((jl_sym_t*)ar); + if (jl_is_slot(ar)) { + av.insert(jl_slot_number(ar)-1); } } } @@ -1796,7 +1771,7 @@ static std::set assigned_in_try(jl_array_t *stmts, int s, long l, int return av; } -static void mark_volatile_vars(jl_array_t *stmts, std::map &vars) +static void mark_volatile_vars(jl_array_t *stmts, std::vector &slots) { size_t slength = jl_array_dim0(stmts); for(int i=0; i < (int)slength; i++) { @@ -1804,17 +1779,17 @@ static void mark_volatile_vars(jl_array_t *stmts, std::maphead == enter_sym) { int last = (int)slength-1; - std::set as = + std::set as = assigned_in_try(stmts, i+1, jl_unbox_long(jl_exprarg(st,0)), &last); for(int j=0; j < (int)slength; j++) { if (j < i || j > last) { - std::set::iterator it = as.begin(); + std::set::iterator it = as.begin(); for(; it != as.end(); it++) { - std::map::iterator vi = vars.find(*it); - if (vi != vars.end() && !vi->second.value.constant && - local_var_occurs(jl_cellref(stmts,j), *it)) { - vi->second.isVolatile = true; + if (local_var_occurs(jl_cellref(stmts,j), *it)) { + jl_varinfo_t &vi = slots[*it]; + if (!vi.value.constant) + vi.isVolatile = true; } } } @@ -1828,7 +1803,7 @@ static void mark_volatile_vars(jl_array_t *stmts, std::mapmodule, f, jl_emptysvec, jl_emptysvec); + jl_value_t *fv = jl_interpret_toplevel_expr_in(ctx->module, f, NULL); if (jl_typeis(fv, jl_intrinsic_type)) { esc = false; JL_I::intrinsic fi = (JL_I::intrinsic)jl_unbox_int32(fv); @@ -1895,16 +1870,11 @@ static void simple_escape_analysis(jl_value_t *expr, bool esc, jl_codectx_t *ctx } return; } - if (jl_is_symbolnode(expr)) { - expr = (jl_value_t*)jl_symbolnode_sym(expr); - } - if (jl_is_symbol(expr)) { - jl_sym_t *vname = ((jl_sym_t*)expr); - if (ctx->vars.find(vname) != ctx->vars.end()) { - jl_varinfo_t &vi = ctx->vars[vname]; - vi.escapes |= esc; - vi.used = true; - } + if (jl_is_slot(expr)) { + int i = jl_slot_number(expr)-1; + jl_varinfo_t &vi = ctx->slots[i]; + vi.escapes |= esc; + vi.used = true; } } @@ -1913,7 +1883,7 @@ static void simple_escape_analysis(jl_value_t *expr, bool esc, jl_codectx_t *ctx // ---- Get Element Pointer (GEP) instructions within the GC frame ---- // Emit a gc-root slot indicator -static Value* emit_local_root(jl_codectx_t *ctx, jl_varinfo_t *vi) +static Value *emit_local_root(jl_codectx_t *ctx, jl_varinfo_t *vi) { CallInst *newroot = CallInst::Create(prepare_call(gcroot_func), "", /*InsertBefore*/ctx->ptlsStates); if (vi) { @@ -2534,7 +2504,7 @@ static bool emit_builtin_call(jl_cgval_t *ret, jl_value_t *f, jl_value_t **args, jl_value_t *fldt = expr_type(args[2], ctx); // VA tuple - if (ctx->vaStack && symbol_eq(args[1], ctx->vaName)) { + if (ctx->vaStack && slot_eq(args[1], ctx->vaSlot)) { Value *valen = emit_n_varargs(ctx); Value *idx = emit_unbox(T_size, emit_expr(args[2], ctx), fldt); idx = emit_bounds_check( @@ -2602,7 +2572,7 @@ static bool emit_builtin_call(jl_cgval_t *ret, jl_value_t *f, jl_value_t **args, } else if (f==jl_builtin_nfields && nargs==1) { - if (ctx->vaStack && symbol_eq(args[1], ctx->vaName) && !ctx->vars[ctx->vaName].isAssigned) { + if (ctx->vaStack && slot_eq(args[1], ctx->vaSlot) && !ctx->slots[ctx->vaSlot].isAssigned) { *ret = mark_julia_type(emit_n_varargs(ctx), false, jl_long_type, ctx); JL_GC_POP(); return true; @@ -2854,8 +2824,7 @@ static jl_cgval_t emit_call(jl_value_t **args, size_t arglen, jl_codectx_t *ctx, jl_cgval_t fval; if (f != NULL) { // TODO jb/functions: avoid making too many roots here - if (!jl_is_globalref(args[0]) && - !(jl_is_symbol(args[0]) && is_global((jl_sym_t*)args[0],ctx)) && + if (!jl_is_globalref(args[0]) && !jl_is_symbol(args[0]) && !jl_is_leaf_type(f)) { jl_add_linfo_root(ctx->linfo, f); } @@ -2897,12 +2866,6 @@ static jl_cgval_t emit_call(jl_value_t **args, size_t arglen, jl_codectx_t *ctx, // --- accessing and assigning variables --- -static int is_global(jl_sym_t *s, jl_codectx_t *ctx) -{ - std::map::iterator it = ctx->vars.find(s); - return (it == ctx->vars.end()); -} - static void undef_var_error_if_null(Value *v, jl_sym_t *name, jl_codectx_t *ctx) { Value *ok = builder.CreateICmpNE(v, V_null); @@ -2976,43 +2939,40 @@ static jl_cgval_t emit_checked_var(Value *bp, jl_sym_t *name, jl_codectx_t *ctx, return mark_julia_type(v, true, jl_any_type, ctx); } -static jl_cgval_t emit_var(jl_sym_t *sym, jl_codectx_t *ctx) +static jl_cgval_t emit_sparam(size_t i, jl_codectx_t *ctx) { - bool isglobal = is_global(sym, ctx); - if (isglobal) { - // look for static parameter - jl_svec_t *sp = ctx->linfo->sparam_syms; - for(size_t i=0; i < jl_svec_len(sp); i++) { - assert(jl_is_symbol(jl_svecref(sp, i))); - if (sym == (jl_sym_t*)jl_svecref(sp, i)) { - if (jl_svec_len(ctx->linfo->sparam_vals) > 0) { - return mark_julia_const(jl_svecref(ctx->linfo->sparam_vals, i)); - } - else { - assert(ctx->spvals_ptr != NULL); - Value *bp = builder.CreateConstInBoundsGEP1_32(LLVM37_param(T_pjlvalue) - builder.CreateBitCast(ctx->spvals_ptr, T_ppjlvalue), - i + sizeof(jl_svec_t) / sizeof(jl_value_t*)); - return mark_julia_type(tbaa_decorate(tbaa_const, builder.CreateLoad(bp)), true, jl_any_type, ctx); - } - } - } - jl_binding_t *jbp=NULL; - Value *bp = global_binding_pointer(ctx->module, sym, &jbp, false, ctx); - assert(bp != NULL); - if (jbp && jbp->value != NULL) { - if (jbp->constp) { - return mark_julia_const(jbp->value); - } - // double-check that a global variable is actually defined. this - // can be a problem in parallel when a definition is missing on - // one machine. - return mark_julia_type(builder.CreateLoad(bp), true, jl_any_type, ctx); - } - return emit_checked_var(bp, sym, ctx); + if (jl_svec_len(ctx->linfo->sparam_vals) > 0) { + return mark_julia_const(jl_svecref(ctx->linfo->sparam_vals, i)); + } + else { + assert(ctx->spvals_ptr != NULL); + Value *bp = builder.CreateConstInBoundsGEP1_32(LLVM37_param(T_pjlvalue) + builder.CreateBitCast(ctx->spvals_ptr, T_ppjlvalue), + i + sizeof(jl_svec_t) / sizeof(jl_value_t*)); + return mark_julia_type(tbaa_decorate(tbaa_const, builder.CreateLoad(bp)), true, jl_any_type, ctx); } +} + +static jl_cgval_t emit_global(jl_sym_t *sym, jl_codectx_t *ctx) +{ + jl_binding_t *jbp=NULL; + Value *bp = global_binding_pointer(ctx->module, sym, &jbp, false, ctx); + assert(bp != NULL); + if (jbp && jbp->value != NULL) { + if (jbp->constp) + return mark_julia_const(jbp->value); + // double-check that a global variable is actually defined. this + // can be a problem in parallel when a definition is missing on + // one machine. + return mark_julia_type(builder.CreateLoad(bp), true, jl_any_type, ctx); + } + return emit_checked_var(bp, sym, ctx); +} - jl_varinfo_t &vi = ctx->vars[sym]; +static jl_cgval_t emit_local(int sl, jl_codectx_t *ctx) +{ + jl_varinfo_t &vi = ctx->slots[sl]; + jl_sym_t *sym = slot_symbol(sl, ctx); if (!vi.isArgument && !vi.isAssigned) { undef_var_error_if_null(V_null, sym, ctx); return jl_cgval_t(); @@ -3076,15 +3036,12 @@ static void emit_assignment(jl_value_t *l, jl_value_t *r, jl_codectx_t *ctx) Value *bp = NULL; if (jl_is_symbol(l)) s = (jl_sym_t*)l; - else if (jl_is_symbolnode(l)) - s = jl_symbolnode_sym(l); else if (jl_is_globalref(l)) bp = global_binding_pointer(jl_globalref_mod(l), jl_globalref_name(l), &bnd, true, ctx); // now bp != NULL else - assert(false); - if (bp == NULL && is_global(s, ctx)) { - bp = global_binding_pointer(ctx->module, s, &bnd, true, ctx); // now bp != NULL - } + assert(jl_is_slot(l)); + if (bp == NULL && s != NULL) + bp = global_binding_pointer(ctx->module, s, &bnd, true, ctx); if (bp != NULL) { // it's a global assert(bnd); Value *rval = boxed(emit_expr(r, ctx), ctx, false); // no root needed since this is about to be assigned to a global @@ -3102,8 +3059,9 @@ static void emit_assignment(jl_value_t *l, jl_value_t *r, jl_codectx_t *ctx) return; } + int sl = jl_slot_number(l)-1; // it's a local variable - jl_varinfo_t &vi = ctx->vars[s]; + jl_varinfo_t &vi = ctx->slots[sl]; jl_cgval_t rval_info = emit_expr(r, ctx); if (!vi.used) return; @@ -3113,9 +3071,8 @@ static void emit_assignment(jl_value_t *l, jl_value_t *r, jl_codectx_t *ctx) // check isboxed in case rval isn't the right type (for example, on a dead branch), // so we don't try to assign it to the arrayvar info jl_arrayvar_t *av = arrayvar_for(l, ctx); - if (av != NULL) { + if (av != NULL) assign_arrayvar(*av, rval_info, ctx); - } } assert(vi.isAssigned); @@ -3160,33 +3117,29 @@ static Value *emit_condition(jl_value_t *cond, const std::string &msg, jl_codect static void emit_stmtpos(jl_value_t *expr, jl_codectx_t *ctx) { - if (jl_is_symbol(expr)) - return; // value not used, no point in attempting codegen for it - if (jl_is_symbolnode(expr)) + if (jl_is_symbol(expr) || jl_is_slot(expr)) return; // value not used, no point in attempting codegen for it if (jl_is_gensym(expr)) return; // value not used, no point in attempting codegen for it if (jl_is_linenode(expr)) return; if (jl_is_newvarnode(expr)) { - jl_sym_t *var = (jl_sym_t*)jl_fieldref(expr, 0); - assert(jl_is_symbol(var)); - jl_varinfo_t &vi = ctx->vars[var]; + jl_value_t *var = jl_fieldref(expr, 0); + assert(jl_is_slot(var)); + jl_varinfo_t &vi = ctx->slots[jl_slot_number(var)-1]; Value *lv = vi.memloc; if (lv != NULL) { // create a new uninitialized variable - if (vi.usedUndef) { + if (vi.usedUndef) builder.CreateStore(V_null, lv); - } } return; } if (jl_is_expr(expr)) { jl_sym_t *head = ((jl_expr_t*)expr)->head; // some expression types are metadata and can be ignored in statement position - if (head == line_sym || head == type_goto_sym || head == meta_sym) { + if (head == line_sym || head == type_goto_sym || head == meta_sym) return; - } // fall-through } (void)emit_expr(expr, ctx); @@ -3196,17 +3149,17 @@ static jl_cgval_t emit_expr(jl_value_t *expr, jl_codectx_t *ctx) { if (jl_is_symbol(expr)) { jl_sym_t *sym = (jl_sym_t*)expr; - return emit_var(sym, ctx); + return emit_global(sym, ctx); } - if (jl_is_symbolnode(expr)) { - jl_sym_t *sym = jl_symbolnode_sym(expr); - jl_value_t *typ = jl_symbolnode_type(expr); + if (jl_is_slot(expr)) { + size_t i = jl_slot_number(expr); + jl_value_t *typ = jl_slot_get_type(expr); if (jl_is_typevar(typ)) typ = ((jl_tvar_t*)typ)->ub; - jl_cgval_t val = emit_var(sym, ctx); + jl_cgval_t val = emit_local(i-1, ctx); if (val.isboxed) - val = remark_julia_type(val, typ); // patch up typ to match SymbolNode.type - return val; // patch up typ to match SymbolNode.type + val = remark_julia_type(val, typ); // patch up typ to match slot.typ + return val; } if (jl_is_gensym(expr)) { ssize_t idx = ((jl_gensym_t*)expr)->id; @@ -3330,27 +3283,30 @@ static jl_cgval_t emit_expr(jl_value_t *expr, jl_codectx_t *ctx) emit_assignment(args[0], args[1], ctx); return ghostValue(jl_void_type); } + else if (head == static_parameter_sym) { + return emit_sparam(jl_unbox_long(args[0])-1, ctx); + } else if (head == method_sym) { jl_value_t *mn = args[0]; - if (jl_is_symbolnode(mn)) - mn = (jl_value_t*)jl_symbolnode_sym(mn); - assert(jl_expr_nargs(ex) != 1 || jl_is_symbol(mn)); + assert(jl_expr_nargs(ex) != 1 || jl_is_symbol(mn) || jl_is_slot(mn)); + Value *bp = NULL, *name, *bp_owner = V_null; + jl_binding_t *bnd = NULL; if (jl_is_symbol(mn)) { if (jl_symbol_name((jl_sym_t*)mn)[0] == '@') jl_errorf("macro definition not allowed inside a local scope"); - Value *name = literal_pointer_val(mn); - jl_binding_t *bnd = NULL; - Value *bp, *bp_owner = V_null; - if (is_global((jl_sym_t*)mn, ctx)) { - bnd = jl_get_binding_for_method_def(ctx->module, (jl_sym_t*)mn); - bp = julia_binding_gv(bnd); - bp_owner = literal_pointer_val((jl_value_t*)ctx->module); - } - else { - jl_varinfo_t &vi = ctx->vars[(jl_sym_t*)mn]; - bp = vi.memloc; - } + name = literal_pointer_val(mn); + bnd = jl_get_binding_for_method_def(ctx->module, (jl_sym_t*)mn); + bp = julia_binding_gv(bnd); + bp_owner = literal_pointer_val((jl_value_t*)ctx->module); + } + else if (jl_is_slot(mn)) { + int sl = jl_slot_number(mn)-1; + jl_varinfo_t &vi = ctx->slots[sl]; + bp = vi.memloc; + name = literal_pointer_val((jl_value_t*)slot_symbol(sl, ctx)); + } + if (bp) { Value *mdargs[4] = { name, bp, bp_owner, literal_pointer_val(bnd) }; jl_cgval_t gf = mark_julia_type( builder.CreateCall(prepare_call(jlgenericfunction_func), ArrayRef(&mdargs[0], 4)), @@ -3366,9 +3322,8 @@ static jl_cgval_t emit_expr(jl_value_t *expr, jl_codectx_t *ctx) } else if (head == const_sym) { jl_sym_t *sym = (jl_sym_t*)args[0]; - assert(jl_is_symbol(sym)); - jl_binding_t *bnd = NULL; - if (is_global(sym, ctx)) { + if (jl_is_symbol(sym)) { + jl_binding_t *bnd = NULL; (void)global_binding_pointer(ctx->module, sym, &bnd, true, ctx); assert(bnd); builder.CreateCall(prepare_call(jldeclareconst_func), literal_pointer_val(bnd)); @@ -3519,7 +3474,7 @@ static jl_cgval_t emit_expr(jl_value_t *expr, jl_codectx_t *ctx) jl_error("syntax: prefix \"$\" in non-quoted expression"); if (jl_is_toplevel_only_expr(expr) && ctx->linfo->name == anonymous_sym && - (ctx->vars.empty() || ((jl_expr_t*)expr)->head == thunk_sym) && + (ctx->slots.empty() || ((jl_expr_t*)expr)->head == thunk_sym) && ctx->linfo->module == jl_current_module) { // call interpreter to run a toplevel expr from inside a // compiled toplevel thunk. @@ -3998,7 +3953,7 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func assert(jl_is_expr(ast)); //jl_static_show(JL_STDOUT, (jl_value_t*)ast); //jl_printf(JL_STDOUT, "\n"); - std::map arrayvars; + std::map arrayvars; std::map labels; std::map handlers; jl_codectx_t ctx; @@ -4009,7 +3964,7 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func ctx.ast = ast; ctx.linfo = lam; ctx.funcName = jl_symbol_name(lam->name); - ctx.vaName = NULL; + ctx.vaSlot = -1; ctx.vaStack = false; ctx.inbounds.push_back(false); ctx.boundsCheck.push_back(false); @@ -4022,6 +3977,7 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func size_t largslen = jl_array_dim0(largs); jl_array_t *vinfos = jl_lam_vinfo(ast); size_t vinfoslen = jl_array_dim0(vinfos); + ctx.slots.resize(vinfoslen); size_t nreq = largslen; int va = 0; @@ -4030,12 +3986,12 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func // compiling this would cause all specializations to inherit // this code and could create an broken compile / function cache - if (nreq > 0 && jl_is_rest_arg(jl_cellref(largs,nreq-1))) { + if (nreq > 0 && jl_is_rest_arg(jl_cellref(largs,largslen-1))) { nreq--; va = 1; - jl_sym_t *vn = jl_decl_var(jl_cellref(largs,nreq)); + jl_sym_t *vn = jl_decl_var(jl_cellref(largs,largslen-1)); if (vn != unused_sym) - ctx.vaName = vn; + ctx.vaSlot = largslen-1; } ctx.nReqArgs = nreq; @@ -4049,13 +4005,13 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func jl_value_t *arg = jl_cellref(largs,i); jl_sym_t *argname = jl_decl_var(arg); if (argname == unused_sym) continue; - jl_varinfo_t &varinfo = ctx.vars[argname]; + jl_varinfo_t &varinfo = ctx.slots[i]; varinfo.isArgument = true; jl_value_t *ty = jl_nth_slot_type(lam->specTypes, i); varinfo.value = mark_julia_type((Value*)NULL, false, ty, &ctx); } - if (va && ctx.vaName) { - jl_varinfo_t &varinfo = ctx.vars[ctx.vaName]; + if (va && ctx.vaSlot != -1) { + jl_varinfo_t &varinfo = ctx.slots[ctx.vaSlot]; varinfo.isArgument = true; varinfo.value = mark_julia_type((Value*)NULL, false, jl_tuple_type, &ctx); } @@ -4065,7 +4021,7 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func assert(jl_is_array(vi)); jl_sym_t *vname = ((jl_sym_t*)jl_cellref(vi,0)); assert(jl_is_symbol(vname)); - jl_varinfo_t &varinfo = ctx.vars[vname]; + jl_varinfo_t &varinfo = ctx.slots[i]; varinfo.isAssigned = (jl_vinfo_assigned(vi)!=0); varinfo.escapes = false; varinfo.isSA = (jl_vinfo_sa(vi)!=0); @@ -4083,7 +4039,7 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func // determine which vars need to be volatile jl_array_t *stmts = jl_lam_body(ast)->args; - mark_volatile_vars(stmts, ctx.vars); + mark_volatile_vars(stmts, ctx.slots); // step 4. determine function signature jl_value_t *jlrettype = lam->rettype; @@ -4278,7 +4234,7 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func std::vector ditypes(0); #endif for(size_t i=0; i < jl_nparams(lam->specTypes); i++) { // assumes !va - if (ctx.vars[jl_decl_var(jl_cellref(largs,i))].value.isghost) + if (i < largslen && ctx.slots[i].value.isghost) continue; ditypes.push_back(julia_type_to_di(jl_tparam(lam->specTypes,i),ctx.dbuilder,false)); } @@ -4329,7 +4285,7 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func for(i=0; i < nreq; i++) { jl_sym_t *argname = jl_decl_var(jl_cellref(largs,i)); if (argname == unused_sym) continue; - jl_varinfo_t &varinfo = ctx.vars[argname]; + jl_varinfo_t &varinfo = ctx.slots[i]; #ifdef LLVM38 varinfo.dinfo = ctx.dbuilder->createParameterVariable( SP, // Scope (current function will be fill in later) @@ -4354,25 +4310,25 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func ctx.sret + i + 1); // Argument number (1-based) #endif } - if (va && ctx.vaName) { + if (va && ctx.vaSlot != -1) { #ifdef LLVM38 - ctx.vars[ctx.vaName].dinfo = ctx.dbuilder->createParameterVariable( + ctx.slots[ctx.vaSlot].dinfo = ctx.dbuilder->createParameterVariable( SP, // Scope (current function will be fill in later) - std::string(jl_symbol_name(ctx.vaName)) + "...", // Variable name + std::string(jl_symbol_name(slot_symbol(ctx.vaSlot, &ctx))) + "...", // Variable name ctx.sret + nreq + 1, // Argument number (1-based) topfile, // File toplineno == -1 ? 0 : toplineno, // Line (for now, use lineno of the function) - julia_type_to_di(ctx.vars[ctx.vaName].value.typ, ctx.dbuilder, false), + julia_type_to_di(ctx.slots[ctx.vaSlot].value.typ, ctx.dbuilder, false), AlwaysPreserve, // May be deleted if optimized out 0); // Flags (TODO: Do we need any) #else - ctx.vars[ctx.vaName].dinfo = ctx.dbuilder->createLocalVariable( + ctx.slots[ctx.vaSlot].dinfo = ctx.dbuilder->createLocalVariable( llvm::dwarf::DW_TAG_arg_variable, // Tag SP, // Scope (current function will be fill in later) - std::string(jl_symbol_name(ctx.vaName)) + "...", // Variable name + std::string(jl_symbol_name(slot_symbol(ctx.vaSlot, &ctx))) + "...", // Variable name topfile, // File toplineno == -1 ? 0 : toplineno, // Line (for now, use lineno of the function) - julia_type_to_di(ctx.vars[ctx.vaName].value.typ, ctx.dbuilder, false), // Variable type + julia_type_to_di(ctx.slots[ctx.vaSlot].value.typ, ctx.dbuilder, false), // Variable type AlwaysPreserve, // May be deleted if optimized out 0, // Flags (TODO: Do we need any) ctx.sret + nreq + 1); // Argument number (1-based) @@ -4380,7 +4336,7 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func } for(i=0; i < vinfoslen; i++) { jl_sym_t *s = (jl_sym_t*)jl_cellref(jl_cellref(vinfos,i),0); - jl_varinfo_t &varinfo = ctx.vars[s]; + jl_varinfo_t &varinfo = ctx.slots[i]; if (varinfo.isArgument) continue; #ifdef LLVM38 @@ -4459,10 +4415,10 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func // must be in the first basic block for the llvm mem2reg pass to work // get pointers for locals stored in the gc frame array (argTemp) - for (std::map::iterator I = ctx.vars.begin(), E = ctx.vars.end(); I != E; ++I) { - jl_sym_t *s = I->first; - jl_varinfo_t &varinfo = I->second; + for(i=0; i < vinfoslen; i++) { + jl_sym_t *s = (jl_sym_t*)jl_cellref(jl_cellref(vinfos,i),0); if (s == unused_sym) continue; + jl_varinfo_t &varinfo = ctx.slots[i]; assert(!varinfo.memloc); // variables shouldn't also have memory locs already if (!varinfo.usedUndef) { if (varinfo.value.constant) { @@ -4474,9 +4430,9 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func varinfo.value = mark_julia_const(jl_tparam0(varinfo.value.typ)); continue; } - else if (store_unboxed_p(s, &ctx)) { + else if (store_unboxed_p(i, &ctx)) { if (varinfo.isAssigned) { // otherwise, just leave it in the input register - Value *lv = alloc_local(s, &ctx); (void)lv; + Value *lv = alloc_local(i, &ctx); (void)lv; #ifdef LLVM36 if (ctx.debug_enabled) { assert(varinfo.dinfo->getType() != jl_pvalue_dillvmt); @@ -4492,9 +4448,9 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func } } if (varinfo.isAssigned || // always need a slot if the variable is assigned - specsig || // for arguments, give them stack slots if then aren't in `argArray` (otherwise, will use that pointer) - (va && s == ctx.vaName && varinfo.escapes) || // or it's the va arg tuple - (s != unused_sym && s == jl_decl_var(jl_cellref(largs, 0)))) { // or it is the first argument (which isn't in `argArray`) + specsig || // for arguments, give them stack slots if then aren't in `argArray` (otherwise, will use that pointer) + (va && (int)i == ctx.vaSlot && varinfo.escapes) || // or it's the va arg tuple + (s != unused_sym && s == jl_decl_var(jl_cellref(largs, 0)))) { // or it is the first argument (which isn't in `argArray`) AllocaInst *av = new AllocaInst(T_pjlvalue, jl_symbol_name(s), /*InsertBefore*/ctx.ptlsStates); varinfo.memloc = av; #ifdef LLVM36 @@ -4516,7 +4472,7 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func } #endif } - maybe_alloc_arrayvar(s, &ctx); + maybe_alloc_arrayvar(i, &ctx); } // step 10. move args into local variables @@ -4528,7 +4484,11 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func jl_value_t *argType = jl_nth_slot_type(lam->specTypes, i); bool isboxed; Type *llvmArgType = julia_type_to_llvm(argType, &isboxed); - jl_varinfo_t &vi = ctx.vars[s]; + if (s == unused_sym) { + if (specsig && !type_is_ghost(llvmArgType)) ++AI; + continue; + } + jl_varinfo_t &vi = ctx.slots[i]; jl_cgval_t theArg; if (s == unused_sym || vi.value.constant) { assert(vi.memloc == NULL); @@ -4611,17 +4571,16 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func emit_local_root(&ctx, &vi); // create a root for vi } // get arrayvar data if applicable - if (arrayvars.find(s) != arrayvars.end()) { - jl_arrayvar_t av = arrayvars[s]; + if (arrayvars.find(i) != arrayvars.end()) { + jl_arrayvar_t av = arrayvars[i]; assign_arrayvar(av, theArg, &ctx); } } } // step 11. allocate rest argument if necessary - if (va && ctx.vaName) { - jl_sym_t *argname = ctx.vaName; - jl_varinfo_t &vi = ctx.vars[argname]; + if (va && ctx.vaSlot != -1) { + jl_varinfo_t &vi = ctx.slots[ctx.vaSlot]; if (!vi.escapes && !vi.isAssigned) { ctx.vaStack = true; } diff --git a/src/dump.c b/src/dump.c index 4b9c9cea88d8a..54447cd374fcb 100644 --- a/src/dump.c +++ b/src/dump.c @@ -616,7 +616,7 @@ static int is_ast_node(jl_value_t *v) } return 0; } - return jl_is_symbol(v) || jl_is_symbolnode(v) || jl_is_gensym(v) || + return jl_is_symbol(v) || jl_is_slot(v) || jl_is_gensym(v) || jl_is_expr(v) || jl_is_newvarnode(v) || jl_is_svec(v) || jl_typeis(v, jl_array_any_type) || jl_is_tuple(v) || jl_is_uniontype(v) || jl_is_int32(v) || jl_is_int64(v) || @@ -828,6 +828,8 @@ static void jl_serialize_value_(ios_t *s, jl_value_t *v) write_int8(s, li->called); jl_serialize_value(s, (jl_value_t*)li->file); write_int32(s, li->line); + write_int32(s, li->nslots); + write_int32(s, li->ngensym); jl_serialize_value(s, (jl_value_t*)li->module); jl_serialize_value(s, (jl_value_t*)li->roots); jl_serialize_value(s, (jl_value_t*)li->def); @@ -1442,6 +1444,8 @@ static jl_value_t *jl_deserialize_value_(ios_t *s, jl_value_t *vtag, jl_value_t li->file = (jl_sym_t*)jl_deserialize_value(s, NULL); jl_gc_wb(li, li->file); li->line = read_int32(s); + li->nslots = read_int32(s); + li->ngensym = read_int32(s); li->module = (jl_module_t*)jl_deserialize_value(s, (jl_value_t**)&li->module); jl_gc_wb(li, li->module); li->roots = (jl_array_t*)jl_deserialize_value(s, (jl_value_t**)&li->roots); @@ -2363,7 +2367,7 @@ void jl_init_serializer(void) htable_new(&backref_table, 0); void *tags[] = { jl_symbol_type, jl_gensym_type, jl_datatype_type, - jl_simplevector_type, jl_array_type, + jl_simplevector_type, jl_array_type, jl_slot_type, jl_expr_type, (void*)LongSymbol_tag, (void*)LongSvec_tag, (void*)LongExpr_tag, (void*)LiteralVal_tag, (void*)SmallInt64_tag, (void*)SmallDataType_tag, @@ -2435,7 +2439,7 @@ void jl_init_serializer(void) jl_methtable_type->name, jl_method_type->name, jl_tvar_type->name, jl_ntuple_type->name, jl_abstractarray_type->name, jl_vararg_type->name, jl_densearray_type->name, jl_void_type->name, jl_lambda_info_type->name, - jl_module_type->name, jl_function_type->name, + jl_module_type->name, jl_function_type->name, jl_slot_type->name, jl_typector_type->name, jl_intrinsic_type->name, jl_task_type->name, jl_labelnode_type->name, jl_linenumbernode_type->name, jl_builtin_type->name, jl_gotonode_type->name, jl_quotenode_type->name, jl_topnode_type->name, diff --git a/src/init.c b/src/init.c index 2865b5ba0693a..90166c4e4ed7a 100644 --- a/src/init.c +++ b/src/init.c @@ -840,7 +840,6 @@ void jl_get_builtin_hooks(void) jl_ascii_string_type = (jl_datatype_t*)core("ASCIIString"); jl_utf8_string_type = (jl_datatype_t*)core("UTF8String"); - jl_symbolnode_type = (jl_datatype_t*)core("SymbolNode"); jl_weakref_type = (jl_datatype_t*)core("WeakRef"); jl_array_uint8_type = jl_apply_type((jl_value_t*)jl_array_type, diff --git a/src/interpreter.c b/src/interpreter.c index bfb873f9b9c21..246529dad632c 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -14,34 +14,27 @@ extern "C" { #endif -static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, size_t nl, size_t ngensym); -static jl_value_t *eval_body(jl_array_t *stmts, jl_value_t **locals, size_t nl, size_t ngensym, +static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, jl_lambda_info_t *lam); +static jl_value_t *eval_body(jl_array_t *stmts, jl_value_t **locals, jl_lambda_info_t *lam, int start, int toplevel); jl_value_t *jl_eval_module_expr(jl_expr_t *ex); int jl_is_toplevel_only_expr(jl_value_t *e); jl_value_t *jl_interpret_toplevel_expr(jl_value_t *e) { - return eval(e, NULL, 0, 0); + return eval(e, NULL, NULL); } -JL_DLLEXPORT jl_value_t *jl_interpret_toplevel_expr_in(jl_module_t *m, - jl_value_t *e, - jl_svec_t *local_syms, - jl_svec_t *local_vals) +JL_DLLEXPORT jl_value_t *jl_interpret_toplevel_expr_in(jl_module_t *m, jl_value_t *e, + jl_lambda_info_t *lam) { jl_value_t *v=NULL; jl_module_t *last_m = jl_current_module; jl_module_t *task_last_m = jl_current_task->current_module; - size_t i, nl = jl_svec_len(local_syms); - jl_value_t **locals = (jl_value_t**)alloca(sizeof(jl_value_t*) * 2 * nl); - for (i = 0; i < nl; i++) { - locals[2 * i] = jl_svecref(local_syms, i); - locals[2 * i + 1] = jl_svec_len(local_vals) > 0 ? jl_svecref(local_vals, i) : NULL; - } + JL_TRY { jl_current_task->current_module = jl_current_module = m; - v = eval(e, locals, nl, 0); + v = eval(e, NULL, lam); } JL_CATCH { jl_current_module = last_m; @@ -54,14 +47,14 @@ JL_DLLEXPORT jl_value_t *jl_interpret_toplevel_expr_in(jl_module_t *m, return v; } -static jl_value_t *do_call(jl_value_t **args, size_t nargs, - jl_value_t **locals, size_t nl, size_t ngensym) +static jl_value_t *do_call(jl_value_t **args, size_t nargs, jl_value_t **locals, + jl_lambda_info_t *lam) { jl_value_t **argv; JL_GC_PUSHARGS(argv, nargs); size_t i; for(i=0; i < nargs; i++) - argv[i] = eval(args[i], locals, nl, ngensym); + argv[i] = eval(args[i], locals, lam); jl_value_t *result = jl_apply_generic(argv, nargs); JL_GC_POP(); return result; @@ -119,32 +112,20 @@ void jl_set_datatype_super(jl_datatype_t *tt, jl_value_t *super) jl_gc_wb(tt, tt->super); } -static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, size_t nl, size_t ngensym) + static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, jl_lambda_info_t *lam) { if (jl_is_symbol(e)) { - jl_value_t *v = NULL; - size_t i; - for(i=0; i < nl; i++) { - if (locals[i*2] == e) { - v = locals[i*2+1]; - break; - } - } - if (i >= nl) - v = jl_get_global(jl_current_module, (jl_sym_t*)e); + jl_value_t *v = jl_get_global(jl_current_module, (jl_sym_t*)e); if (v == NULL) jl_undefined_var_error((jl_sym_t*)e); return v; } - if (jl_is_symbolnode(e)) { - return eval((jl_value_t*)jl_symbolnode_sym(e), locals, nl, ngensym); - } if (jl_is_gensym(e)) { ssize_t genid = ((jl_gensym_t*)e)->id; - if (genid >= ngensym || genid < 0) + if (genid >= lam->ngensym || genid < 0) jl_error("access to invalid GenSym location"); else - return locals[nl*2 + genid]; + return locals[lam->nslots + genid]; } if (jl_is_quotenode(e)) { return jl_fieldref(e,0); @@ -157,6 +138,15 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, size_t nl, size_t ng return v; } if (!jl_is_expr(e)) { + if (jl_typeis(e, jl_slot_type)) { + ssize_t n = jl_slot_number(e); + if (n > lam->nslots || n < 1) + jl_error("access to invalid slot number"); + jl_value_t *v = locals[n-1]; + if (v == NULL) // TODO get var name from `lam` + jl_undefined_var_error(anonymous_sym); + return v; + } if (jl_is_globalref(e)) { jl_value_t *gfargs[2] = {(jl_value_t*)jl_globalref_mod(e), (jl_value_t*)jl_globalref_name(e)}; return jl_f_getfield(NULL, gfargs, 2); @@ -166,14 +156,10 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, size_t nl, size_t ng } if (jl_is_newvarnode(e)) { jl_value_t *var = jl_fieldref(e,0); - assert(!jl_is_gensym(var)); - assert(jl_is_symbol(var)); - for(size_t i=0; i < nl; i++) { - if (locals[i*2] == var) { - locals[i*2+1] = NULL; - break; - } - } + assert(jl_typeis(var,jl_slot_type)); + ssize_t n = jl_slot_number(var); + assert(n <= lam->nslots && n > 0); + locals[n-1] = NULL; return (jl_value_t*)jl_nothing; } return e; @@ -182,47 +168,44 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, size_t nl, size_t ng jl_value_t **args = (jl_value_t**)jl_array_data(ex->args); size_t nargs = jl_array_len(ex->args); if (ex->head == call_sym) { - return do_call(args, nargs, locals, nl, ngensym); + return do_call(args, nargs, locals, lam); } else if (ex->head == assign_sym) { jl_value_t *sym = args[0]; - jl_value_t *rhs = eval(args[1], locals, nl, ngensym); + jl_value_t *rhs = eval(args[1], locals, lam); if (jl_is_gensym(sym)) { ssize_t genid = ((jl_gensym_t*)sym)->id; - if (genid >= ngensym || genid < 0) + if (genid >= lam->ngensym || genid < 0) jl_error("assignment to invalid GenSym location"); - locals[nl*2 + genid] = rhs; - return rhs; + locals[lam->nslots + genid] = rhs; } - if (jl_is_symbol(sym)) { - size_t i; - for (i=0; i < nl; i++) { - if (locals[i*2] == sym) { - locals[i*2+1] = rhs; - return rhs; - } - } + else if (jl_typeis(sym,jl_slot_type)) { + ssize_t n = jl_slot_number(sym); + assert(n <= lam->nslots && n > 0); + locals[n-1] = rhs; } - jl_module_t *m = jl_current_module; - if (jl_is_globalref(sym)) { - m = jl_globalref_mod(sym); - sym = (jl_value_t*)jl_globalref_name(sym); + else { + jl_module_t *m = jl_current_module; + if (jl_is_globalref(sym)) { + m = jl_globalref_mod(sym); + sym = (jl_value_t*)jl_globalref_name(sym); + } + assert(jl_is_symbol(sym)); + JL_GC_PUSH1(&rhs); + jl_binding_t *b = jl_get_binding_wr(m, (jl_sym_t*)sym); + jl_checked_assignment(b, rhs); + JL_GC_POP(); } - assert(jl_is_symbol(sym)); - JL_GC_PUSH1(&rhs); - jl_binding_t *b = jl_get_binding_wr(m, (jl_sym_t*)sym); - jl_checked_assignment(b, rhs); - JL_GC_POP(); return rhs; } else if (ex->head == new_sym) { - jl_value_t *thetype = eval(args[0], locals, nl, ngensym); + jl_value_t *thetype = eval(args[0], locals, lam); jl_value_t *v=NULL; JL_GC_PUSH2(&thetype, &v); assert(jl_is_structtype(thetype)); v = jl_new_struct_uninit((jl_datatype_t*)thetype); for(size_t i=1; i < nargs; i++) { - jl_set_nth_field(v, i-1, eval(args[i], locals, nl, ngensym)); + jl_set_nth_field(v, i-1, eval(args[i], locals, lam)); } JL_GC_POP(); return v; @@ -231,7 +214,15 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, size_t nl, size_t ng return (jl_value_t*)jl_nothing; } else if (ex->head == body_sym) { - return eval_body(ex->args, locals, nl, ngensym, 0, 0); + return eval_body(ex->args, locals, lam, 0, 0); + } + else if (ex->head == static_parameter_sym) { + ssize_t n = jl_unbox_long(args[0]); + assert(n > 0); + // static parameter val unknown needs to be an error for ccall + if (n > jl_svec_len(lam->sparam_vals)) + jl_error("could not determine static parameter value"); + return jl_svecref(lam->sparam_vals, n-1); } else if (ex->head == exc_sym) { return jl_exception_in_transit; @@ -247,12 +238,6 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, size_t nl, size_t ng jl_value_t **bp=NULL; jl_value_t *bp_owner=NULL; jl_binding_t *b=NULL; - for (size_t i=0; i < nl; i++) { - if (locals[i*2] == (jl_value_t*)fname) { - bp = &locals[i*2+1]; - break; - } - } if (bp == NULL) { b = jl_get_binding_for_method_def(jl_current_module, fname); bp = &b->value; @@ -265,23 +250,18 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, size_t nl, size_t ng jl_value_t *atypes=NULL, *meth=NULL; JL_GC_PUSH2(&atypes, &meth); - atypes = eval(args[1], locals, nl, ngensym); - meth = eval(args[2], locals, nl, ngensym); + atypes = eval(args[1], locals, lam); + meth = eval(args[2], locals, lam); jl_method_def((jl_svec_t*)atypes, (jl_lambda_info_t*)meth, args[3]); JL_GC_POP(); return jl_nothing; } else if (ex->head == copyast_sym) { - return jl_copy_ast(eval(args[0], locals, nl, ngensym)); + return jl_copy_ast(eval(args[0], locals, lam)); } else if (ex->head == const_sym) { jl_value_t *sym = args[0]; assert(jl_is_symbol(sym)); - for (size_t i=0; i < nl; i++) { - if (locals[i*2] == sym) { - return (jl_value_t*)jl_nothing; - } - } jl_binding_t *b = jl_get_binding_wr(jl_current_module, (jl_sym_t*)sym); jl_declare_constant(b); return (jl_value_t*)jl_nothing; @@ -297,7 +277,7 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, size_t nl, size_t ng } else if (ex->head == abstracttype_sym) { jl_value_t *name = args[0]; - jl_value_t *para = eval(args[1], locals, nl, ngensym); + jl_value_t *para = eval(args[1], locals, lam); jl_value_t *super = NULL; jl_value_t *temp = NULL; jl_datatype_t *dt = NULL; @@ -310,7 +290,7 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, size_t nl, size_t ng check_can_assign_type(b); b->value = (jl_value_t*)dt; jl_gc_wb_binding(b, dt); - super = eval(args[2], locals, nl, ngensym); + super = eval(args[2], locals, lam); jl_set_datatype_super(dt, super); jl_reinstantiate_inner_types(dt); b->value = temp; @@ -326,9 +306,9 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, size_t nl, size_t ng jl_datatype_t *dt = NULL; JL_GC_PUSH4(¶, &super, &temp, &dt); assert(jl_is_symbol(name)); - para = eval(args[1], locals, nl, ngensym); + para = eval(args[1], locals, lam); assert(jl_is_svec(para)); - vnb = eval(args[2], locals, nl, ngensym); + vnb = eval(args[2], locals, lam); if (!jl_is_long(vnb)) jl_errorf("invalid declaration of bits type %s", jl_symbol_name((jl_sym_t*)name)); @@ -342,7 +322,7 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, size_t nl, size_t ng check_can_assign_type(b); b->value = (jl_value_t*)dt; jl_gc_wb_binding(b, dt); - super = eval(args[3], locals, nl, ngensym); + super = eval(args[3], locals, lam); jl_set_datatype_super(dt, super); jl_reinstantiate_inner_types(dt); b->value = temp; @@ -355,13 +335,13 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, size_t nl, size_t ng else if (ex->head == compositetype_sym) { jl_value_t *name = args[0]; assert(jl_is_symbol(name)); - jl_value_t *para = eval(args[1], locals, nl, ngensym); + jl_value_t *para = eval(args[1], locals, lam); assert(jl_is_svec(para)); jl_value_t *temp = NULL; jl_value_t *super = NULL; jl_datatype_t *dt = NULL; JL_GC_PUSH4(¶, &super, &temp, &dt); - temp = eval(args[2], locals, nl, ngensym); // field names + temp = eval(args[2], locals, lam); // field names dt = jl_new_datatype((jl_sym_t*)name, jl_any_type, (jl_svec_t*)para, (jl_svec_t*)temp, NULL, 0, args[5]==jl_true ? 1 : 0, jl_unbox_long(args[6])); @@ -374,11 +354,11 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, size_t nl, size_t ng jl_gc_wb_binding(b,dt); JL_TRY { - super = eval(args[3], locals, nl, ngensym); + super = eval(args[3], locals, lam); jl_set_datatype_super(dt, super); // operations that can fail inside_typedef = 1; - dt->types = (jl_svec_t*)eval(args[4], locals, nl, ngensym); + dt->types = (jl_svec_t*)eval(args[4], locals, lam); jl_gc_wb(dt, dt->types); inside_typedef = 0; for(size_t i=0; i < jl_svec_len(dt->types); i++) { @@ -463,16 +443,12 @@ jl_value_t *jl_toplevel_eval_body(jl_array_t *stmts) ngensym = maxid; } jl_value_t **locals = NULL; - if (ngensym > 0) { - JL_GC_PUSHARGS(locals, ngensym); - } - jl_value_t *ret = eval_body(stmts, locals, 0, ngensym, 0, 1); - if (ngensym > 0) - JL_GC_POP(); + assert(ngensym == 0); + jl_value_t *ret = eval_body(stmts, locals, NULL, 0, 1); return ret; } -static jl_value_t *eval_body(jl_array_t *stmts, jl_value_t **locals, size_t nl, size_t ngensym, +static jl_value_t *eval_body(jl_array_t *stmts, jl_value_t **locals, jl_lambda_info_t *lam, int start, int toplevel) { jl_handler_t __eh; @@ -489,14 +465,13 @@ static jl_value_t *eval_body(jl_array_t *stmts, jl_value_t **locals, size_t nl, if (jl_is_expr(stmt)) { jl_sym_t *head = ((jl_expr_t*)stmt)->head; if (head == goto_ifnot_sym) { - jl_value_t *cond = eval(jl_exprarg(stmt,0), locals, nl, ngensym); + jl_value_t *cond = eval(jl_exprarg(stmt,0), locals, lam); if (cond == jl_false) { i = jl_unbox_long(jl_exprarg(stmt, 1))-1; continue; } else if (cond != jl_true) { - jl_type_error_rt("toplevel", "if", - (jl_value_t*)jl_bool_type, cond); + jl_type_error_rt("toplevel", "if", (jl_value_t*)jl_bool_type, cond); } } else if (head == return_sym) { @@ -504,12 +479,12 @@ static jl_value_t *eval_body(jl_array_t *stmts, jl_value_t **locals, size_t nl, if (toplevel && jl_is_toplevel_only_expr(ex)) return jl_toplevel_eval(ex); else - return eval(ex, locals, nl, ngensym); + return eval(ex, locals, lam); } else if (head == enter_sym) { jl_enter_handler(&__eh); if (!jl_setjmp(__eh.eh_ctx,1)) { - return eval_body(stmts, locals, nl, ngensym, i+1, toplevel); + return eval_body(stmts, locals, lam, i+1, toplevel); } else { #ifdef _OS_WINDOWS_ @@ -528,14 +503,14 @@ static jl_value_t *eval_body(jl_array_t *stmts, jl_value_t **locals, size_t nl, if (toplevel && jl_is_toplevel_only_expr(stmt)) jl_toplevel_eval(stmt); else - eval(stmt, locals, nl, ngensym); + eval(stmt, locals, lam); } } else { if (toplevel && jl_is_toplevel_only_expr(stmt)) jl_toplevel_eval(stmt); else - eval(stmt, locals, nl, ngensym); + eval(stmt, locals, lam); } i++; } @@ -543,39 +518,17 @@ static jl_value_t *eval_body(jl_array_t *stmts, jl_value_t **locals, size_t nl, return NULL; } -jl_value_t *jl_interpret_toplevel_thunk_with(jl_lambda_info_t *lam, - jl_value_t **loc, size_t nl) +jl_value_t *jl_interpret_toplevel_thunk(jl_lambda_info_t *lam) { jl_expr_t *ast = (jl_expr_t*)lam->ast; jl_array_t *stmts = jl_lam_body(ast)->args; - size_t nargs = jl_array_len(jl_lam_args(ast)); - jl_array_t *l = jl_lam_vinfo(ast); - size_t llength = jl_array_len(l) - nargs; - nl += llength; jl_value_t **locals; - jl_value_t *gensym_types = jl_lam_gensyms(ast); - size_t ngensym = (jl_is_array(gensym_types) ? jl_array_len(gensym_types) : jl_unbox_gensym(gensym_types)); - JL_GC_PUSHARGS(locals, nl*2+ngensym); - jl_value_t *r = (jl_value_t*)jl_nothing; - size_t i=0; - for(i=0; i < llength; i++) { - locals[i*2] = jl_cellref(jl_cellref(l,i+nargs),0); - //locals[i*2+1] = NULL; // init'd by JL_GC_PUSHARGS - } - for(; i < nl; i++) { - locals[i*2] = loc[(i-llength)*2]; - locals[i*2+1] = loc[(i-llength)*2+1]; - } - r = eval_body(stmts, locals, nl, ngensym, 0, 1); + JL_GC_PUSHARGS(locals, lam->nslots + lam->ngensym); + jl_value_t *r = eval_body(stmts, locals, lam, 0, 1); JL_GC_POP(); return r; } -jl_value_t *jl_interpret_toplevel_thunk(jl_lambda_info_t *lam) -{ - return jl_interpret_toplevel_thunk_with(lam, NULL, 0); -} - #ifdef __cplusplus } #endif diff --git a/src/jltypes.c b/src/jltypes.c index 95460b29ebea3..1bc170ca3ee8b 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -26,6 +26,7 @@ jl_datatype_t *jl_typename_type; jl_datatype_t *jl_sym_type; jl_datatype_t *jl_symbol_type; jl_datatype_t *jl_gensym_type; +jl_datatype_t *jl_slot_type; jl_datatype_t *jl_simplevector_type; jl_typename_t *jl_tuple_typename; jl_tupletype_t *jl_anytuple_type; @@ -3371,7 +3372,11 @@ void jl_init_types(void) jl_gensym_type = jl_new_datatype(jl_symbol("GenSym"), jl_any_type, jl_emptysvec, jl_svec1(jl_symbol("id")), - jl_svec1(jl_long_type), 0, 0, 0); + jl_svec1(jl_long_type), 0, 0, 1); + + jl_slot_type = jl_new_datatype(jl_symbol("Slot"), jl_any_type, jl_emptysvec, + jl_svec(2, jl_symbol("id"), jl_symbol("typ")), + jl_svec(2, jl_long_type, jl_any_type), 0, 0, 2); jl_init_int32_int64_cache(); @@ -3457,8 +3462,8 @@ void jl_init_types(void) jl_newvarnode_type = jl_new_datatype(jl_symbol("NewvarNode"), jl_any_type, jl_emptysvec, - jl_svec(1, jl_symbol("name")), - jl_svec(1, jl_sym_type), 0, 0, 1); + jl_svec(1, jl_symbol("slot")), + jl_svec(1, jl_slot_type), 0, 0, 1); jl_topnode_type = jl_new_datatype(jl_symbol("TopNode"), jl_any_type, jl_emptysvec, @@ -3481,7 +3486,7 @@ void jl_init_types(void) jl_lambda_info_type = jl_new_datatype(jl_symbol("LambdaInfo"), jl_any_type, jl_emptysvec, - jl_svec(17, jl_symbol("ast"), jl_symbol("rettype"), + jl_svec(19, jl_symbol("ast"), jl_symbol("rettype"), jl_symbol("sparam_syms"), jl_symbol("sparam_vals"), jl_symbol("tfunc"), jl_symbol("name"), jl_symbol("roots"), @@ -3490,16 +3495,18 @@ void jl_init_types(void) jl_symbol("specializations"), jl_symbol("module"), jl_symbol("def"), jl_symbol("file"), jl_symbol("line"), + jl_symbol("nslots"), jl_symbol("ngensym"), jl_symbol("inferred"), jl_symbol("pure"), jl_symbol("inInference")), - jl_svec(17, jl_any_type, jl_any_type, + jl_svec(19, jl_any_type, jl_any_type, jl_simplevector_type, jl_simplevector_type, jl_any_type, jl_sym_type, jl_any_type, jl_any_type, jl_any_type, jl_array_any_type, jl_module_type, jl_any_type, jl_sym_type, jl_int32_type, + jl_int32_type, jl_int32_type, jl_bool_type, jl_bool_type, jl_bool_type), 0, 1, 5); @@ -3619,6 +3626,8 @@ void jl_init_types(void) dots_sym = jl_symbol("..."); list_sym = jl_symbol("list"); unused_sym = jl_symbol("#unused#"); + slot_sym = jl_symbol("slot"); + static_parameter_sym = jl_symbol("static_parameter"); } #ifdef __cplusplus diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index be2b4469844b6..1362ca0a76ce1 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -2323,8 +2323,7 @@ (table.keys (free-vars- e (table)))) (define (analyze-vars-lambda e env captvars sp new-sp) - (let* ((args (filter (lambda (v) (not (eq? (arg-name v) UNUSED))) - (lam:args e))) + (let* ((args (lam:args e)) (locl (caddr e)) (allv (nconc (map arg-name args) locl)) (fv (let* ((fv (diff (free-vars (lam:body e)) allv)) @@ -2919,7 +2918,11 @@ f(x) = yt(x) (let ((temps? (or *very-linear-mode* (expr-contains-p (lambda (x) (and (assignment? x) (symbol? (cadr x)))) - (cons 'block (cdr lst)))))) + (cons 'block (cdr lst))))) + (simple? (every (lambda (x) (or (simple-atom? x) (symbol? x) (jlgensym? x) + (and (pair? x) + (memq (car x) '(quote inert top copyast))))) + lst))) (let loop ((lst lst) (vals '())) (if (null? lst) @@ -2927,7 +2930,7 @@ f(x) = yt(x) (let* ((arg (car lst)) (aval (compile arg break-labels #t #f))) (loop (cdr lst) - (cons (if (and temps? + (cons (if (and temps? (not simple?) (not (simple-atom? arg)) (not (jlgensym? arg)) (not (simple-atom? aval)) (not (jlgensym? aval)) (not (and (pair? arg) @@ -3236,8 +3239,8 @@ f(x) = yt(x) (loop (cdr stmts) (+ i 1))))) tbl)) -(define (renumber-labels! body label2idx) - (let loop ((stmts (cdr body))) +(define (renumber-labels! lam label2idx) + (let loop ((stmts (cdr (lam:body lam)))) (if (pair? stmts) (let ((el (car stmts))) (if (pair? el) @@ -3247,12 +3250,39 @@ f(x) = yt(x) (else #f))) (loop (cdr stmts)))))) +(define (renumber-slots lam e) + (cond ((symbol? e) + (let loop ((vi (car (lam:vinfo lam))) + (i 1)) + (cond ((null? vi) + (let nextsp ((sps (lam:sp lam)) + (j 1)) + (cond ((null? sps) e) + ((eq? e (car sps)) `(static_parameter ,j)) + (else (nextsp (cdr sps) (+ j 1)))))) + ((eq? e (caar vi)) + `(slot ,i)) + (else (loop (cdr vi) (+ i 1)))))) + ((or (atom? e) (quoted? e)) e) + ((eq? (car e) 'lambda) + (let ((body (renumber-slots e (lam:body e)))) + `(lambda ,(cadr e) ,(caddr e) ,body))) + (else (cons (car e) + (map (lambda (x) (renumber-slots lam x)) + (cdr e)))))) + (define (renumber-things ex) - (if (atom? ex) ex - (begin (if (eq? (car ex) 'lambda) - (renumber-labels! (lam:body ex) (label-to-idx-map (lam:body ex)))) - (for-each renumber-things (cdr ex)))) - ex) + (let do-labels ((ex ex)) + (if (pair? ex) + (begin (if (eq? (car ex) 'lambda) + (renumber-labels! ex (label-to-idx-map (lam:body ex)))) + (for-each do-labels (cdr ex))))) + (let do-slots ((ex ex)) + (if (atom? ex) ex + (if (eq? (car ex) 'lambda) + (renumber-slots #f ex) + (cons (car ex) + (map do-slots (cdr ex))))))) ;; expander entry point diff --git a/src/julia.h b/src/julia.h index d293159bca69b..49e30d140123c 100644 --- a/src/julia.h +++ b/src/julia.h @@ -192,6 +192,8 @@ typedef struct _jl_lambda_info_t { struct _jl_lambda_info_t *def; // original this is specialized from jl_sym_t *file; int32_t line; + int32_t nslots; + int32_t ngensym; int8_t inferred; int8_t pure; int8_t inInference; // flags to tell if inference is running on this function @@ -379,6 +381,7 @@ extern JL_DLLEXPORT jl_datatype_t *jl_typector_type; extern JL_DLLEXPORT jl_datatype_t *jl_sym_type; extern JL_DLLEXPORT jl_datatype_t *jl_symbol_type; extern JL_DLLEXPORT jl_datatype_t *jl_gensym_type; +extern JL_DLLEXPORT jl_datatype_t *jl_slot_type; extern JL_DLLEXPORT jl_datatype_t *jl_simplevector_type; extern JL_DLLEXPORT jl_typename_t *jl_tuple_typename; extern JL_DLLEXPORT jl_datatype_t *jl_anytuple_type; @@ -449,7 +452,6 @@ extern JL_DLLEXPORT jl_value_t *jl_array_uint8_type; extern JL_DLLEXPORT jl_value_t *jl_array_any_type; extern JL_DLLEXPORT jl_value_t *jl_array_symbol_type; extern JL_DLLEXPORT jl_datatype_t *jl_expr_type; -extern JL_DLLEXPORT jl_datatype_t *jl_symbolnode_type; extern JL_DLLEXPORT jl_datatype_t *jl_globalref_type; extern JL_DLLEXPORT jl_datatype_t *jl_linenumbernode_type; extern JL_DLLEXPORT jl_datatype_t *jl_labelnode_type; @@ -482,7 +484,7 @@ extern jl_sym_t *goto_sym; extern jl_sym_t *goto_ifnot_sym; extern jl_sym_t *label_sym; extern jl_sym_t *return_sym; extern jl_sym_t *lambda_sym; extern jl_sym_t *assign_sym; extern jl_sym_t *null_sym; extern jl_sym_t *body_sym; -extern jl_sym_t *method_sym; +extern jl_sym_t *method_sym; extern jl_sym_t *slot_sym; extern jl_sym_t *enter_sym; extern jl_sym_t *leave_sym; extern jl_sym_t *exc_sym; extern jl_sym_t *new_sym; extern jl_sym_t *static_typeof_sym; @@ -495,7 +497,7 @@ extern jl_sym_t *boundscheck_sym; extern jl_sym_t *inbounds_sym; extern jl_sym_t *copyast_sym; extern jl_sym_t *fastmath_sym; extern jl_sym_t *pure_sym; extern jl_sym_t *simdloop_sym; extern jl_sym_t *meta_sym; extern jl_sym_t *list_sym; -extern jl_sym_t *inert_sym; +extern jl_sym_t *inert_sym; extern jl_sym_t *static_parameter_sym; // gc ------------------------------------------------------------------------- @@ -650,11 +652,11 @@ STATIC_INLINE jl_value_t *jl_cellset(void *a, size_t i, void *x) #define jl_nfields(v) jl_datatype_nfields(jl_typeof(v)) // Not using jl_fieldref to avoid allocations -#define jl_symbolnode_sym(s) (*(jl_sym_t**)s) -#define jl_symbolnode_type(s) (((jl_value_t**)s)[1]) #define jl_linenode_file(x) (*(jl_sym_t**)x) #define jl_linenode_line(x) (((intptr_t*)x)[1]) #define jl_labelnode_label(x) (((intptr_t*)x)[0]) +#define jl_slot_number(x) (((intptr_t*)x)[0]) +#define jl_slot_get_type(x) (((jl_value_t**)x)[1]) #define jl_gotonode_label(x) (((intptr_t*)x)[0]) #define jl_globalref_mod(s) (*(jl_module_t**)s) #define jl_globalref_name(s) (((jl_sym_t**)s)[1]) @@ -769,8 +771,8 @@ static inline uint32_t jl_fielddesc_size(int8_t fielddesc_type) #define jl_is_bool(v) jl_typeis(v,jl_bool_type) #define jl_is_symbol(v) jl_typeis(v,jl_sym_type) #define jl_is_gensym(v) jl_typeis(v,jl_gensym_type) +#define jl_is_slot(v) jl_typeis(v,jl_slot_type) #define jl_is_expr(v) jl_typeis(v,jl_expr_type) -#define jl_is_symbolnode(v) jl_typeis(v,jl_symbolnode_type) #define jl_is_globalref(v) jl_typeis(v,jl_globalref_type) #define jl_is_labelnode(v) jl_typeis(v,jl_labelnode_type) #define jl_is_gotonode(v) jl_typeis(v,jl_gotonode_type) @@ -1219,10 +1221,8 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_in(jl_module_t *m, jl_value_t *ex); JL_DLLEXPORT jl_value_t *jl_toplevel_eval_in_warn(jl_module_t *m, jl_value_t *ex, int delay_warn); JL_DLLEXPORT jl_value_t *jl_load(const char *fname, size_t len); -JL_DLLEXPORT jl_value_t *jl_interpret_toplevel_expr_in(jl_module_t *m, - jl_value_t *e, - jl_svec_t *local_syms, - jl_svec_t *local_vals); +JL_DLLEXPORT jl_value_t *jl_interpret_toplevel_expr_in(jl_module_t *m, jl_value_t *e, + jl_lambda_info_t *lam); JL_DLLEXPORT jl_module_t *jl_base_relative_to(jl_module_t *m); // AST access diff --git a/src/julia_internal.h b/src/julia_internal.h index 72edff4c96b54..f9a3dda8a04a9 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -183,8 +183,6 @@ jl_value_t *jl_eval_global_var(jl_module_t *m, jl_sym_t *e); jl_value_t *jl_parse_eval_all(const char *fname, size_t len, const char *content, size_t contentlen); jl_value_t *jl_interpret_toplevel_thunk(jl_lambda_info_t *lam); -jl_value_t *jl_interpret_toplevel_thunk_with(jl_lambda_info_t *lam, - jl_value_t **loc, size_t nl); jl_value_t *jl_interpret_toplevel_expr(jl_value_t *e); jl_value_t *jl_static_eval(jl_value_t *ex, void *ctx_, jl_module_t *mod, jl_lambda_info_t *li, int sparams, int allow_alloc); @@ -206,7 +204,6 @@ jl_array_t *jl_lam_staticparams(jl_expr_t *l); jl_sym_t *jl_lam_argname(jl_lambda_info_t *li, int i); int jl_lam_vars_captured(jl_expr_t *ast); jl_expr_t *jl_lam_body(jl_expr_t *l); -int jl_local_in_linfo(jl_lambda_info_t *linfo, jl_sym_t *sym); jl_value_t *jl_first_argument_datatype(jl_value_t *argtypes); jl_value_t *jl_preresolve_globals(jl_value_t *expr, jl_lambda_info_t *lam); int jl_has_intrinsics(jl_lambda_info_t *li, jl_expr_t *e, jl_module_t *m); diff --git a/test/hashing.jl b/test/hashing.jl index 3b0d4fe4f0d26..f2f57bf6f6d11 100644 --- a/test/hashing.jl +++ b/test/hashing.jl @@ -95,9 +95,9 @@ let a = QuoteNode(1), b = QuoteNode(1.0) @test (hash(a)==hash(b)) == (a==b) end -let a = Expr(:block, SymbolNode(:a, Any)), - b = Expr(:block, SymbolNode(:a, Any)), - c = Expr(:block, SymbolNode(:c, Any)) +let a = Expr(:block, Slot(1, Any)), + b = Expr(:block, Slot(1, Any)), + c = Expr(:block, Slot(3, Any)) @test a == b && hash(a) == hash(b) @test a != c && hash(a) != hash(c) @test b != c && hash(b) != hash(c) diff --git a/test/inline.jl b/test/inline.jl index 752d5918aad25..917dc4e045171 100644 --- a/test/inline.jl +++ b/test/inline.jl @@ -16,17 +16,19 @@ function walk(func, expr) end """ -Helper to test that every SymbolNode/NewvarNode has a -corresponding varinfo entry after inlining. +Helper to test that every slot is in range after inlining. """ function test_inlined_symbols(func, argtypes) linfo = code_typed(func, argtypes)[1] locals = linfo.args[2][1] - local_names = Set([name for (name, typ, flag) in locals]) + nl = length(locals) ast = linfo.args[3] walk(ast) do e - if isa(e, NewvarNode) || isa(e, SymbolNode) - @test e.name in local_names + if isa(e, Slot) + @test 1 <= e.id <= nl + end + if isa(e, NewvarNode) + @test 1 <= e.slot.id <= nl end end end From ab5c34e42de354fff8a0b388890a4ca78a5e08d1 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 7 Mar 2016 16:11:46 -0500 Subject: [PATCH 3/6] fix interaction of old and new typeinf code during bootstrap --- base/essentials.jl | 4 ++++ base/inference.jl | 15 ++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/base/essentials.jl b/base/essentials.jl index 7d42dad0efdbb..bd8a16d87f0ab 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -125,6 +125,10 @@ function precompile(f::ANY, args::Tuple) ccall(:jl_compile_hint, Void, (Any,), Tuple{Core.Typeof(f), args...}) end +function precompile(argt::Type) + ccall(:jl_compile_hint, Void, (Any,), argt) +end + esc(e::ANY) = Expr(:escape, e) macro boundscheck(blk) diff --git a/base/inference.jl b/base/inference.jl index 199e78976f3b8..a7421d11bb7eb 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -1541,10 +1541,7 @@ function typeinf_edge(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector, nee if !needtree return (nothing, code, true) end - elseif !isa(code,Tuple) && !needtree - # TODO: temporary hack - return (nothing, code.bestguess, false) - else + elseif isa(code,Tuple) inftree, inftyp = code if linfo.inInference linfo.inferred = true @@ -1558,6 +1555,9 @@ function typeinf_edge(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector, nee linfo.ngensym = length(inftree.args[2][3]) end return (inftree, inftyp, true) # code is a tuple (ast, type) + else + # otherwise this is an InferenceState from a different bootstrap stage's + # copy of the inference code; ignore it. end end end @@ -1810,7 +1810,9 @@ function typeinf_frame(frame) for (caller, callerW) in frame.backedges # notify backedges of updated type information for caller_pc in callerW - push!(caller.ip, caller_pc) + if caller.stmt_types[caller_pc] !== () + push!(caller.ip, caller_pc) + end end end unmark_fixedpoint(frame) @@ -3366,6 +3368,9 @@ end # make sure that typeinf is executed before turning on typeinf_ext # this ensures that typeinf_ext doesn't recurse before it can add the item to the workq + +precompile(methods(typeinf_edge).defs.sig) + for m in _methods_by_ftype(Tuple{typeof(typeinf_loop), Vararg{Any}}, 10) typeinf(m[3].func, m[1], m[2], true) end From 1bc01687633b7dfb59d6a581110ca52c3538e42e Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Tue, 8 Mar 2016 00:20:28 -0500 Subject: [PATCH 4/6] move var metadata from LambdaInfo.ast to fields of LambdaInfo LambdaInfo.ast is now LambdaInfo.code, and is just an array of statements store LambdaInfo objects in the tfunc cache, instead of just ASTs now type inference operates on InferenceState.linfo in place, and eventually copies the results to InferenceState.destination if necessary. --- base/expr.jl | 16 +- base/inference.jl | 456 ++++++++++++++++++++++------------------ base/interactiveutil.jl | 16 +- base/methodshow.jl | 9 +- base/reflection.jl | 15 +- base/serialize.jl | 27 ++- base/show.jl | 7 +- base/stacktraces.jl | 4 +- src/alloc.c | 95 ++++++--- src/ast.c | 49 +---- src/builtins.c | 10 +- src/cgutils.cpp | 7 +- src/codegen.cpp | 72 +++---- src/dump.c | 55 ++--- src/gf.c | 33 +-- src/init.c | 4 - src/interpreter.c | 35 +-- src/jltypes.c | 25 ++- src/julia.h | 44 ++-- src/julia_internal.h | 4 +- src/toplevel.c | 127 ++++++----- test/core.jl | 2 +- test/inference.jl | 6 +- test/inline.jl | 5 +- test/meta.jl | 2 +- 25 files changed, 608 insertions(+), 517 deletions(-) diff --git a/base/expr.jl b/base/expr.jl index 875bf5b56f965..e4d8a255fec99 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -133,7 +133,7 @@ end function popmeta!(body::Expr, sym::Symbol) body.head == :block || return false, [] - found, metaex = findmeta_block(body) + found, metaex = findmeta_block(body.args) if !found return false, [] end @@ -151,23 +151,29 @@ function popmeta!(body::Expr, sym::Symbol) false, [] end popmeta!(arg, sym) = (false, []) +function popmeta!(body::Array{Any,1}, sym::Symbol) + ex = Expr(:block); ex.args = body + popmeta!(ex, sym) +end function findmeta(ex::Expr) if ex.head == :function || (ex.head == :(=) && typeof(ex.args[1]) == Expr && ex.args[1].head == :call) body::Expr = ex.args[2] body.head == :block || error(body, " is not a block expression") - return findmeta_block(ex) + return findmeta_block(ex.args) end error(ex, " is not a function expression") end -function findmeta_block(ex::Expr) - for a in ex.args +findmeta(ex::Array{Any,1}) = findmeta_block(ex) + +function findmeta_block(exargs) + for a in exargs if isa(a, Expr) if (a::Expr).head == :meta return true, a::Expr elseif (a::Expr).head == :block - found, exb = findmeta_block(a) + found, exb = findmeta_block(a.args) if found return found, exb end diff --git a/base/inference.jl b/base/inference.jl index a7421d11bb7eb..e7d524bb694ac 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -20,11 +20,8 @@ end type InferenceState atypes #::Type # type sig - ast #::Expr - body::Array{Any,1} # ast body sp::SimpleVector # static parameters gensym_types::Array{Any,1} # types of the GenSym's in this function - vinfo::Array{Any,1} # variable properties label_counter::Int # index of the current highest label for this function fedbackvars::Dict{GenSym, Bool} mod::Module @@ -33,6 +30,7 @@ type InferenceState # info on the state of inference and the linfo linfo::LambdaInfo + destination::LambdaInfo # results need to be copied here when we finish nargs::Int stmt_types::Vector{Any} # return type @@ -60,15 +58,10 @@ type InferenceState inferred::Bool tfunc_idx::Int - function InferenceState(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector, ast, optimize::Bool) - if !isa(ast,Expr) - ast = ccall(:jl_uncompress_ast, Any, (Any,Any), linfo, ast) - end - assert(is(ast.head,:lambda)) - vinflist = ast.args[2][1]::Array{Any,1} - nslots = length(vinflist) - body = (ast.args[3].args)::Array{Any,1} - nl = label_counter(body)+1 + function InferenceState(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector, optimize::Bool) + @assert isa(linfo.code,Array{Any,1}) + nslots = length(linfo.slotnames) + nl = label_counter(linfo.code)+1 if length(linfo.sparam_vals) > 0 sp = linfo.sparam_vals @@ -78,15 +71,14 @@ type InferenceState sp = sparams end - n = length(body) + n = length(linfo.code) s = Any[ () for i=1:n ] # initial types s[1] = Any[ VarState(Bottom,true) for i=1:nslots ] - la = length(ast.args[1]) + la = linfo.nargs if la > 0 - lastarg = ast.args[1][la] - if is_rest_arg(lastarg) + if linfo.isva if atypes === Tuple if la > 1 atypes = Tuple{Any[Any for i=1:la-1]..., Tuple.parameters[1]} @@ -126,8 +118,8 @@ type InferenceState @assert la == 0 # wrong number of arguments end - gensym_uses = find_gensym_uses(body) - gensym_types = Any[ NF for i=1:length(gensym_uses) ] + gensym_uses = find_gensym_uses(linfo.code) + gensym_types = linfo.gensymtypes gensym_init = copy(gensym_types) # exception handlers @@ -139,9 +131,9 @@ type InferenceState push!(W, 1) #initial pc to visit frame = new( - atypes, ast, body, sp, gensym_types, vinflist, nl, Dict{GenSym, Bool}(), linfo.module, 0, false, + atypes, sp, gensym_types, nl, Dict{GenSym, Bool}(), linfo.module, 0, false, - linfo, la, s, Union{}, W, n, + linfo, linfo, la, s, Union{}, W, n, cur_hand, handler_at, n_handlers, gensym_uses, gensym_init, ObjectIdDict(), #Dict{InferenceState, Vector{LineNum}}(), @@ -1504,8 +1496,34 @@ function newvar!(sv::InferenceState, typ) return GenSym(id) end -is_rest_arg(arg::ANY) = (ccall(:jl_is_rest_arg,Int32,(Any,), arg) != 0) - +# copy a LambdaInfo just enough to make it not share data with li.def +function unshare_linfo(li::LambdaInfo) + if li.nargs > 0 + if li === li.def + li = ccall(:jl_copy_lambda_info, Any, (Any,), li)::LambdaInfo + end + if !isa(li.code, Array{Any,1}) + li.code = ccall(:jl_uncompress_ast, Any, (Any,Any), li, li.code) + elseif li.code === li.def.code && li !== li.def + li.code = astcopy(li.code) + end + li.slotnames = copy(li.slotnames) + li.slotflags = copy(li.slotflags) + if isa(li.slottypes,Array) + li.slottypes = copy(li.slottypes) + end + if isa(li.gensymtypes,Array) + li.gensymtypes = copy(li.gensymtypes) + end + end + if !isa(li.slottypes,Array) + li.slottypes = Any[ Any for i = 1:length(li.slotnames) ] + end + if !isa(li.gensymtypes,Array) + li.gensymtypes = Any[ NF for i = 1:(li.gensymtypes::Int) ] + end + return li +end #### entry points for inferring a LambdaInfo given a type signature #### @@ -1514,8 +1532,8 @@ function typeinf_edge(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector, nee if linfo.module === Core && isempty(sparams) && isempty(linfo.sparam_vals) atypes = Tuple end - local ast::Expr, tfunc_idx = -1 - curtype = Bottom + local tfunc_idx = -1 + local frame # check cached t-functions # linfo.def is the original unspecialized version of a method. # we aggregate all saved type inference data there. @@ -1528,33 +1546,22 @@ function typeinf_edge(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector, nee if isa(code, InferenceState) # inference on this signature is in progress frame = code - tfunc_idx = i if linfo.inInference # record the LambdaInfo where this result should be cached when it is finished - @assert !frame.linfo.inInference - frame.linfo = linfo + @assert frame.destination === frame.linfo || frame.destination === linfo + frame.destination = linfo end + tfunc_idx = i + break elseif isa(code, Type) - curtype = code::Type # sometimes just a return type is stored here. if a full AST # is not needed, we can return it. if !needtree return (nothing, code, true) end - elseif isa(code,Tuple) - inftree, inftyp = code - if linfo.inInference - linfo.inferred = true - linfo.ast = inftree - linfo.rettype = inftyp - linfo.inInference = false - if !isa(inftree,Expr) - inftree = ccall(:jl_uncompress_ast, Any, (Any,Any), linfo, inftree) - end - linfo.nslots = length(inftree.args[2][1]) - linfo.ngensym = length(inftree.args[2][3]) - end - return (inftree, inftyp, true) # code is a tuple (ast, type) + elseif isa(code,LambdaInfo) + @assert code.inferred + return (code, code.rettype, true) else # otherwise this is an InferenceState from a different bootstrap stage's # copy of the inference code; ignore it. @@ -1591,9 +1598,9 @@ function typeinf_edge(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector, nee end end # add lam to be inferred and record the edge - ast = ccall(:jl_prepare_ast, Any, (Any,), linfo)::Expr + linfo = unshare_linfo(linfo) # our stack frame inference context - frame = InferenceState(linfo, atypes, sparams, ast, optimize) + frame = InferenceState(linfo, atypes, sparams, optimize) if cached #println(linfo) @@ -1627,7 +1634,7 @@ function typeinf_edge(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector, nee end end typeinf_loop(frame) - return (frame.ast, frame.bestguess, frame.inferred) + return (frame.linfo, frame.bestguess, frame.inferred) end function typeinf_edge(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector, caller) @@ -1644,7 +1651,21 @@ function typeinf_uncached(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector, return typeinf_edge(linfo, atypes, sparams, true, optimize, false, nothing) end function typeinf_ext(linfo::LambdaInfo, toplevel::Bool) - return typeinf_edge(linfo, linfo.specTypes, svec(), true, true, true, nothing) + (code, _t, _) = typeinf_edge(linfo, linfo.specTypes, svec(), true, true, true, nothing) + if code.inferred + linfo.inferred = true + linfo.inInference = false + if linfo !== code + linfo.code = code.code + linfo.slotnames = code.slotnames + linfo.slottypes = code.slottypes + linfo.slotflags = code.slotflags + linfo.gensymtypes = code.gensymtypes + linfo.rettype = code.rettype + linfo.pure = code.pure + end + end + nothing end @@ -1728,7 +1749,7 @@ function typeinf_frame(frame) else frame.cur_hand = frame.handler_at[pc] end - stmt = frame.body[pc] + stmt = frame.linfo.code[pc] changes = abstract_interpret(stmt, s[pc]::Array{Any,1}, frame) if changes === () # if there was a Expr(:static_typeof) on this line, @@ -1955,41 +1976,46 @@ function finish(me::InferenceState) me.gensym_types[i] = Union{} end end - fulltree = type_annotate(me.ast, me.stmt_types, me, me.bestguess, me.nargs) + type_annotate!(me.linfo, me.stmt_types, me, me.bestguess, me.nargs) # make sure (meta pure) is stripped from full tree - @assert fulltree.args[3].head === :body - body = Expr(:block) - body.args = fulltree.args[3].args::Array{Any,1} - ispure = popmeta!(body, :pure)[1] + ispure = popmeta!(me.linfo.code, :pure)[1] # run optimization passes on fulltree if me.optimize if JLOptions().can_inline == 1 - fulltree.args[3] = inlining_pass(fulltree.args[3], me, fulltree)[1] - inbounds_meta_elim_pass(fulltree.args[3]) + inlining_pass!(me.linfo, me) + inbounds_meta_elim_pass!(me.linfo.code) end - alloc_elim_pass(fulltree, me) - getfield_elim_pass(fulltree.args[3], me) - reindex_labels!(fulltree.args[3], me) + alloc_elim_pass!(me.linfo, me) + getfield_elim_pass!(me.linfo, me) + reindex_labels!(me.linfo, me) end # finalize and record the linfo result - me.ast = fulltree me.inferred = true - compressedtree = ccall(:jl_compress_ast, Any, (Any,Any), me.linfo.def, fulltree) - if me.linfo.inInference - me.linfo.inferred = true - me.linfo.pure = ispure - me.linfo.ast = compressedtree - me.linfo.rettype = me.bestguess - me.linfo.inInference = false - me.linfo.nslots = length(fulltree.args[2][1]) - me.linfo.ngensym = length(me.gensym_types) + me.linfo.inferred = true + me.linfo.inInference = false + me.linfo.pure = ispure + compressedtree = ccall(:jl_compress_ast, Any, (Any,Any), me.linfo.def, me.linfo.code) + me.linfo.code = compressedtree + me.linfo.rettype = me.bestguess + + if me.destination !== me.linfo + out = me.destination + out.inferred = true + out.inInference = false + out.code = me.linfo.code + out.slotnames = me.linfo.slotnames + out.slottypes = me.linfo.slottypes + out.slotflags = me.linfo.slotflags + out.gensymtypes = me.linfo.gensymtypes + out.rettype = me.linfo.rettype + out.pure = me.linfo.pure end if me.tfunc_idx != -1 - me.linfo.def.tfunc[me.tfunc_idx + 1] = (compressedtree, me.bestguess) + me.linfo.def.tfunc[me.tfunc_idx + 1] = me.linfo me.linfo.def.tfunc[me.tfunc_idx + 2] = false end @@ -2064,7 +2090,7 @@ function eval_annotate(e::ANY, vtypes::ANY, sv::InferenceState, decls, undefs) end # annotate types of all symbols in AST -function type_annotate(ast::Expr, states::Array{Any,1}, sv::ANY, rettype::ANY, nargs) +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) @@ -2072,7 +2098,7 @@ function type_annotate(ast::Expr, states::Array{Any,1}, sv::ANY, rettype::ANY, n for i = 1:nargs decls[i] = states[1][i].typ end - body = ast.args[3].args::Array{Any,1} + body = linfo.code for i=1:length(body) st_i = states[i] if st_i !== () @@ -2080,21 +2106,17 @@ function type_annotate(ast::Expr, states::Array{Any,1}, sv::ANY, rettype::ANY, n body[i] = eval_annotate(body[i], st_i, sv, decls, undefs) end end - ast.args[3].typ = rettype # add declarations for variables that are always the same type for i = 1:nslots - vi = ast.args[2][1][i] if decls[i] !== NF - vi[2] = decls[i] + linfo.slottypes[i] = decls[i] end if undefs[i] - vi[3] |= 32 + linfo.slotflags[i] |= 32 end end - ast.args[2][3] = sv.gensym_types - - return ast + nothing end # replace slots 1:na with argexprs, static params with spvals, and increment @@ -2281,7 +2303,7 @@ end # static parameters are ok if all the static parameter values are leaf types, # meaning they are fully known. # `ft` is the type of the function. `f` is the exact function if known, or else `nothing`. -function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::InferenceState, enclosing_ast::Expr) +function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::InferenceState, enclosing::LambdaInfo) local linfo, metharg::Type, argexprs = e.args, @@ -2403,7 +2425,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference # # check call stack to see if this argument list is growing # st = inference_stack # while !isa(st, EmptyCallStack) - # if st.ast === linfo.def.ast && length(atypes) > length(st.types) + # if st.code === linfo.def.code && length(atypes) > length(st.types) # atypes = limit_tuple_type(atypes) # meth = _methods(f, atypes, 1) # if meth === false || length(meth) != 1 @@ -2424,21 +2446,21 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference methargs = metharg.parameters nm = length(methargs) - (ast, ty, inferred) = typeinf(linfo, metharg, methsp, true) - if is(ast,nothing) || !inferred + (linfo, ty, inferred) = typeinf(linfo, metharg, methsp, true) + if is(linfo,nothing) || !inferred return NF end + ast = linfo.code - if !isa(ast,Expr) + if !isa(ast,Array{Any,1}) ast = ccall(:jl_uncompress_ast, Any, (Any,Any), linfo, ast) else ast = astcopy(ast) end - ast = ast::Expr - vinflist = ast.args[2][1]::Array{Any,1} + ast = ast::Array{Any,1} body = Expr(:block) - body.args = ast.args[3].args::Array{Any,1} + body.args = ast propagate_inbounds, _ = popmeta!(body, :propagate_inbounds) cost::Int = 1000 if incompletematch @@ -2483,12 +2505,11 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference filter!(x->!(isa(x,Expr) && x.head === :meta && isempty(x.args)), body.args) - enc_vinflist = enclosing_ast.args[2][1]::Array{Any,1} - na = length(ast.args[1]) + na = linfo.nargs # check for vararg function isva = false - if na > 0 && is_rest_arg(ast.args[1][na]) + if na > 0 && linfo.isva if length(argexprs) < na - 1 return (Expr(:call, TopNode(:error), "too few arguments"), []) end @@ -2592,9 +2613,9 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference islocal = false # if the argument name is also used as a local variable, # we need to keep it as a variable name - if vinflist[i][3] != 0 + if linfo.slotflags[i] != 0 islocal = true - aeitype = tmerge(aeitype, vinflist[i][2]) + aeitype = tmerge(aeitype, linfo.slottypes[i]) end # ok for argument to occur more than once if the actual argument @@ -2647,7 +2668,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference vnew = newvar!(sv, aeitype) argexprs[i] = vnew else - vnew = add_slot!(enclosing_ast, aeitype, #=SSA=#false) + vnew = add_slot!(enclosing, aeitype, #=SSA=#false) argexprs[i] = vnew end unshift!(stmts, Expr(:(=), vnew, aei)) @@ -2669,23 +2690,20 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference end # re-number the GenSyms and copy their type-info to the new ast - gensym_types = ast.args[2][3] - if gensym_types != 0 - if (isa(gensym_types,Integer)) - gensym_types = Any[Any for i = 1:ast.args[2][3]] - end - if !isempty(gensym_types) - incr = length(sv.gensym_types) - if incr != 0 - body = gensym_increment(body, incr) - end - append!(sv.gensym_types, ast.args[2][3]) + gensym_types = linfo.gensymtypes + if !isempty(gensym_types) + incr = length(sv.gensym_types) + if incr != 0 + body = gensym_increment(body, incr) end + append!(sv.gensym_types, gensym_types) end # ok, substitute argument expressions for argument names in the body - body = substitute!(body, na, argexprs, spvals, length(enc_vinflist)-na) - append!(enc_vinflist, vinflist[na+1:end]) + body = substitute!(body, na, argexprs, spvals, length(enclosing.slotnames)-na) + append!(enclosing.slotnames, linfo.slotnames[na+1:end]) + append!(enclosing.slottypes, linfo.slottypes[na+1:end]) + append!(enclosing.slotflags, linfo.slotflags[na+1:end]) # make labels / goto statements unique newlabels = zeros(Int,label_counter(body.args)+1) @@ -2715,7 +2733,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference # convert return statements into a series of goto's retstmt = genlabel(sv) - rettype = (ast.args[3]::Expr).typ + rettype = linfo.rettype local retval multiret = false lastexpr = pop!(body.args) @@ -2734,7 +2752,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference if a.head === :return if !multiret # create slot first time - retval = add_slot!(enclosing_ast, rettype, false) + retval = add_slot!(enclosing, rettype, false) end multiret = true unshift!(a.args, retval) @@ -2838,7 +2856,31 @@ end const corenumtype = Union{Int32,Int64,Float32,Float64} -function inlining_pass(e::Expr, sv, ast) +function inlining_pass!(linfo::LambdaInfo, sv::InferenceState) + eargs = linfo.code + stmts = [] + i = 1 + while i <= length(eargs) + ei = eargs[i] + if isa(ei,Expr) + res = inlining_pass(ei, sv, linfo) + eargs[i] = res[1] + else + i += 1 + continue + end + if isa(res[2],Array) + sts = res[2]::Array{Any,1} + for j = 1:length(sts) + insert!(eargs, i, sts[j]) + i += 1 + end + end + i += 1 + end +end + +function inlining_pass(e::Expr, sv, linfo) if e.head === :method # avoid running the inlining pass on function definitions return (e,()) @@ -2848,25 +2890,6 @@ function inlining_pass(e::Expr, sv, ast) return (e,()) end stmts = [] - if e.head === :body - i = 1 - while i <= length(eargs) - ei = eargs[i] - if isa(ei,Expr) - res = inlining_pass(ei, sv, ast) - eargs[i] = res[1] - if isa(res[2],Array) - sts = res[2]::Array{Any,1} - for j = 1:length(sts) - insert!(eargs, i, sts[j]) - i += 1 - end - end - end - i += 1 - end - return (e, stmts) - end arg1 = eargs[1] # don't inline first (global) arguments of ccall, as this needs to be evaluated # by the interpreter and inlining might put in something it can't handle, @@ -2895,7 +2918,7 @@ function inlining_pass(e::Expr, sv, ast) else argloc = eargs end - res = inlining_pass(ei::Expr, sv, ast) + res = inlining_pass(ei::Expr, sv, linfo) res1 = res[1] if has_stmts && !effect_free(res1, sv, false) restype = exprtype(res1,sv) @@ -2970,7 +2993,7 @@ function inlining_pass(e::Expr, sv, ast) (a === Bottom || isvarargtype(a)) && return (e, stmts) ata[i] = a end - res = inlineable(f, ft, e, ata, sv, ast) + res = inlineable(f, ft, e, ata, sv, linfo) if isa(res,Tuple) if isa(res[2],Array) && !isempty(res[2]) append!(stmts,res[2]) @@ -3029,10 +3052,11 @@ function inlining_pass(e::Expr, sv, ast) return (e,stmts) end -function add_slot!(ast, typ, is_sa) - vinflist = ast.args[2][1]::Array{Any,1} - id = length(vinflist)+1 - push!(vinflist, Any[:__temp__, typ, 2+16*is_sa]) +function add_slot!(linfo::LambdaInfo, typ, is_sa) + id = length(linfo.slotnames)+1 + push!(linfo.slotnames, :__temp__) + push!(linfo.slottypes, typ) + push!(linfo.slotflags, 2+16*is_sa) Slot(id, typ) end @@ -3052,68 +3076,73 @@ function is_known_call_p(e::Expr, pred, sv) return !is(f,false) && pred(_ieval(f,sv)) end -is_var_assigned(ast, v) = isa(v,Slot) && ast.args[2][1][v.id][3]&2 != 0 +is_var_assigned(linfo, v) = isa(v,Slot) && linfo.slotflags[v.id]&2 != 0 -function delete_var!(ast, id, T) +function delete_var!(linfo, id, T) filter!(x->!(isa(x,Expr) && (x.head === :(=) || x.head === :const) && isa(x.args[1],T) && x.args[1].id == id), - ast.args[3].args) - ast + linfo.code) + linfo +end + +function slot_replace!(linfo::LambdaInfo, id, rhs, T) + for i = 1:length(linfo.code) + linfo.code[i] = _slot_replace!(linfo.code[i], id, rhs, T) + end + linfo end -function slot_replace!(e, id, rhs, T) +function _slot_replace!(e, id, rhs, T) if isa(e,T) && e.id == id return rhs end if isa(e,Expr) for i = 1:length(e.args) - e.args[i] = slot_replace!(e.args[i], id, rhs, T) + e.args[i] = _slot_replace!(e.args[i], id, rhs, T) end end return e end -occurs_undef(var::Int, expr, varinfo) = - varinfo[var][3]&32 != 0 && occurs_more(expr, e->(isa(e,Slot) && e.id==var), 0)>0 +occurs_undef(var::Int, expr, flags) = + flags[var]&32 != 0 && occurs_more(expr, e->(isa(e,Slot) && e.id==var), 0)>0 # remove all single-assigned vars v in "v = x" where x is an argument # and not assigned. # "sa" is the result of find_sa_vars # T: Slot or Gensym -function remove_redundant_temp_vars(ast, sa, T) - varinfo = ast.args[2][1] - gensym_types = ast.args[2][3] - body = ast.args[3] +function remove_redundant_temp_vars(linfo, sa, T) + flags = linfo.slotflags + gensym_types = linfo.gensymtypes + bexpr = Expr(:block); bexpr.args = linfo.code for (v,init) in sa - if (isa(init, Slot) && !is_var_assigned(ast, init)) + if (isa(init, Slot) && !is_var_assigned(linfo, init)) # this transformation is not valid for vars used before def. # we need to preserve the point of assignment to know where to # throw errors (issue #4645). - if T===GenSym || !occurs_undef(v, body, varinfo) + if T===GenSym || !occurs_undef(v, bexpr, flags) # the transformation is not ideal if the assignment # is present for the auto-unbox functionality # (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] : varinfo[v][2]) - delete_var!(ast, v, T) - slot_replace!(body, v, init, T) + if init.typ <: (T===GenSym ? gensym_types[v+1] : linfo.slottypes[v]) + delete_var!(linfo, v, T) + slot_replace!(linfo, v, init, T) end end end end - ast + linfo end # compute set of slots assigned once -function find_sa_vars(ast) - body = ast.args[3].args +function find_sa_vars(linfo::LambdaInfo) + body = linfo.code av = ObjectIdDict() av2 = ObjectIdDict() gss = ObjectIdDict() - vinfos = ast.args[2][1]::Array{Any,1} - nargs = length(ast.args[1]) - args = ast.args[1] + nargs = linfo.nargs for i = 1:length(body) e = body[i] if isa(e,Expr) && is(e.head,:(=)) @@ -3171,60 +3200,67 @@ end # removes inbounds metadata if we never encounter an inbounds=true or # boundscheck context in the method body -function inbounds_meta_elim_pass(e::Expr) +function inbounds_meta_elim_pass!(code::Array{Any,1}) if findfirst(x -> isa(x, Expr) && ((x.head === :inbounds && x.args[1] == true) || x.head === :boundscheck), - e.args) == 0 - filter!(x -> !(isa(x, Expr) && x.head === :inbounds), e.args) + code) == 0 + filter!(x -> !(isa(x, Expr) && x.head === :inbounds), code) end end # does the same job as alloc_elim_pass for allocations inline in getfields # TODO can probably be removed when we switch to a linear IR -function getfield_elim_pass(e::Expr, sv) +function getfield_elim_pass!(linfo::LambdaInfo, sv) + body = linfo.code + for i = 1:length(body) + body[i] = _getfield_elim_pass!(body[i], sv) + end +end + +function _getfield_elim_pass!(e::Expr, sv) for i = 1:length(e.args) - ei = e.args[i] - if isa(ei,Expr) - getfield_elim_pass(ei, sv) - if is_known_call(ei, getfield, sv) && length(ei.args)==3 && - (isa(ei.args[3],Int) || isa(ei.args[3],QuoteNode)) - e1 = ei.args[2] - j = ei.args[3] - if isa(e1,Expr) - alloc = is_immutable_allocation(e1, sv) - if !is(alloc, false) - flen, fnames = alloc - if isa(j,QuoteNode) - j = findfirst(fnames, j.value) - end - if 1 <= j <= flen - ok = true - for k = 2:length(e1.args) - k == j+1 && continue - if !effect_free(e1.args[k], sv, true) - ok = false; break - end - end - if ok - e.args[i] = e1.args[j+1] - end + e.args[i] = _getfield_elim_pass!(e.args[i], sv) + end + if is_known_call(e, getfield, sv) && length(e.args)==3 && + (isa(e.args[3],Int) || isa(e.args[3],QuoteNode)) + e1 = e.args[2] + j = e.args[3] + if isa(e1,Expr) + alloc = is_immutable_allocation(e1, sv) + if !is(alloc, false) + flen, fnames = alloc + if isa(j,QuoteNode) + j = findfirst(fnames, j.value) + end + if 1 <= j <= flen + ok = true + for k = 2:length(e1.args) + k == j+1 && continue + if !effect_free(e1.args[k], sv, true) + ok = false; break end end - elseif isa(e1,Tuple) && isa(j,Int) && (1 <= j <= length(e1)) - e1j = e1[j] - if !(isa(e1j,Number) || isa(e1j,AbstractString) || isa(e1j,Tuple) || - isa(e1j,Type)) - e1j = QuoteNode(e1j) + if ok + return e1.args[j+1] end - e.args[i] = e1j - elseif isa(e1,QuoteNode) && isa(e1.value,Tuple) && isa(j,Int) && (1 <= j <= length(e1.value)) - e.args[i] = QuoteNode(e1.value[j]) end end + elseif isa(e1,Tuple) && isa(j,Int) && (1 <= j <= length(e1)) + e1j = e1[j] + if !(isa(e1j,Number) || isa(e1j,AbstractString) || isa(e1j,Tuple) || + isa(e1j,Type)) + e1j = QuoteNode(e1j) + end + return e1j + elseif isa(e1,QuoteNode) && isa(e1.value,Tuple) && isa(j,Int) && (1 <= j <= length(e1.value)) + return QuoteNode(e1.value[j]) end end + return e end +_getfield_elim_pass!(e::ANY, sv) = e + # check if e is a successful allocation of an immutable struct # if it is, returns (n,f) such that it is always valid to call # getfield(..., 1 <= x <= n) or getfield(..., x in f) on the result @@ -3250,14 +3286,15 @@ function is_immutable_allocation(e :: ANY, sv::InferenceState) end false end + # eliminate allocation of unnecessary immutables # that are only used as arguments to safe getfield calls -function alloc_elim_pass(ast::Expr, sv::InferenceState) - bexpr = ast.args[3]::Expr - body = (ast.args[3].args)::Array{Any,1} - vs, gs = find_sa_vars(ast) - remove_redundant_temp_vars(ast, vs, Slot) - remove_redundant_temp_vars(ast, gs, GenSym) +function alloc_elim_pass!(linfo::LambdaInfo, sv::InferenceState) + body = linfo.code + bexpr = Expr(:block); bexpr.args = body + vs, gs = find_sa_vars(linfo) + remove_redundant_temp_vars(linfo, vs, Slot) + remove_redundant_temp_vars(linfo, gs, GenSym) i = 1 while i < length(body) e = body[i] @@ -3295,14 +3332,14 @@ function alloc_elim_pass(ast::Expr, sv::InferenceState) end end i += n_ins - replace_getfield!(ast, bexpr, var, vals, field_names, sv, i) + replace_getfield!(linfo, bexpr, var, vals, field_names, sv, i) else i += 1 end end end -function replace_getfield!(ast, e::Expr, tupname, vals, field_names, sv, i0) +function replace_getfield!(linfo::LambdaInfo, e::Expr, tupname, vals, field_names, sv, i0) for i = i0:length(e.args) a = e.args[i] if isa(a,Expr) && is_known_call(a, getfield, sv) && @@ -3321,7 +3358,7 @@ function replace_getfield!(ast, e::Expr, tupname, vals, field_names, sv, i0) val = val::Slot if a.typ <: val.typ && !typeseq(a.typ,val.typ) val.typ = a.typ - ast.args[2][1][val.id][2] = a.typ + linfo.slottypes[val.id] = a.typ end elseif isa(val,GenSym) val = val::GenSym @@ -3332,25 +3369,26 @@ function replace_getfield!(ast, e::Expr, tupname, vals, field_names, sv, i0) end e.args[i] = val elseif isa(a, Expr) - replace_getfield!(ast, a::Expr, tupname, vals, field_names, sv, 1) + replace_getfield!(linfo, a::Expr, tupname, vals, field_names, sv, 1) end end end # fix label numbers to always equal the statement index of the label -function reindex_labels!(e, sv) +function reindex_labels!(linfo::LambdaInfo, sv::InferenceState) + body = linfo.code mapping = zeros(Int, sv.label_counter) - for i = 1:length(e.args) - el = e.args[i] + for i = 1:length(body) + el = body[i] if isa(el,LabelNode) mapping[el.label] = i - e.args[i] = LabelNode(i) + body[i] = LabelNode(i) end end - for i = 1:length(e.args) - el = e.args[i] + for i = 1:length(body) + el = body[i] if isa(el,GotoNode) - e.args[i] = GotoNode(mapping[el.label]) + body[i] = GotoNode(mapping[el.label]) elseif isa(el,Expr) if el.head === :gotoifnot el.args[2] = mapping[el.args[2]] diff --git a/base/interactiveutil.jl b/base/interactiveutil.jl index d23997108a5d0..89aee2e807507 100644 --- a/base/interactiveutil.jl +++ b/base/interactiveutil.jl @@ -225,17 +225,19 @@ versioninfo(verbose::Bool) = versioninfo(STDOUT,verbose) function code_warntype(io::IO, f, t::ANY) emph_io = IOContext(io, :TYPEEMPHASIZE => true) - ct = code_typed(f, t) - for ast in ct + for li in code_typed(f, t) println(emph_io, "Variables:") - vars = ast.args[2][1] - for v in vars - print(emph_io, " ", v[1]) - show_expr_type(emph_io, v[2], true) + for i = 1:length(li.slotnames) + print(emph_io, " ", li.slotnames[i]) + if isa(li.slottypes,Array) + show_expr_type(emph_io, li.slottypes[i], true) + end print(emph_io, '\n') end print(emph_io, "\nBody:\n ") - show_unquoted(emph_io, ast.args[3], 2) + body = Expr(:body); body.args = uncompressed_ast(li) + body.typ = li.rettype + show_unquoted(emph_io, body, 2) print(emph_io, '\n') end nothing diff --git a/base/methodshow.jl b/base/methodshow.jl index 9b9b52eaaa24d..5ebb194722512 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -34,8 +34,7 @@ function arg_decl_parts(m::Method) tv = Any[tv...] end li = m.func - e = uncompressed_ast(li) - argnames = e.args[1] + argnames = li.slotnames[1:li.nargs] s = symbol("?") decls = [argtype_decl(:tvar_env => tv, get(argnames,i,s), m.sig.parameters[i]) for i = 1:length(m.sig.parameters)] @@ -49,11 +48,7 @@ function kwarg_decl(m::Method, kwtype::DataType) while d !== nothing if typeseq(d.sig, sig) li = d.func - e = uncompressed_ast(li) - argnames = Any[(isa(n,Expr) ? n.args[1] : n) for n in e.args[1]] - kwargs = filter!(x->!(x in argnames || '#' in string(x)), - Any[x[1] for x in e.args[2][1]]) - return kwargs + return filter(x->!('#' in string(x)), li.slotnames[li.nargs+1:end]) end d = d.next end diff --git a/base/reflection.jl b/base/reflection.jl index 633fd41f937da..f9c7ffa9cf693 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -170,7 +170,7 @@ end tt_cons(t::ANY, tup::ANY) = (@_pure_meta; Tuple{t, (isa(tup, Type) ? tup.parameters : tup)...}) -code_lowered(f, t::ANY=Tuple) = map(m->uncompressed_ast(m.func), methods(f, t)) +code_lowered(f, t::ANY=Tuple) = map(m->m.func, methods(f, t)) function methods(f::ANY,t::ANY) if isa(f,Builtin) throw(ArgumentError("argument is not a generic function")) @@ -249,7 +249,7 @@ done(mt::MethodTable, m::Method) = false done(mt::MethodTable, i::Void) = true uncompressed_ast(l::LambdaInfo) = - isa(l.ast,Expr) ? l.ast : ccall(:jl_uncompress_ast, Any, (Any,Any), l, l.ast) + isa(l.code,Array{Any,1}) ? l.code : ccall(:jl_uncompress_ast, Any, (Any,Any), l, l.code) # Printing code representations in IR and assembly function _dump_function(f, t::ANY, native, wrapper, strip_ir_metadata, dump_module) @@ -295,14 +295,11 @@ function code_typed(f::ANY, types::ANY=Tuple; optimize=true) for x in _methods(f,types,-1) linfo = func_for_method_checked(x, types) if optimize - (tree, ty) = Core.Inference.typeinf(linfo, x[1], x[2], true) + (li, ty) = Core.Inference.typeinf(linfo, x[1], x[2], true) else - (tree, ty) = Core.Inference.typeinf_uncached(linfo, x[1], x[2], optimize=false) + (li, ty) = Core.Inference.typeinf_uncached(linfo, x[1], x[2], optimize=false) end - if !isa(tree, Expr) - tree = ccall(:jl_uncompress_ast, Any, (Any,Any), linfo, tree) - end - push!(asts, tree) + push!(asts, li) end asts end @@ -312,7 +309,7 @@ function return_types(f::ANY, types::ANY=Tuple) rt = [] for x in _methods(f,types,-1) linfo = func_for_method_checked(x,types) - (tree, ty) = Core.Inference.typeinf(linfo, x[1], x[2]) + (_li, ty) = Core.Inference.typeinf(linfo, x[1], x[2]) push!(rt, ty) end rt diff --git a/base/serialize.jl b/base/serialize.jl index a43320b237513..27a21e4fd3c56 100644 --- a/base/serialize.jl +++ b/base/serialize.jl @@ -310,6 +310,10 @@ function serialize(s::SerializationState, linfo::LambdaInfo) writetag(s.io, LAMBDASTATICDATA_TAG) serialize(s, object_number(linfo)) serialize(s, uncompressed_ast(linfo)) + serialize(s, linfo.slotnames) + serialize(s, linfo.slottypes) + serialize(s, linfo.slotflags) + serialize(s, linfo.gensymtypes) if isdefined(linfo.def, :roots) serialize(s, linfo.def.roots::Vector{Any}) else @@ -323,6 +327,8 @@ function serialize(s::SerializationState, linfo::LambdaInfo) serialize(s, linfo.file) serialize(s, linfo.line) serialize(s, linfo.pure) + serialize(s, linfo.nargs) + serialize(s, linfo.isva) end function serialize(s::SerializationState, t::Task) @@ -552,7 +558,11 @@ function deserialize(s::SerializationState, ::Type{LambdaInfo}) makenew = true end deserialize_cycle(s, linfo) - ast = deserialize(s)::Expr + code = deserialize(s) + slotnames = deserialize(s) + slottypes = deserialize(s) + slotflags = deserialize(s) + gensymtypes = deserialize(s) roots = deserialize(s)::Vector{Any} sparam_syms = deserialize(s)::SimpleVector sparam_vals = deserialize(s)::SimpleVector @@ -562,17 +572,26 @@ function deserialize(s::SerializationState, ::Type{LambdaInfo}) file = deserialize(s) line = deserialize(s) pure = deserialize(s) + nargs = deserialize(s) + isva = deserialize(s) if makenew - linfo.module = mod + linfo.code = code + linfo.slotnames = slotnames + linfo.slottypes = slottypes + linfo.slotflags = slotflags + linfo.gensymtypes = gensymtypes + linfo.roots = roots linfo.sparam_syms = sparam_syms linfo.sparam_vals = sparam_vals - ccall(:jl_lambda_info_set_ast, Void, (Any, Any), linfo, ast) linfo.inferred = infr - linfo.roots = roots + linfo.module = mod linfo.name = name linfo.file = file linfo.line = line linfo.pure = pure + linfo.nargs = nargs + linfo.isva = isva + ccall(:jl_lambda_info_init_properties, Void, (Any,), linfo) known_object_data[lnumber] = linfo end return linfo diff --git a/base/show.jl b/base/show.jl index f37afb0c6dca7..236caeeb875ce 100644 --- a/base/show.jl +++ b/base/show.jl @@ -209,9 +209,10 @@ function show(io::IO, m::Module) end function show(io::IO, l::LambdaInfo) - print(io, "AST(") - show(io, uncompressed_ast(l)) - print(io, ")") + println(io, "LambdaInfo for ", l.name) + body = Expr(:body); body.args = uncompressed_ast(l) + body.typ = l.rettype + show(io, body) end function show_delim_array(io::IO, itr::Union{AbstractArray,SimpleVector}, op, delim, cl, delim_one, diff --git a/base/stacktraces.jl b/base/stacktraces.jl index a3a7a503a37ac..47db91d98a54e 100644 --- a/base/stacktraces.jl +++ b/base/stacktraces.jl @@ -165,8 +165,8 @@ function show_spec_linfo(io::IO, frame::StackFrame) print(io, frame.func !== empty_sym ? frame.func : "?") else linfo = get(frame.outer_linfo) - if isdefined(linfo, 8) - params = linfo.(#=specTypes=#8).parameters + if isdefined(linfo, :specTypes) + params = linfo.specTypes.parameters ft = params[1] if ft <: Function && isempty(ft.parameters) && isdefined(ft.name.module, ft.name.mt.name) && diff --git a/src/alloc.c b/src/alloc.c index 12be244b90ada..71a49cea8d820 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -96,7 +96,7 @@ jl_sym_t *dot_sym; jl_sym_t *newvar_sym; jl_sym_t *boundscheck_sym; jl_sym_t *inbounds_sym; jl_sym_t *copyast_sym; jl_sym_t *fastmath_sym; jl_sym_t *pure_sym; jl_sym_t *simdloop_sym; -jl_sym_t *meta_sym; +jl_sym_t *meta_sym; jl_sym_t *compiler_temp_sym; jl_sym_t *inert_sym; jl_sym_t *vararg_sym; jl_sym_t *unused_sym; jl_sym_t *static_parameter_sym; @@ -271,12 +271,24 @@ JL_DLLEXPORT jl_value_t *jl_new_struct_uninit(jl_datatype_t *type) return jv; } +JL_DLLEXPORT void jl_lambda_info_init_properties(jl_lambda_info_t *li) +{ + int i; + uint8_t called=0; + for(i=1; i < li->nargs && i <= 8; i++) { + jl_value_t *ai = jl_cellref(li->slotnames,i); + if (ai == (jl_value_t*)unused_sym) continue; + if (jl_array_uint8_ref(li->slotflags,i)&64) + called |= (1<<(i-1)); + } + li->called = called; +} + JL_DLLEXPORT void jl_lambda_info_set_ast(jl_lambda_info_t *li, jl_value_t *ast) { - li->ast = ast; jl_gc_wb(li, ast); - if (!jl_is_expr(ast)) - return; + assert(jl_is_expr(ast)); jl_array_t *body = jl_lam_body((jl_expr_t*)ast)->args; + li->code = body; jl_gc_wb(li, li->code); if (has_meta(body, pure_sym)) li->pure = 1; jl_value_t *body1 = skip_meta(body); @@ -288,25 +300,38 @@ JL_DLLEXPORT void jl_lambda_info_set_ast(jl_lambda_info_t *li, jl_value_t *ast) li->file = (jl_sym_t*)jl_exprarg(body1, 1); li->line = jl_unbox_long(jl_exprarg(body1, 0)); } - jl_array_t *vis = jl_lam_vinfo((jl_expr_t*)li->ast); - jl_array_t *args = jl_lam_args((jl_expr_t*)li->ast); - li->nslots = jl_array_len(vis); - jl_value_t *gensym_types = jl_lam_gensyms((jl_expr_t*)li->ast); - li->ngensym = (jl_is_array(gensym_types) ? jl_array_len(gensym_types) : jl_unbox_long(gensym_types)); + jl_array_t *vis = jl_lam_vinfo((jl_expr_t*)ast); + jl_array_t *args = jl_lam_args((jl_expr_t*)ast); + size_t nslots = jl_array_len(vis); size_t narg = jl_array_len(args); - uint8_t called=0; - int i, j=0; - for(i=1; i < narg && i <= 8; i++) { - jl_value_t *ai = jl_cellref(args,i); - if (ai == (jl_value_t*)unused_sym || !jl_is_symbol(ai)) continue; - jl_value_t *vj; - do { - vj = jl_cellref(vis, j++); - } while (jl_cellref(vj,0) != ai); - if (jl_unbox_long(jl_cellref(vj,2))&64) - called |= (1<<(i-1)); + li->nargs = narg; + li->isva = narg>0 && jl_is_rest_arg(jl_cellref(args, narg-1)); + jl_value_t *gensym_types = jl_lam_gensyms((jl_expr_t*)ast); + size_t ngensym = (jl_is_array(gensym_types) ? jl_array_len(gensym_types) : jl_unbox_long(gensym_types)); + li->slotnames = jl_alloc_cell_1d(nslots); + li->slottypes = jl_nothing; + li->slotflags = jl_alloc_array_1d(jl_array_uint8_type, nslots); + li->gensymtypes = jl_box_long(ngensym); + int i; + for(i=0; i < nslots; i++) { + jl_value_t *vi = jl_cellref(vis, i); + jl_sym_t *name = (jl_sym_t*)jl_cellref(vi, 0); + assert(jl_is_symbol(name)); + char *str = jl_symbol_name(name); + if (i > 0 && name != unused_sym) { + if (str[0] == '#') { + // convention for renamed variables: #...#original_name + char *nxt = strchr(str + 1, '#'); + if (nxt) + name = jl_symbol(nxt+1); + else if (str[1] == 's') // compiler-generated temporaries, #sXXX + name = compiler_temp_sym; + } + } + jl_cellset(li->slotnames, i, name); + jl_array_uint8_set(li->slotflags, i, jl_unbox_long(jl_cellref(vi, 2))); } - li->called = called; + jl_lambda_info_init_properties(li); } JL_DLLEXPORT @@ -316,7 +341,9 @@ jl_lambda_info_t *jl_new_lambda_info(jl_value_t *ast, jl_svec_t *tvars, jl_svec_ jl_lambda_info_t *li = (jl_lambda_info_t*)newobj((jl_value_t*)jl_lambda_info_type, NWORDS(sizeof(jl_lambda_info_t))); - li->ast = ast; + li->code = NULL; + li->slotnames = li->slotflags = NULL; + li->slottypes = li->gensymtypes = NULL; li->rettype = (jl_value_t*)jl_any_type; li->file = null_sym; li->module = ctx; @@ -343,15 +370,27 @@ jl_lambda_info_t *jl_new_lambda_info(jl_value_t *ast, jl_svec_t *tvars, jl_svec_ li->pure = 0; li->called = 0xff; li->needs_sparam_vals_ducttape = 2; - if (ast != NULL) + if (ast != NULL) { + JL_GC_PUSH1(&li); jl_lambda_info_set_ast(li, ast); + JL_GC_POP(); + } return li; } -jl_lambda_info_t *jl_copy_lambda_info(jl_lambda_info_t *linfo) +JL_DLLEXPORT jl_lambda_info_t *jl_copy_lambda_info(jl_lambda_info_t *linfo) { jl_lambda_info_t *new_linfo = - jl_new_lambda_info(linfo->ast, linfo->sparam_syms, linfo->sparam_vals, linfo->module); + jl_new_lambda_info(NULL, linfo->sparam_syms, linfo->sparam_vals, linfo->module); + new_linfo->code = linfo->code; + new_linfo->slotnames = linfo->slotnames; + new_linfo->slottypes = linfo->slottypes; + new_linfo->slotflags = linfo->slotflags; + new_linfo->gensymtypes = linfo->gensymtypes; + new_linfo->called = linfo->called; + new_linfo->nargs = linfo->nargs; + new_linfo->isva = linfo->isva; + new_linfo->pure = linfo->pure; new_linfo->rettype = linfo->rettype; new_linfo->tfunc = linfo->tfunc; new_linfo->name = linfo->name; @@ -679,6 +718,8 @@ JL_DLLEXPORT jl_datatype_t *jl_new_datatype(jl_sym_t *name, jl_datatype_t *super t = jl_int64_type; else if (!strcmp(jl_symbol_name((jl_sym_t*)name), "Bool")) t = jl_bool_type; + else if (!strcmp(jl_symbol_name((jl_sym_t*)name), "UInt8")) + t = jl_uint8_type; } if (t == NULL) t = jl_new_uninitialized_datatype(jl_svec_len(fnames), 2); // TODO @@ -880,6 +921,9 @@ void jl_init_int32_int64_cache(void) boxed_gensym_cache[i] = jl_box32(jl_gensym_type, i); #endif } + for(i=0; i < 256; i++) { + boxed_uint8_cache[i] = jl_box8(jl_uint8_type, i); + } } void jl_init_box_caches(void) @@ -887,7 +931,6 @@ void jl_init_box_caches(void) int64_t i; for(i=0; i < 256; i++) { boxed_int8_cache[i] = jl_box8(jl_int8_type, i); - boxed_uint8_cache[i] = jl_box8(jl_uint8_type, i); } for(i=0; i < NBOX_C; i++) { boxed_int16_cache[i] = jl_box16(jl_int16_type, i-NBOX_C/2); diff --git a/src/ast.c b/src/ast.c index ae8e1d47a83e8..dbafa41d894a8 100644 --- a/src/ast.c +++ b/src/ast.c @@ -486,7 +486,7 @@ static jl_value_t *scm_to_julia_(fl_context_t *fl_ctx, value_t e, int eo) e = cdr_(e); } nli = jl_new_lambda_info((jl_value_t*)ex, tvars, jl_emptysvec, jl_current_module); - jl_preresolve_globals(nli->ast, nli); + jl_preresolve_globals((jl_value_t*)nli, nli); JL_GC_POP(); return (jl_value_t*)nli; } @@ -918,18 +918,6 @@ jl_array_t *jl_lam_args(jl_expr_t *l) return (jl_array_t*)ae; } -jl_sym_t *jl_lam_argname(jl_lambda_info_t *li, int i) -{ - jl_expr_t *ast; - if (jl_is_expr(li->ast)) - ast = (jl_expr_t*)li->ast; - else - ast = (jl_expr_t*)jl_uncompress_ast(li, li->ast); - // NOTE (gc root): `ast` is not rooted here, but jl_lam_args and jl_cellref - // do not allocate. - return (jl_sym_t*)jl_cellref(jl_lam_args(ast),i); -} - // get array of var info records (for args and locals) jl_array_t *jl_lam_vinfo(jl_expr_t *l) { @@ -961,13 +949,6 @@ jl_expr_t *jl_lam_body(jl_expr_t *l) return (jl_expr_t*)be; } -jl_sym_t *jl_decl_var(jl_value_t *ex) -{ - if (jl_is_symbol(ex)) return (jl_sym_t*)ex; - assert(jl_is_expr(ex)); - return (jl_sym_t*)jl_exprarg(ex, 0); -} - JL_DLLEXPORT int jl_is_rest_arg(jl_value_t *ex) { if (!jl_is_expr(ex)) return 0; @@ -1018,25 +999,6 @@ JL_DLLEXPORT jl_value_t *jl_copy_ast(jl_value_t *expr) return expr; } -// given a new lambda_info with static parameter values, make a copy -// of the tree with declared types evaluated and static parameters passed -// on to all enclosed functions. -// this tree can then be further mutated by optimization passes. -JL_DLLEXPORT jl_value_t *jl_prepare_ast(jl_lambda_info_t *li) -{ - jl_value_t *ast = li->ast; - if (ast == NULL) return NULL; - JL_GC_PUSH1(&ast); - if (!jl_is_expr(ast)) { - ast = jl_uncompress_ast(li, ast); - } - else { - ast = jl_copy_ast(ast); - } - JL_GC_POP(); - return ast; -} - JL_DLLEXPORT int jl_is_operator(char *sym) { jl_ast_context_t *ctx = jl_ast_ctx_enter(); @@ -1088,6 +1050,15 @@ jl_value_t *jl_preresolve_globals(jl_value_t *expr, jl_lambda_info_t *lam) return expr; return jl_module_globalref(lam->module, (jl_sym_t*)expr); } + else if (jl_is_lambda_info(expr)) { + jl_array_t *exprs = ((jl_lambda_info_t*)expr)->code; + if (jl_typeis(exprs, jl_array_any_type)) { + size_t l = jl_array_len(exprs); + size_t i; + for(i=0; i < l; i++) + jl_cellset(exprs, i, jl_preresolve_globals(jl_cellref(exprs,i), lam)); + } + } else if (jl_is_expr(expr)) { jl_expr_t *e = (jl_expr_t*)expr; if (e->head == lambda_sym) { diff --git a/src/builtins.c b/src/builtins.c index 5eca771687f09..e12cd4a1fbe1e 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -113,12 +113,6 @@ JL_DLLEXPORT void JL_NORETURN jl_type_error(const char *fname, jl_value_t *expec JL_DLLEXPORT void JL_NORETURN jl_undefined_var_error(jl_sym_t *var) { - if (jl_symbol_name(var)[0] == '#') { - // convention for renamed variables: #...#original_name - char *nxt = strchr(jl_symbol_name(var) + 1, '#'); - if (nxt) - var = jl_symbol(nxt+1); - } jl_throw(jl_new_struct(jl_undefvarerror_type, var)); } @@ -1084,11 +1078,11 @@ jl_value_t *jl_mk_builtin_func(const char *name, jl_fptr_t fptr) { jl_sym_t *sname = jl_symbol(name); jl_value_t *f = jl_new_generic_function_with_supertype(sname, jl_core_module, jl_builtin_type, 0); - jl_lambda_info_t *li = jl_new_lambda_info(jl_nothing, jl_emptysvec, jl_emptysvec, jl_core_module); + jl_lambda_info_t *li = jl_new_lambda_info(NULL, jl_emptysvec, jl_emptysvec, jl_core_module); li->fptr = fptr; li->name = sname; // TODO jb/functions: what should li->ast be? - li->ast = (jl_value_t*)jl_exprn(lambda_sym,0); jl_gc_wb(li, li->ast); + li->code = (jl_array_t*)jl_an_empty_cell; jl_gc_wb(li, li->code); jl_method_cache_insert(jl_gf_mtable(f), jl_anytuple_type, li); return f; } diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 6e4affa00f061..071adbb3243fc 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -849,9 +849,12 @@ static inline jl_module_t *topmod(jl_codectx_t *ctx) static jl_value_t *expr_type(jl_value_t *e, jl_codectx_t *ctx) { if (jl_is_gensym(e)) { + if (jl_is_long(ctx->linfo->gensymtypes)) + return (jl_value_t*)jl_any_type; int idx = ((jl_gensym_t*)e)->id; - jl_value_t *gensym_types = jl_lam_gensyms(ctx->ast); - return (jl_is_array(gensym_types) ? jl_cellref(gensym_types, idx) : (jl_value_t*)jl_any_type); + assert(jl_is_array(ctx->linfo->gensymtypes)); + 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); diff --git a/src/codegen.cpp b/src/codegen.cpp index 416b3a9624d95..e9433b0867986 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -556,7 +556,6 @@ typedef struct { std::map *labels; std::map *handlers; jl_module_t *module; - jl_expr_t *ast; jl_lambda_info_t *linfo; Value *spvals_ptr; Value *argArray; @@ -742,7 +741,7 @@ static bool store_unboxed_p(int s, jl_codectx_t *ctx) static jl_sym_t *slot_symbol(int s, jl_codectx_t *ctx) { - return (jl_sym_t*)jl_cellref(jl_cellref(jl_lam_vinfo(ctx->ast), s), 0); + return (jl_sym_t*)jl_cellref(ctx->linfo->slotnames, s); } static Value *alloc_local(int s, jl_codectx_t *ctx) @@ -759,7 +758,8 @@ static Value *alloc_local(int s, jl_codectx_t *ctx) // CreateAlloca is OK here because alloc_local is only called during prologue setup Value *lv = builder.CreateAlloca(vtype, 0, jl_symbol_name(slot_symbol(s,ctx))); vi.value = mark_julia_slot(lv, jt); - vi.value.isimmutable &= vi.isSA; // slot is not immutable if there are multiple assignments + // slot is not immutable if there are multiple assignments + vi.value.isimmutable &= (vi.isSA && s >= ctx->linfo->nargs); assert(vi.value.isboxed == false); return lv; } @@ -3018,7 +3018,7 @@ static void emit_assignment(jl_value_t *l, jl_value_t *r, jl_codectx_t *ctx) } if (slot.isboxed && slot.isimmutable) { // see if inference had a better type for the gensym than the expression (after inlining getfield on a Tuple) - jl_value_t *gensym_types = jl_lam_gensyms(ctx->ast); + jl_value_t *gensym_types = (jl_value_t*)ctx->linfo->gensymtypes; if (jl_is_array(gensym_types)) { jl_value_t *declType = jl_cellref(gensym_types, idx); if (declType != slot.typ) { @@ -3860,7 +3860,7 @@ static Function *gen_cfun_wrapper(jl_lambda_info_t *lam, jl_function_t *ff, jl_v } // generate a julia-callable function that calls f (AKA lam) -static Function *gen_jlcall_wrapper(jl_lambda_info_t *lam, jl_expr_t *ast, Function *f, bool sret, Module *M) +static Function *gen_jlcall_wrapper(jl_lambda_info_t *lam, Function *f, bool sret, Module *M) { std::stringstream funcName; const std::string &fname = f->getName().str(); @@ -3893,7 +3893,7 @@ static Function *gen_jlcall_wrapper(jl_lambda_info_t *lam, jl_expr_t *ast, Funct ctx.spvals_ptr = NULL; allocate_gc_frame(b0, &ctx); - size_t nargs = jl_array_dim0(jl_lam_args(ast)); + size_t nargs = lam->nargs; size_t nfargs = f->getFunctionType()->getNumParams(); Value **args = (Value**) alloca(nfargs*sizeof(Value*)); unsigned idx = 0; @@ -3945,12 +3945,10 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func assert(declarations && "Capturing declarations is always required"); // step 1. unpack AST and allocate codegen context for this function - jl_expr_t *ast = (jl_expr_t*)lam->ast; - JL_GC_PUSH1(&ast); - if (!jl_is_expr(ast)) { - ast = (jl_expr_t*)jl_uncompress_ast(lam, (jl_value_t*)ast); - } - assert(jl_is_expr(ast)); + jl_array_t *code = lam->code; + JL_GC_PUSH1(&code); + if (!jl_typeis(code,jl_array_any_type)) + code = jl_uncompress_ast(lam, code); //jl_static_show(JL_STDOUT, (jl_value_t*)ast); //jl_printf(JL_STDOUT, "\n"); std::map arrayvars; @@ -3961,7 +3959,6 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func ctx.labels = &labels; ctx.handlers = &handlers; ctx.module = lam->module; - ctx.ast = ast; ctx.linfo = lam; ctx.funcName = jl_symbol_name(lam->name); ctx.vaSlot = -1; @@ -3971,12 +3968,9 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func ctx.spvals_ptr = NULL; // step 2. process var-info lists to see what vars need boxing - jl_value_t *gensym_types = jl_lam_gensyms(ast); - int n_gensyms = (jl_is_array(gensym_types) ? jl_array_len(gensym_types) : jl_unbox_gensym(gensym_types)); - jl_array_t *largs = jl_lam_args(ast); - size_t largslen = jl_array_dim0(largs); - jl_array_t *vinfos = jl_lam_vinfo(ast); - size_t vinfoslen = jl_array_dim0(vinfos); + int n_gensyms = jl_is_long(lam->gensymtypes) ? jl_unbox_long(lam->gensymtypes) : jl_array_len(lam->gensymtypes); + size_t largslen = lam->nargs; + size_t vinfoslen = jl_array_dim0(lam->slotnames); ctx.slots.resize(vinfoslen); size_t nreq = largslen; int va = 0; @@ -3986,10 +3980,10 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func // compiling this would cause all specializations to inherit // this code and could create an broken compile / function cache - if (nreq > 0 && jl_is_rest_arg(jl_cellref(largs,largslen-1))) { + if (nreq > 0 && lam->isva) { nreq--; va = 1; - jl_sym_t *vn = jl_decl_var(jl_cellref(largs,largslen-1)); + jl_sym_t *vn = (jl_sym_t*)jl_cellref(lam->slotnames,largslen-1); if (vn != unused_sym) ctx.vaSlot = largslen-1; } @@ -4002,8 +3996,7 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func // step 3. some variable analysis size_t i; for(i=0; i < nreq; i++) { - jl_value_t *arg = jl_cellref(largs,i); - jl_sym_t *argname = jl_decl_var(arg); + jl_sym_t *argname = (jl_sym_t*)jl_cellref(lam->slotnames,i); if (argname == unused_sym) continue; jl_varinfo_t &varinfo = ctx.slots[i]; varinfo.isArgument = true; @@ -4017,28 +4010,28 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func } for(i=0; i < vinfoslen; i++) { - jl_array_t *vi = (jl_array_t*)jl_cellref(vinfos, i); - assert(jl_is_array(vi)); - jl_sym_t *vname = ((jl_sym_t*)jl_cellref(vi,0)); - assert(jl_is_symbol(vname)); jl_varinfo_t &varinfo = ctx.slots[i]; - varinfo.isAssigned = (jl_vinfo_assigned(vi)!=0); + uint8_t flags = jl_array_uint8_ref(lam->slotflags, i); + varinfo.isAssigned = (jl_vinfo_assigned(flags)!=0); varinfo.escapes = false; - varinfo.isSA = (jl_vinfo_sa(vi)!=0); - varinfo.usedUndef = (jl_vinfo_usedundef(vi)!=0) || (!varinfo.isArgument && !lam->inferred); + varinfo.isSA = (jl_vinfo_sa(flags)!=0); + varinfo.usedUndef = (jl_vinfo_usedundef(flags)!=0) || (!varinfo.isArgument && !lam->inferred); if (!varinfo.isArgument || varinfo.isAssigned) { - jl_value_t *typ = jl_cellref(vi,1); + jl_value_t *typ = jl_is_array(lam->slottypes) ? jl_cellref(lam->slottypes,i) : (jl_value_t*)jl_any_type; if (!jl_is_type(typ)) typ = (jl_value_t*)jl_any_type; varinfo.value = mark_julia_type((Value*)NULL, false, typ, &ctx); } } + jl_array_t *stmts = code; + size_t stmtslen = jl_array_dim0(stmts); + // finish recording escape info - simple_escape_analysis((jl_value_t*)ast, true, &ctx); + for(i=0; i < stmtslen; i++) + simple_escape_analysis(jl_cellref(stmts,i), true, &ctx); // determine which vars need to be volatile - jl_array_t *stmts = jl_lam_body(ast)->args; mark_volatile_vars(stmts, ctx.slots); // step 4. determine function signature @@ -4106,7 +4099,7 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func #ifdef LLVM37 f->addFnAttr("no-frame-pointer-elim", "true"); #endif - fwrap = gen_jlcall_wrapper(lam, ast, f, ctx.sret, M); + fwrap = gen_jlcall_wrapper(lam, f, ctx.sret, M); declarations->functionObject = function_proto(fwrap); declarations->specFunctionObject = function_proto(f); } @@ -4283,7 +4276,7 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func const bool AlwaysPreserve = true; // Go over all arguments and local variables and initialize their debug information for(i=0; i < nreq; i++) { - jl_sym_t *argname = jl_decl_var(jl_cellref(largs,i)); + jl_sym_t *argname = (jl_sym_t*)jl_cellref(lam->slotnames,i); if (argname == unused_sym) continue; jl_varinfo_t &varinfo = ctx.slots[i]; #ifdef LLVM38 @@ -4335,7 +4328,7 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func #endif } for(i=0; i < vinfoslen; i++) { - jl_sym_t *s = (jl_sym_t*)jl_cellref(jl_cellref(vinfos,i),0); + jl_sym_t *s = (jl_sym_t*)jl_cellref(lam->slotnames,i); jl_varinfo_t &varinfo = ctx.slots[i]; if (varinfo.isArgument) continue; @@ -4397,7 +4390,6 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func allocate_gc_frame(b0, &ctx); // step 8. allocate space for exception handler contexts - size_t stmtslen = jl_array_dim0(stmts); for(i=0; i < stmtslen; i++) { jl_value_t *stmt = jl_cellref(stmts,i); if (jl_is_expr(stmt) && ((jl_expr_t*)stmt)->head == enter_sym) { @@ -4416,7 +4408,7 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func // get pointers for locals stored in the gc frame array (argTemp) for(i=0; i < vinfoslen; i++) { - jl_sym_t *s = (jl_sym_t*)jl_cellref(jl_cellref(vinfos,i),0); + jl_sym_t *s = slot_symbol(i, &ctx); if (s == unused_sym) continue; jl_varinfo_t &varinfo = ctx.slots[i]; assert(!varinfo.memloc); // variables shouldn't also have memory locs already @@ -4450,7 +4442,7 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func if (varinfo.isAssigned || // always need a slot if the variable is assigned specsig || // for arguments, give them stack slots if then aren't in `argArray` (otherwise, will use that pointer) (va && (int)i == ctx.vaSlot && varinfo.escapes) || // or it's the va arg tuple - (s != unused_sym && s == jl_decl_var(jl_cellref(largs, 0)))) { // or it is the first argument (which isn't in `argArray`) + (s != unused_sym && i == 0)) { // or it is the first argument (which isn't in `argArray`) AllocaInst *av = new AllocaInst(T_pjlvalue, jl_symbol_name(s), /*InsertBefore*/ctx.ptlsStates); varinfo.memloc = av; #ifdef LLVM36 @@ -4480,7 +4472,7 @@ static std::unique_ptr emit_function(jl_lambda_info_t *lam, jl_llvm_func if (ctx.sret) AI++; // skip sret slot for(i=0; i < nreq; i++) { - jl_sym_t *s = jl_decl_var(jl_cellref(largs,i)); + jl_sym_t *s = (jl_sym_t*)jl_cellref(lam->slotnames,i); jl_value_t *argType = jl_nth_slot_type(lam->specTypes, i); bool isboxed; Type *llvmArgType = julia_type_to_llvm(argType, &isboxed); diff --git a/src/dump.c b/src/dump.c index 54447cd374fcb..8a5d4ae0fe46f 100644 --- a/src/dump.c +++ b/src/dump.c @@ -515,6 +515,8 @@ static void jl_serialize_datatype(ios_t *s, jl_datatype_t *dt) tag = 3; else if (dt == jl_int64_type) tag = 4; + else if (dt == jl_uint8_type) + tag = 8; writetag(s, (jl_value_t*)SmallDataType_tag); write_uint8(s, 0); // virtual size jl_serialize_value(s, (jl_value_t*)jl_datatype_type); @@ -608,14 +610,6 @@ static void jl_serialize_module(ios_t *s, jl_module_t *m) static int is_ast_node(jl_value_t *v) { - if (jl_is_lambda_info(v)) { - jl_lambda_info_t *li = (jl_lambda_info_t*)v; - if (jl_is_expr(li->ast)) { - li->ast = jl_compress_ast(li, li->ast); - jl_gc_wb(li, li->ast); - } - return 0; - } return jl_is_symbol(v) || jl_is_slot(v) || jl_is_gensym(v) || jl_is_expr(v) || jl_is_newvarnode(v) || jl_is_svec(v) || jl_typeis(v, jl_array_any_type) || jl_is_tuple(v) || @@ -791,7 +785,11 @@ static void jl_serialize_value_(ios_t *s, jl_value_t *v) else if (jl_is_lambda_info(v)) { writetag(s, jl_lambda_info_type); jl_lambda_info_t *li = (jl_lambda_info_t*)v; - jl_serialize_value(s, li->ast); + jl_serialize_value(s, li->code); + jl_serialize_value(s, li->slotnames); + jl_serialize_value(s, li->slottypes); + jl_serialize_value(s, li->slotflags); + jl_serialize_value(s, li->gensymtypes); jl_serialize_value(s, li->rettype); jl_serialize_value(s, (jl_value_t*)li->sparam_syms); jl_serialize_value(s, (jl_value_t*)li->sparam_vals); @@ -810,10 +808,10 @@ static void jl_serialize_value_(ios_t *s, jl_value_t *v) for(i=0; i < l; i += 3) { if (!jl_is_leaf_type(jl_cellref(tf,i))) { jl_value_t *ret = jl_cellref(tf,i+1); - if (jl_is_tuple(ret)) { - jl_value_t *ast = jl_fieldref(ret, 0); - if (jl_is_array(ast) && jl_array_len(ast) > 500) - jl_cellset(tf, i+1, jl_fieldref(ret,1)); + if (jl_is_lambda_info(ret)) { + jl_array_t *code = ((jl_lambda_info_t*)ret)->code; + if (jl_is_array(code) && jl_array_len(code) > 500) + jl_cellset(tf, i+1, ((jl_lambda_info_t*)ret)->rettype); } } } @@ -826,10 +824,10 @@ static void jl_serialize_value_(ios_t *s, jl_value_t *v) write_int8(s, li->inferred); write_int8(s, li->pure); write_int8(s, li->called); + write_int8(s, li->isva); jl_serialize_value(s, (jl_value_t*)li->file); write_int32(s, li->line); - write_int32(s, li->nslots); - write_int32(s, li->ngensym); + write_int32(s, li->nargs); jl_serialize_value(s, (jl_value_t*)li->module); jl_serialize_value(s, (jl_value_t*)li->roots); jl_serialize_value(s, (jl_value_t*)li->def); @@ -1186,6 +1184,8 @@ static jl_value_t *jl_deserialize_datatype(ios_t *s, int pos, jl_value_t **loc) dt = jl_bool_type; else if (tag == 4) dt = jl_int64_type; + else if (tag == 8) + dt = jl_uint8_type; else dt = jl_new_uninitialized_datatype(nf, fielddesc_type); assert(tree_literal_values==NULL && mode != MODE_AST); @@ -1422,8 +1422,11 @@ static jl_value_t *jl_deserialize_value_(ios_t *s, jl_value_t *vtag, jl_value_t NWORDS(sizeof(jl_lambda_info_t))); if (usetable) arraylist_push(&backref_list, li); - li->ast = jl_deserialize_value(s, &li->ast); - jl_gc_wb(li, li->ast); + li->code = (jl_array_t*)jl_deserialize_value(s, (jl_value_t**)&li->code); jl_gc_wb(li, li->code); + li->slotnames = (jl_array_t*)jl_deserialize_value(s, (jl_value_t**)&li->slotnames); jl_gc_wb(li, li->slotnames); + li->slottypes = jl_deserialize_value(s, &li->slottypes); jl_gc_wb(li, li->slottypes); + li->slotflags = (jl_array_t*)jl_deserialize_value(s, (jl_value_t**)&li->slotflags); jl_gc_wb(li, li->slotflags); + li->gensymtypes = jl_deserialize_value(s, &li->gensymtypes); jl_gc_wb(li, li->gensymtypes); li->rettype = jl_deserialize_value(s, &li->rettype); jl_gc_wb(li, li->rettype); li->sparam_syms = (jl_svec_t*)jl_deserialize_value(s, (jl_value_t**)&li->sparam_syms); @@ -1441,11 +1444,11 @@ static jl_value_t *jl_deserialize_value_(ios_t *s, jl_value_t *vtag, jl_value_t li->inferred = read_int8(s); li->pure = read_int8(s); li->called = read_int8(s); + li->isva = read_int8(s); li->file = (jl_sym_t*)jl_deserialize_value(s, NULL); jl_gc_wb(li, li->file); li->line = read_int32(s); - li->nslots = read_int32(s); - li->ngensym = read_int32(s); + li->nargs = read_int32(s); li->module = (jl_module_t*)jl_deserialize_value(s, (jl_value_t**)&li->module); jl_gc_wb(li, li->module); li->roots = (jl_array_t*)jl_deserialize_value(s, (jl_value_t**)&li->roots); @@ -2032,7 +2035,7 @@ JL_DLLEXPORT void jl_restore_system_image_data(const char *buf, size_t len) JL_SIGATOMIC_END(); } -JL_DLLEXPORT jl_value_t *jl_compress_ast(jl_lambda_info_t *li, jl_value_t *ast) +JL_DLLEXPORT jl_array_t *jl_compress_ast(jl_lambda_info_t *li, jl_array_t *ast) { JL_SIGATOMIC_BEGIN(); JL_LOCK(dump); // Might GC @@ -2054,7 +2057,7 @@ JL_DLLEXPORT jl_value_t *jl_compress_ast(jl_lambda_info_t *li, jl_value_t *ast) //jl_printf(JL_STDERR, "%d bytes, %d values\n", dest.size, vals->length); - jl_value_t *v = (jl_value_t*)jl_takebuf_array(&dest); + jl_array_t *v = jl_takebuf_array(&dest); if (jl_array_len(tree_literal_values) == 0 && last_tlv == NULL) { li->def->roots = NULL; } @@ -2067,7 +2070,7 @@ JL_DLLEXPORT jl_value_t *jl_compress_ast(jl_lambda_info_t *li, jl_value_t *ast) return v; } -JL_DLLEXPORT jl_value_t *jl_uncompress_ast(jl_lambda_info_t *li, jl_value_t *data) +JL_DLLEXPORT jl_array_t *jl_uncompress_ast(jl_lambda_info_t *li, jl_array_t *data) { JL_SIGATOMIC_BEGIN(); JL_LOCK(dump); // Might GC @@ -2082,7 +2085,7 @@ JL_DLLEXPORT jl_value_t *jl_uncompress_ast(jl_lambda_info_t *li, jl_value_t *dat ios_setbuf(&src, (char*)bytes->data, jl_array_len(bytes), 0); src.size = jl_array_len(bytes); int en = jl_gc_enable(0); // Might GC - jl_value_t *v = jl_deserialize_value(&src, NULL); + jl_array_t *v = (jl_array_t*)jl_deserialize_value(&src, NULL); jl_gc_enable(en); tree_literal_values = NULL; tree_enclosing_module = NULL; @@ -2400,7 +2403,7 @@ void jl_init_serializer(void) jl_box_int32(39), jl_box_int32(40), jl_box_int32(41), jl_box_int32(42), jl_box_int32(43), jl_box_int32(44), jl_box_int32(45), jl_box_int32(46), jl_box_int32(47), - jl_box_int32(48), jl_box_int32(49), jl_box_int32(50), + jl_box_int32(48), jl_box_int32(49), #endif jl_box_int64(0), jl_box_int64(1), jl_box_int64(2), jl_box_int64(3), jl_box_int64(4), jl_box_int64(5), @@ -2419,7 +2422,7 @@ void jl_init_serializer(void) jl_box_int64(39), jl_box_int64(40), jl_box_int64(41), jl_box_int64(42), jl_box_int64(43), jl_box_int64(44), jl_box_int64(45), jl_box_int64(46), jl_box_int64(47), - jl_box_int64(48), jl_box_int64(49), jl_box_int64(50), + jl_box_int64(48), jl_box_int64(49), #endif jl_labelnode_type, jl_linenumbernode_type, jl_gotonode_type, jl_quotenode_type, jl_topnode_type, @@ -2431,7 +2434,7 @@ void jl_init_serializer(void) jl_ANY_flag, jl_array_any_type, jl_intrinsic_type, jl_method_type, jl_methtable_type, jl_voidpointer_type, jl_newvarnode_type, jl_array_symbol_type, jl_anytuple_type, jl_tparam0(jl_anytuple_type), - jl_typeof(jl_emptytuple), + jl_typeof(jl_emptytuple), jl_array_uint8_type, jl_symbol_type->name, jl_gensym_type->name, jl_tuple_typename, jl_ref_type->name, jl_pointer_type->name, jl_simplevector_type->name, jl_datatype_type->name, jl_uniontype_type->name, jl_array_type->name, diff --git a/src/gf.c b/src/gf.c index 880c807f26d63..ad29e0ed625ad 100644 --- a/src/gf.c +++ b/src/gf.c @@ -293,7 +293,7 @@ static jl_lambda_info_t *jl_add_static_parameters(jl_lambda_info_t *l, jl_svec_t for example, if an unspecialized method is needed, the slow compiled code should be associated with method->unspecialized, not method */ - assert(!nli->ast || + assert(!nli->code || (nli->fptr == NULL && nli->jlcall_api == 0 && nli->functionObjects.functionObject == NULL && @@ -330,11 +330,18 @@ jl_lambda_info_t *jl_get_unspecialized(jl_lambda_info_t *method) return method->unspecialized; if (method->specTypes && def->sparam_syms != jl_emptysvec) { if (def->needs_sparam_vals_ducttape == 2) { - jl_value_t *code = method->ast; + jl_array_t *code = method->code; JL_GC_PUSH1(&code); - if (!jl_is_expr(code)) + if (!jl_typeis(code, jl_array_any_type)) code = jl_uncompress_ast(def, code); - def->needs_sparam_vals_ducttape = jl_has_intrinsics(method, (jl_expr_t*)code, method->module); + size_t i, l = jl_array_len(code); + def->needs_sparam_vals_ducttape = 0; + for(i=0; i < l; i++) { + if (jl_has_intrinsics(method, jl_cellref(code,i), method->module)) { + def->needs_sparam_vals_ducttape = 1; + break; + } + } JL_GC_POP(); } if (method->needs_sparam_vals_ducttape) { @@ -815,7 +822,7 @@ static jl_lambda_info_t *cache_method(jl_methtable_t *mt, jl_tupletype_t *type, else (void)jl_method_cache_insert(mt, type, newmeth); - if (newmeth->ast != NULL) { + if (newmeth->code != NULL) { jl_array_t *spe = method->specializations; if (spe == NULL) { spe = jl_alloc_cell_1d(1); @@ -895,11 +902,12 @@ JL_DLLEXPORT jl_lambda_info_t *jl_instantiate_staged(jl_lambda_info_t *generator ex = jl_exprn(lambda_sym, 2); - jl_expr_t *generatorast = (jl_expr_t*)generator->ast; - if (!jl_is_expr(generatorast)) - generatorast = (jl_expr_t*)jl_uncompress_ast(generator, (jl_value_t*)generatorast); - jl_array_t *argnames = jl_lam_args(generatorast); + int nargs = generator->nargs; + jl_array_t *argnames = jl_alloc_cell_1d(nargs); jl_cellset(ex->args, 0, argnames); + size_t i; + for(i=0; i < nargs; i++) + jl_cellset(argnames, i, jl_cellref(generator->slotnames,i)); jl_expr_t *scopeblock = jl_exprn(jl_symbol("scope-block"), 1); jl_cellset(ex->args, 1, scopeblock); @@ -913,8 +921,7 @@ JL_DLLEXPORT jl_lambda_info_t *jl_instantiate_staged(jl_lambda_info_t *generator // invoke code generator assert(jl_nparams(tt) == jl_array_len(argnames) || - (jl_is_rest_arg(jl_cellref(argnames, jl_array_len(argnames)-1)) && - (jl_nparams(tt) >= jl_array_len(argnames) - 1))); + (generator->isva && (jl_nparams(tt) >= jl_array_len(argnames) - 1))); jl_cellset(body->args, 1, jl_call_unspecialized(sparam_vals, generator, jl_svec_data(tt->parameters), jl_nparams(tt))); if (generator->sparam_syms != jl_emptysvec) { @@ -930,6 +937,8 @@ JL_DLLEXPORT jl_lambda_info_t *jl_instantiate_staged(jl_lambda_info_t *generator // need to eval macros in the right module, but not give a warning for the `eval` call unless that results in a call to `eval` jl_lambda_info_t *func = (jl_lambda_info_t*)jl_toplevel_eval_in_warn(generator->module, (jl_value_t*)ex, 1); func->name = generator->name; + if (generator->isva) + func->isva = 1; JL_GC_POP(); return func; } @@ -1446,7 +1455,7 @@ jl_lambda_info_t *jl_get_specialization1(jl_tupletype_t *types) } JL_CATCH { goto not_found; } - if (sf == NULL || sf->ast == NULL || sf->inInference) + if (sf == NULL || sf->code == NULL || sf->inInference) goto not_found; if (sf->functionObjects.functionObject == NULL) { if (sf->fptr != NULL) diff --git a/src/init.c b/src/init.c index 90166c4e4ed7a..aa92a14cb4fa5 100644 --- a/src/init.c +++ b/src/init.c @@ -808,7 +808,6 @@ void jl_get_builtin_hooks(void) jl_char_type = (jl_datatype_t*)core("Char"); jl_int8_type = (jl_datatype_t*)core("Int8"); - jl_uint8_type = (jl_datatype_t*)core("UInt8"); jl_int16_type = (jl_datatype_t*)core("Int16"); jl_uint16_type = (jl_datatype_t*)core("UInt16"); jl_uint32_type = (jl_datatype_t*)core("UInt32"); @@ -841,9 +840,6 @@ void jl_get_builtin_hooks(void) jl_ascii_string_type = (jl_datatype_t*)core("ASCIIString"); jl_utf8_string_type = (jl_datatype_t*)core("UTF8String"); jl_weakref_type = (jl_datatype_t*)core("WeakRef"); - - jl_array_uint8_type = jl_apply_type((jl_value_t*)jl_array_type, - jl_svec2(jl_uint8_type, jl_box_long(1))); } JL_DLLEXPORT void jl_get_system_hooks(void) diff --git a/src/interpreter.c b/src/interpreter.c index 246529dad632c..1e7ccd285fcd0 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -112,7 +112,17 @@ void jl_set_datatype_super(jl_datatype_t *tt, jl_value_t *super) jl_gc_wb(tt, tt->super); } - static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, jl_lambda_info_t *lam) +static int jl_linfo_nslots(jl_lambda_info_t *li) +{ + return jl_array_len(li->slotflags); +} + +static int jl_linfo_ngensyms(jl_lambda_info_t *li) +{ + return jl_is_long(li->gensymtypes) ? jl_unbox_long(li->gensymtypes) : jl_array_len(li->gensymtypes); +} + +static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, jl_lambda_info_t *lam) { if (jl_is_symbol(e)) { jl_value_t *v = jl_get_global(jl_current_module, (jl_sym_t*)e); @@ -122,10 +132,10 @@ void jl_set_datatype_super(jl_datatype_t *tt, jl_value_t *super) } if (jl_is_gensym(e)) { ssize_t genid = ((jl_gensym_t*)e)->id; - if (genid >= lam->ngensym || genid < 0) + if (genid >= jl_linfo_ngensyms(lam) || genid < 0) jl_error("access to invalid GenSym location"); else - return locals[lam->nslots + genid]; + return locals[jl_linfo_nslots(lam) + genid]; } if (jl_is_quotenode(e)) { return jl_fieldref(e,0); @@ -140,11 +150,11 @@ void jl_set_datatype_super(jl_datatype_t *tt, jl_value_t *super) if (!jl_is_expr(e)) { if (jl_typeis(e, jl_slot_type)) { ssize_t n = jl_slot_number(e); - if (n > lam->nslots || n < 1) + if (n > jl_linfo_nslots(lam) || n < 1) jl_error("access to invalid slot number"); jl_value_t *v = locals[n-1]; - if (v == NULL) // TODO get var name from `lam` - jl_undefined_var_error(anonymous_sym); + if (v == NULL) + jl_undefined_var_error((jl_sym_t*)jl_cellref(lam->slotnames,n-1)); return v; } if (jl_is_globalref(e)) { @@ -158,7 +168,7 @@ void jl_set_datatype_super(jl_datatype_t *tt, jl_value_t *super) jl_value_t *var = jl_fieldref(e,0); assert(jl_typeis(var,jl_slot_type)); ssize_t n = jl_slot_number(var); - assert(n <= lam->nslots && n > 0); + assert(n <= jl_linfo_nslots(lam) && n > 0); locals[n-1] = NULL; return (jl_value_t*)jl_nothing; } @@ -175,13 +185,13 @@ void jl_set_datatype_super(jl_datatype_t *tt, jl_value_t *super) jl_value_t *rhs = eval(args[1], locals, lam); if (jl_is_gensym(sym)) { ssize_t genid = ((jl_gensym_t*)sym)->id; - if (genid >= lam->ngensym || genid < 0) + if (genid >= jl_linfo_ngensyms(lam) || genid < 0) jl_error("assignment to invalid GenSym location"); - locals[lam->nslots + genid] = rhs; + locals[jl_linfo_nslots(lam) + genid] = rhs; } else if (jl_typeis(sym,jl_slot_type)) { ssize_t n = jl_slot_number(sym); - assert(n <= lam->nslots && n > 0); + assert(n <= jl_linfo_nslots(lam) && n > 0); locals[n-1] = rhs; } else { @@ -520,10 +530,9 @@ static jl_value_t *eval_body(jl_array_t *stmts, jl_value_t **locals, jl_lambda_i jl_value_t *jl_interpret_toplevel_thunk(jl_lambda_info_t *lam) { - jl_expr_t *ast = (jl_expr_t*)lam->ast; - jl_array_t *stmts = jl_lam_body(ast)->args; + jl_array_t *stmts = lam->code; jl_value_t **locals; - JL_GC_PUSHARGS(locals, lam->nslots + lam->ngensym); + JL_GC_PUSHARGS(locals, jl_linfo_nslots(lam) + jl_linfo_ngensyms(lam)); jl_value_t *r = eval_body(stmts, locals, lam, 0, 1); JL_GC_POP(); return r; diff --git a/src/jltypes.c b/src/jltypes.c index 1bc170ca3ee8b..d974caab9aadd 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3370,6 +3370,10 @@ void jl_init_types(void) jl_int64_type = jl_new_bitstype((jl_value_t*)jl_symbol("Int64"), jl_any_type, jl_emptysvec, 64); + jl_uint8_type = NULL; + jl_uint8_type = jl_new_bitstype((jl_value_t*)jl_symbol("UInt8"), + jl_any_type, jl_emptysvec, 8); + jl_gensym_type = jl_new_datatype(jl_symbol("GenSym"), jl_any_type, jl_emptysvec, jl_svec1(jl_symbol("id")), jl_svec1(jl_long_type), 0, 0, 1); @@ -3431,6 +3435,9 @@ void jl_init_types(void) jl_svec(2, jl_symbol_type, jl_box_long(1))); + jl_array_uint8_type = jl_apply_type((jl_value_t*)jl_array_type, + jl_svec2(jl_uint8_type, jl_box_long(1))); + jl_expr_type = jl_new_datatype(jl_symbol("Expr"), jl_any_type, jl_emptysvec, @@ -3486,7 +3493,9 @@ void jl_init_types(void) jl_lambda_info_type = jl_new_datatype(jl_symbol("LambdaInfo"), jl_any_type, jl_emptysvec, - jl_svec(19, jl_symbol("ast"), jl_symbol("rettype"), + jl_svec(23, jl_symbol("code"), jl_symbol("slotnames"), + jl_symbol("slottypes"), jl_symbol("slotflags"), + jl_symbol("gensymtypes"), jl_symbol("rettype"), jl_symbol("sparam_syms"), jl_symbol("sparam_vals"), jl_symbol("tfunc"), jl_symbol("name"), jl_symbol("roots"), @@ -3495,20 +3504,21 @@ void jl_init_types(void) jl_symbol("specializations"), jl_symbol("module"), jl_symbol("def"), jl_symbol("file"), jl_symbol("line"), - jl_symbol("nslots"), jl_symbol("ngensym"), - jl_symbol("inferred"), - jl_symbol("pure"), + jl_symbol("nargs"), jl_symbol("inferred"), + jl_symbol("pure"), jl_symbol("isva"), jl_symbol("inInference")), - jl_svec(19, jl_any_type, jl_any_type, + jl_svec(23, jl_any_type, jl_array_any_type, + jl_any_type, jl_array_uint8_type, + jl_any_type, jl_any_type, jl_simplevector_type, jl_simplevector_type, jl_any_type, jl_sym_type, jl_any_type, jl_any_type, jl_any_type, jl_array_any_type, jl_module_type, jl_any_type, jl_sym_type, jl_int32_type, - jl_int32_type, jl_int32_type, + jl_int32_type, jl_bool_type, jl_bool_type, jl_bool_type, jl_bool_type), - 0, 1, 5); + 0, 1, 10); jl_typector_type = jl_new_datatype(jl_symbol("TypeConstructor"), @@ -3628,6 +3638,7 @@ void jl_init_types(void) unused_sym = jl_symbol("#unused#"); slot_sym = jl_symbol("slot"); static_parameter_sym = jl_symbol("static_parameter"); + compiler_temp_sym = jl_symbol("#temp#"); } #ifdef __cplusplus diff --git a/src/julia.h b/src/julia.h index 49e30d140123c..a590d5b0677d7 100644 --- a/src/julia.h +++ b/src/julia.h @@ -175,7 +175,11 @@ typedef struct _jl_lambda_info_t { // a function pointer. // this is the stuff that's shared among different instantiations // (different environments) of a closure. - jl_value_t *ast; + jl_array_t *code; // compressed uint8 array, or Any array of statements + jl_array_t *slotnames; // names of local variables + jl_value_t *slottypes; + jl_array_t *slotflags; // local var bit flags + jl_value_t *gensymtypes; jl_value_t *rettype; // sparams is a vector of values indexed by symbols jl_svec_t *sparam_syms; @@ -192,14 +196,14 @@ typedef struct _jl_lambda_info_t { struct _jl_lambda_info_t *def; // original this is specialized from jl_sym_t *file; int32_t line; - int32_t nslots; - int32_t ngensym; + int32_t nargs; int8_t inferred; int8_t pure; + int8_t isva; int8_t inInference; // flags to tell if inference is running on this function - uint8_t called; // bit flags: whether each of the first 8 arguments is called // hidden fields: + uint8_t called; // bit flags: whether each of the first 8 arguments is called uint8_t jlcall_api : 1; // the c-abi for fptr; 0 = jl_fptr_t, 1 = jl_fptr_sparam_t uint8_t inCompile : 1; // if there are intrinsic calls, sparams are probably required to compile successfully, @@ -487,7 +491,7 @@ extern jl_sym_t *null_sym; extern jl_sym_t *body_sym; extern jl_sym_t *method_sym; extern jl_sym_t *slot_sym; extern jl_sym_t *enter_sym; extern jl_sym_t *leave_sym; extern jl_sym_t *exc_sym; extern jl_sym_t *new_sym; -extern jl_sym_t *static_typeof_sym; +extern jl_sym_t *static_typeof_sym; extern jl_sym_t *compiler_temp_sym; extern jl_sym_t *const_sym; extern jl_sym_t *thunk_sym; extern jl_sym_t *anonymous_sym; extern jl_sym_t *underscore_sym; extern jl_sym_t *abstracttype_sym; extern jl_sym_t *bitstype_sym; @@ -644,6 +648,19 @@ STATIC_INLINE jl_value_t *jl_cellset(void *a, size_t i, void *x) return (jl_value_t*)x; } +STATIC_INLINE uint8_t jl_array_uint8_ref(void *a, size_t i) +{ + assert(i < jl_array_len(a)); + assert(jl_typeis(a, jl_array_uint8_type)); + return ((uint8_t*)(jl_array_data(a)))[i]; +} +STATIC_INLINE void jl_array_uint8_set(void *a, size_t i, uint8_t x) +{ + assert(i < jl_array_len(a)); + assert(jl_typeis(a, jl_array_uint8_type)); + ((uint8_t*)(jl_array_data(a)))[i] = x; +} + #define jl_exprarg(e,n) (((jl_value_t**)jl_array_data(((jl_expr_t*)(e))->args))[n]) #define jl_exprargset(e, n, v) jl_cellset(((jl_expr_t*)(e))->args, n, v) #define jl_expr_nargs(e) jl_array_len(((jl_expr_t*)(e))->args) @@ -1228,28 +1245,27 @@ JL_DLLEXPORT jl_module_t *jl_base_relative_to(jl_module_t *m); // AST access JL_DLLEXPORT int jl_is_rest_arg(jl_value_t *ex); -JL_DLLEXPORT jl_value_t *jl_prepare_ast(jl_lambda_info_t *li); JL_DLLEXPORT jl_value_t *jl_copy_ast(jl_value_t *expr); -JL_DLLEXPORT jl_value_t *jl_compress_ast(jl_lambda_info_t *li, jl_value_t *ast); -JL_DLLEXPORT jl_value_t *jl_uncompress_ast(jl_lambda_info_t *li, jl_value_t *data); +JL_DLLEXPORT jl_array_t *jl_compress_ast(jl_lambda_info_t *li, jl_array_t *ast); +JL_DLLEXPORT jl_array_t *jl_uncompress_ast(jl_lambda_info_t *li, jl_array_t *data); JL_DLLEXPORT int jl_is_operator(char *sym); JL_DLLEXPORT int jl_operator_precedence(char *sym); -STATIC_INLINE int jl_vinfo_assigned(jl_array_t *vi) +STATIC_INLINE int jl_vinfo_assigned(uint8_t vi) { - return (jl_unbox_long(jl_cellref(vi,2))&2)!=0; + return (vi&2)!=0; } -STATIC_INLINE int jl_vinfo_sa(jl_array_t *vi) +STATIC_INLINE int jl_vinfo_sa(uint8_t vi) { - return (jl_unbox_long(jl_cellref(vi,2))&16)!=0; + return (vi&16)!=0; } -STATIC_INLINE int jl_vinfo_usedundef(jl_array_t *vi) +STATIC_INLINE int jl_vinfo_usedundef(uint8_t vi) { - return (jl_unbox_long(jl_cellref(vi,2))&32)!=0; + return (vi&32)!=0; } // calling into julia --------------------------------------------------------- diff --git a/src/julia_internal.h b/src/julia_internal.h index f9a3dda8a04a9..6ea7ae0f0a581 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -194,19 +194,17 @@ jl_lambda_info_t *jl_method_lookup_by_type(jl_methtable_t *mt, jl_tupletype_t *t int cache, int inexact); jl_lambda_info_t *jl_method_lookup(jl_methtable_t *mt, jl_value_t **args, size_t nargs, int cache); jl_value_t *jl_gf_invoke(jl_tupletype_t *types, jl_value_t **args, size_t nargs); -jl_sym_t *jl_decl_var(jl_value_t *ex); jl_array_t *jl_lam_args(jl_expr_t *l); jl_array_t *jl_lam_vinfo(jl_expr_t *l); jl_array_t *jl_lam_capt(jl_expr_t *l); jl_value_t *jl_lam_gensyms(jl_expr_t *l); jl_array_t *jl_lam_staticparams(jl_expr_t *l); -jl_sym_t *jl_lam_argname(jl_lambda_info_t *li, int i); int jl_lam_vars_captured(jl_expr_t *ast); jl_expr_t *jl_lam_body(jl_expr_t *l); jl_value_t *jl_first_argument_datatype(jl_value_t *argtypes); jl_value_t *jl_preresolve_globals(jl_value_t *expr, jl_lambda_info_t *lam); -int jl_has_intrinsics(jl_lambda_info_t *li, jl_expr_t *e, jl_module_t *m); +int jl_has_intrinsics(jl_lambda_info_t *li, jl_value_t *v, jl_module_t *m); jl_value_t *jl_nth_slot_type(jl_tupletype_t *sig, size_t i); void jl_compute_field_offsets(jl_datatype_t *st); diff --git a/src/toplevel.c b/src/toplevel.c index 44a010370fc5b..9f66e6adeacd6 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -240,8 +240,10 @@ JL_DLLEXPORT jl_module_t *jl_base_relative_to(jl_module_t *m) return jl_top_module; } -int jl_has_intrinsics(jl_lambda_info_t *li, jl_expr_t *e, jl_module_t *m) +int jl_has_intrinsics(jl_lambda_info_t *li, jl_value_t *v, jl_module_t *m) { + if (!jl_is_expr(v)) return 0; + jl_expr_t *e = (jl_expr_t*)v; if (jl_array_len(e->args) == 0) return 0; if (e->head == static_typeof_sym) @@ -261,7 +263,7 @@ int jl_has_intrinsics(jl_lambda_info_t *li, jl_expr_t *e, jl_module_t *m) int i; for (i=0; i < jl_array_len(e->args); i++) { jl_value_t *a = jl_exprarg(e,i); - if (jl_is_expr(a) && jl_has_intrinsics(li, (jl_expr_t*)a, m)) + if (jl_is_expr(a) && jl_has_intrinsics(li, a, m)) return 1; } return 0; @@ -269,51 +271,49 @@ int jl_has_intrinsics(jl_lambda_info_t *li, jl_expr_t *e, jl_module_t *m) // heuristic for whether a top-level input should be evaluated with // the compiler or the interpreter. -static int jl_eval_with_compiler_p(jl_lambda_info_t *li, jl_expr_t *expr, int compileloops, jl_module_t *m) +static int jl_eval_with_compiler_p(jl_lambda_info_t *li, jl_array_t *body, int compileloops, jl_module_t *m) { - assert(jl_is_expr(expr)); - if (expr->head==body_sym && compileloops) { - jl_array_t *body = expr->args; - size_t i, maxlabl=0; - // compile if there are backwards branches - for(i=0; i < jl_array_len(body); i++) { - jl_value_t *stmt = jl_cellref(body,i); - if (jl_is_labelnode(stmt)) { - int l = jl_labelnode_label(stmt); - if (l > maxlabl) maxlabl = l; - } + size_t i, maxlabl=0; + // compile if there are backwards branches + for(i=0; i < jl_array_len(body); i++) { + jl_value_t *stmt = jl_cellref(body,i); + if (jl_is_labelnode(stmt)) { + int l = jl_labelnode_label(stmt); + if (l > maxlabl) maxlabl = l; + } + } + size_t sz = (maxlabl+1+7)/8; + char *labls = (char*)alloca(sz); memset(labls,0,sz); + for(i=0; i < jl_array_len(body); i++) { + jl_value_t *stmt = jl_cellref(body,i); + if (jl_is_labelnode(stmt)) { + int l = jl_labelnode_label(stmt); + labls[l/8] |= (1<<(l&7)); } - size_t sz = (maxlabl+1+7)/8; - char *labls = (char*)alloca(sz); memset(labls,0,sz); - for(i=0; i < jl_array_len(body); i++) { - jl_value_t *stmt = jl_cellref(body,i); - if (jl_is_labelnode(stmt)) { - int l = jl_labelnode_label(stmt); - labls[l/8] |= (1<<(l&7)); + else if (compileloops && jl_is_gotonode(stmt)) { + int l = jl_gotonode_label(stmt); + if (labls[l/8]&(1<<(l&7))) { + return 1; } - else if (compileloops && jl_is_gotonode(stmt)) { - int l = jl_gotonode_label(stmt); + } + else if (jl_is_expr(stmt)) { + if (compileloops && ((jl_expr_t*)stmt)->head==goto_ifnot_sym) { + int l = jl_unbox_long(jl_exprarg(stmt,1)); if (labls[l/8]&(1<<(l&7))) { return 1; } } - else if (jl_is_expr(stmt)) { - if (compileloops && ((jl_expr_t*)stmt)->head==goto_ifnot_sym) { - int l = jl_unbox_long(jl_exprarg(stmt,1)); - if (labls[l/8]&(1<<(l&7))) { - return 1; - } - } - // to compile code that uses exceptions - /* - if (((jl_expr_t*)stmt)->head == enter_sym) { - return 1; - } - */ - } } + if (jl_has_intrinsics(li, stmt, m)) return 1; } - if (jl_has_intrinsics(li, expr, m)) return 1; + return 0; +} + +static int jl_eval_expr_with_compiler_p(jl_value_t *e, int compileloops, jl_module_t *m) +{ + if (jl_is_expr(e) && ((jl_expr_t*)e)->head == body_sym) + return jl_eval_with_compiler_p(NULL, ((jl_expr_t*)e)->args, compileloops, m); + if (jl_has_intrinsics(NULL, e, m)) return 1; return 0; } @@ -519,14 +519,11 @@ jl_value_t *jl_toplevel_eval_flex(jl_value_t *e, int fast) if (head == thunk_sym) { thk = (jl_lambda_info_t*)jl_exprarg(ex,0); assert(jl_is_lambda_info(thk)); - if (!jl_is_expr(thk->ast)) { - thk->ast = jl_uncompress_ast(thk, thk->ast); - jl_gc_wb(thk, thk->ast); - } - ewc = jl_eval_with_compiler_p(thk, jl_lam_body((jl_expr_t*)thk->ast), fast, jl_current_module); + assert(jl_typeis(thk->code, jl_array_any_type)); + ewc = jl_eval_with_compiler_p(thk, thk->code, fast, jl_current_module); } else { - if (head && jl_eval_with_compiler_p(NULL, (jl_expr_t*)ex, fast, jl_current_module)) { + if (head && jl_eval_expr_with_compiler_p((jl_value_t*)ex, fast, jl_current_module)) { thk = jl_wrap_expr((jl_value_t*)ex); ewc = 1; } @@ -616,25 +613,19 @@ void print_func_loc(JL_STREAM *s, jl_lambda_info_t *li); void jl_check_static_parameter_conflicts(jl_lambda_info_t *li, jl_svec_t *t, jl_sym_t *fname) { - jl_array_t *vinfo; - size_t nvars; - - if (li->ast && jl_is_expr(li->ast)) { - vinfo = jl_lam_vinfo((jl_expr_t*)li->ast); - nvars = jl_array_len(vinfo); - for(size_t i=0; i < jl_svec_len(t); i++) { - for(size_t j=0; j < nvars; j++) { - jl_value_t *tv = jl_svecref(t,i); - if (jl_is_typevar(tv)) { - if ((jl_sym_t*)jl_cellref((jl_array_t*)jl_cellref(vinfo,j),0) == - ((jl_tvar_t*)tv)->name) { - jl_printf(JL_STDERR, - "WARNING: local variable %s conflicts with a static parameter in %s", - jl_symbol_name(((jl_tvar_t*)tv)->name), - jl_symbol_name(fname)); - print_func_loc(JL_STDERR, li); - jl_printf(JL_STDERR, ".\n"); - } + size_t nvars = jl_array_len(li->slotnames); + + for(size_t i=0; i < jl_svec_len(t); i++) { + for(size_t j=0; j < nvars; j++) { + jl_value_t *tv = jl_svecref(t,i); + if (jl_is_typevar(tv)) { + if ((jl_sym_t*)jl_cellref(li->slotnames, j) == ((jl_tvar_t*)tv)->name) { + jl_printf(JL_STDERR, + "WARNING: local variable %s conflicts with a static parameter in %s", + jl_symbol_name(((jl_tvar_t*)tv)->name), + jl_symbol_name(fname)); + print_func_loc(JL_STDERR, li); + jl_printf(JL_STDERR, ".\n"); } } } @@ -726,7 +717,7 @@ static jl_lambda_info_t *expr_to_lambda(jl_expr_t *f) } // wrap in a LambdaInfo jl_lambda_info_t *li = jl_new_lambda_info((jl_value_t*)f, tvar_syms, jl_emptysvec, jl_current_module); - jl_preresolve_globals(li->ast, li); + jl_preresolve_globals((jl_value_t*)li, li); JL_GC_POP(); return li; } @@ -771,7 +762,7 @@ JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, jl_lambda_info_t *f, jl_valu jl_value_t *elt = jl_tparam(argtypes,i); if (!jl_is_type(elt) && !jl_is_typevar(elt)) { jl_exceptionf(jl_argumenterror_type, "invalid type for argument %s in method definition for %s at %s:%d", - jl_symbol_name(jl_lam_argname(f,i)), + jl_symbol_name((jl_sym_t*)jl_cellref(f->slotnames,i)), jl_symbol_name(name), jl_symbol_name(f->file), f->line); } @@ -792,9 +783,9 @@ JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, jl_lambda_info_t *f, jl_valu } jl_add_method_to_table(mt, argtypes, f, tvars, isstaged == jl_true); - if (jl_boot_file_loaded && f->ast && jl_is_expr(f->ast)) { - f->ast = jl_compress_ast(f, f->ast); - jl_gc_wb(f, f->ast); + if (jl_boot_file_loaded && f->code && jl_typeis(f->code, jl_array_any_type)) { + f->code = jl_compress_ast(f, f->code); + jl_gc_wb(f, f->code); } JL_GC_POP(); } diff --git a/test/core.jl b/test/core.jl index dca1c6d3b2aa6..34049084b52fd 100644 --- a/test/core.jl +++ b/test/core.jl @@ -3189,7 +3189,7 @@ typealias PossiblyInvalidUnion{T} Union{T,Int} @test split(string(gensym("abc")),'#')[3] == "abc" # meta nodes for optional positional arguments -@test Base.uncompressed_ast(expand(:(@inline f(p::Int=2) = 3)).args[2].args[3]).args[3].args[1].args[1] === :inline +@test Base.uncompressed_ast(expand(:(@inline f(p::Int=2) = 3)).args[2].args[3])[1].args[1] === :inline # issue #13007 call13007{T,N}(::Type{Array{T,N}}) = 0 diff --git a/test/inference.jl b/test/inference.jl index c3afe6ffd9223..71f1641187755 100644 --- a/test/inference.jl +++ b/test/inference.jl @@ -185,9 +185,7 @@ end end let ast12474 = code_typed(f12474, Tuple{Float64}) - for (_, vartype) in ast12474[1].args[2][1] - @test isleaftype(vartype) - end + @test all(isleaftype, ast12474[1].slottypes) end @@ -200,7 +198,7 @@ end @eval f15259(x,y) = (a = $(Expr(:new, :A15259, :x, :y)); (a.x, a.y, getfield(a,1), getfield(a, 2))) @test isempty(filter(x -> isa(x,Expr) && x.head === :(=) && isa(x.args[2], Expr) && x.args[2].head === :new, - code_typed(f15259, (Any,Int))[1].args[3].args)) + code_typed(f15259, (Any,Int))[1].code)) @test f15259(1,2) == (1,2,1,2) # check that error cases are still correct @eval g15259(x,y) = (a = $(Expr(:new, :A15259, :x, :y)); a.z) diff --git a/test/inline.jl b/test/inline.jl index 917dc4e045171..c66ada585dadb 100644 --- a/test/inline.jl +++ b/test/inline.jl @@ -20,9 +20,8 @@ Helper to test that every slot is in range after inlining. """ function test_inlined_symbols(func, argtypes) linfo = code_typed(func, argtypes)[1] - locals = linfo.args[2][1] - nl = length(locals) - ast = linfo.args[3] + nl = length(linfo.slottypes) + ast = Expr(:body); ast.args = Base.uncompressed_ast(linfo) walk(ast) do e if isa(e, Slot) @test 1 <= e.id <= nl diff --git a/test/meta.jl b/test/meta.jl index e1e076aec6cac..46f47eb7832ac 100644 --- a/test/meta.jl +++ b/test/meta.jl @@ -61,7 +61,7 @@ asts = code_lowered(dummy, Tuple{}) ast = asts[1] body = Expr(:block) -body.args = ast.args[3].args +body.args = Base.uncompressed_ast(ast) @test popmeta!(body, :test) == (true, [42]) @test popmeta!(body, :nonexistent) == (false, []) From e6400abc52772841ae17cccc7f9f72075be5331c Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 10 Mar 2016 00:45:19 -0500 Subject: [PATCH 5/6] fix some cases of compile-time evaluation with interpreter changes --- src/interpreter.c | 4 ++-- src/intrinsics.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/interpreter.c b/src/interpreter.c index 1e7ccd285fcd0..fb55d5bc9dbc9 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -132,7 +132,7 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, jl_lambda_info_t *la } if (jl_is_gensym(e)) { ssize_t genid = ((jl_gensym_t*)e)->id; - if (genid >= jl_linfo_ngensyms(lam) || genid < 0) + if (genid >= jl_linfo_ngensyms(lam) || genid < 0 || locals == NULL) jl_error("access to invalid GenSym location"); else return locals[jl_linfo_nslots(lam) + genid]; @@ -150,7 +150,7 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, jl_lambda_info_t *la if (!jl_is_expr(e)) { if (jl_typeis(e, jl_slot_type)) { ssize_t n = jl_slot_number(e); - if (n > jl_linfo_nslots(lam) || n < 1) + if (n > jl_linfo_nslots(lam) || n < 1 || locals == NULL) jl_error("access to invalid slot number"); jl_value_t *v = locals[n-1]; if (v == NULL) diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 573e127e3bc90..97c8c570872a7 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -350,11 +350,11 @@ static jl_value_t *staticeval_bitstype(jl_value_t *targ, const char *fname, jl_c bt = jl_tparam0(et); } else { - bt = try_eval(targ, ctx, NULL); // TODO: change this to an actual call to staticeval rather than actually executing code + bt = jl_static_eval(targ, ctx, ctx->module, ctx->linfo, true, true); if (bt) jl_add_linfo_root(ctx->linfo, bt); } - if (fname && (!bt || !jl_is_bitstype(bt))) { - jl_errorf("%s: expected bits type as first argument", fname); + if (!bt || !jl_is_bitstype(bt)) { + emit_error("expected bits type as first argument", ctx); return NULL; } return bt; From 1ee954aa427f99aead073b0a8031136c566f5401 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Wed, 23 Mar 2016 23:39:59 -0400 Subject: [PATCH 6/6] doc and NEWS updates for IR changes --- NEWS.md | 8 ++++++++ doc/devdocs/ast.rst | 50 ++++++++++++++++++--------------------------- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/NEWS.md b/NEWS.md index 95add100209f9..007c3c8bf3190 100644 --- a/NEWS.md +++ b/NEWS.md @@ -47,6 +47,13 @@ Compiler/Runtime improvements Breaking changes ---------------- + * Local variables and arguments are represented in lowered code as numbered `Slot` + objects instead of as symbols ([#15609]). + + * The information that used to be in the `ast` field of the `LambdaStaticData` type + is now divided among the fields `code`, `slotnames`, `slottypes`, `slotflags`, + `gensymtypes`, `rettype`, `nargs`, and `isva` in the `LambdaInfo` type ([#15609]). + Library improvements -------------------- @@ -173,3 +180,4 @@ Deprecated or removed [#15242]: https://github.com/JuliaLang/julia/issues/15242 [#15258]: https://github.com/JuliaLang/julia/issues/15258 [#15550]: https://github.com/JuliaLang/julia/issues/15550 +[#15609]: https://github.com/JuliaLang/julia/issues/15609 diff --git a/doc/devdocs/ast.rst b/doc/devdocs/ast.rst index b01e3b97f4f03..1a137113c3688 100644 --- a/doc/devdocs/ast.rst +++ b/doc/devdocs/ast.rst @@ -4,12 +4,13 @@ Julia ASTs .. currentmodule:: Base -Julia has two AST representations. First there is a surface syntax AST returned +Julia has two representations of code. First there is a surface syntax AST returned by the parser (e.g. the :func:`parse` function), and manipulated by macros. It is a structured representation of code as it is written, constructed by ``julia-parser.scm`` from a character stream. -Next there is a lowered AST which is used by type inference and code generation. -In the lowered form, there are generally fewer types of nodes, all macros +Next there is a lowered form, or IR (intermediate representation), which is used by +type inference and code generation. +In the lowered form there are fewer types of nodes, all macros are expanded, and all control flow is converted to explicit branches and sequences of statements. The lowered form is constructed by ``julia-syntax.scm``. @@ -21,17 +22,17 @@ significant rearrangement of the input syntax. Lowered form ------------ -The following data types exist in lowered ASTs: +The following data types exist in lowered form: ``Expr`` has a node type indicated by the ``head`` field, and an ``args`` field which is a ``Vector{Any}`` of subexpressions. -``Symbol`` - used to name local variables and static parameters within a function. +``Slot`` + identifies arguments and local variables by consecutive numbering. ``LambdaInfo`` - wraps the AST of each method. + wraps the IR of each method. ``LineNumberNode`` line number metadata @@ -54,9 +55,6 @@ The following data types exist in lowered ASTs: forces a name to be resolved as a global in Base. This is now mostly redundant with ``GlobalRef(Base, :x)``. -``SymbolNode`` - used to annotate a local variable with a type - ``GenSym`` refers to a consecutively-numbered (starting at 0) static single assignment (SSA) variable inserted by the compiler. @@ -75,6 +73,9 @@ These symbols appear in the ``head`` field of ``Expr``\s in lowered form. function call. ``args[1]`` is the function to call, ``args[2:end]`` are the arguments. +``static_parameter`` + reference a static parameter by index. + ``line`` line number and file name metadata. Unlike a ``LineNumberNode``, can also contain a file name. @@ -181,22 +182,13 @@ LambdaInfo ``sparam_vals`` - The values of the static parameters (once known), indexed by ``sparam_syms``. -Has an ``->ast`` field pointing to an ``Expr`` with head ``lambda``. This -``Expr`` has the following layout: +``code`` - An ``Any`` array of statements, or a UInt8 array with a compressed representation of the code. -``args[1]`` - ``Vector{Any}`` of argument name symbols. For varargs functions, the last - element is actually an ``Expr`` with head ``...``. The argument of this - ``Expr`` is an ``Expr`` with head ``::``. The first argument of ``::`` is a - symbol (the argument name), and the second argument is a type declaration. +``slotnames`` - An array of symbols giving the name of each slot (argument or local variable). -``args[2]`` - A ``Vector{Any}`` with variable information: +``slottypes`` - An array of types for the slots. - ``args[2][1]`` - An array of 3-element ``varinfo`` arrays, one for each - argument or local variable. A ``varinfo`` array has the form ``Any[:name, - type, bits]``. The ``bits`` field is an integer - describing variable properties as follows: +``slotflags`` - A UInt8 array of slot properties, represented as bit flags: - 1 - captured (closed over) - 2 - assigned (only false if there are *no* assignment statements with this var on the left) - 4 - assigned by an inner function @@ -204,15 +196,13 @@ Has an ``->ast`` field pointing to an ``Expr`` with head ``lambda``. This - 16 - statically assigned once - 32 - might be used before assigned. This flag is only valid after type inference. - ``args[2][2]`` - An array of ``varinfo`` triples for each outer variable - this function captures. +``gensymtypes`` - Either an array or an Int giving the number of compiler-inserted + temporary locations in the function. If an array, specifies a type for each location. - ``args[2][3]`` - The types of variables represented by ``GenSym`` objects. - Given ``GenSym`` ``g``, its type will be at ``args[2][3][g.id+1]``. +``nargs`` - The number of argument slots. The first ``nargs`` entries of the slots + arrays refer to arguments. -``args[3]`` - an ``Expr`` with head ``body`` whose arguments are the statements - comprising the function body. +``isva`` - A boolean indicating whether the function is variadic. Surface syntax AST