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/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/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/expr.jl b/base/expr.jl index bf5f522786726..e4d8a255fec99 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) @@ -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/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 9a76eb1e21eb5..e7d524bb694ac 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 @@ -21,12 +20,8 @@ end type InferenceState atypes #::Type # type sig - 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 fedbackvars::Dict{GenSym, Bool} mod::Module @@ -35,8 +30,8 @@ type InferenceState # info on the state of inference and the linfo linfo::LambdaInfo - args::Vector{Any} - labels::Vector{Int} + destination::LambdaInfo # results need to be copied here when we finish + nargs::Int stmt_types::Vector{Any} # return type bestguess #::Type @@ -63,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} - vars = map(vi->vi[1], 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 @@ -81,34 +71,21 @@ 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) + n = length(linfo.code) 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 = 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]} 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 @@ -132,17 +109,17 @@ 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 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 @@ -154,9 +131,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, sp, gensym_types, nl, Dict{GenSym, Bool}(), linfo.module, 0, false, - linfo, args, labels, 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}}(), @@ -185,16 +162,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) @@ -204,10 +171,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) @@ -558,24 +521,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) @@ -713,13 +670,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 @@ -1198,14 +1153,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 @@ -1233,6 +1188,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) @@ -1314,53 +1285,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 @@ -1368,10 +1305,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 @@ -1383,7 +1317,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 @@ -1444,44 +1378,71 @@ 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 false + return state end +stchanged(new::Tuple{}, old::Tuple{}) = false +stchanged(new, old::Tuple{}) = true -#### helper functions for typeinf initialization and looping #### +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 findlabel(labels, l) - i = l+1 > length(labels) ? 0 : labels[l+1] - if i == 0 - error("label ",l," not found") +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 i + return false end +#### helper functions for typeinf initialization and looping #### + function label_counter(body) l = -1 for b in body @@ -1535,11 +1496,34 @@ 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) - +# 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 #### @@ -1548,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. @@ -1562,28 +1546,25 @@ 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,LambdaInfo) + @assert code.inferred + return (code, code.rettype, true) else - inftree, inftyp = code - if linfo.inInference - linfo.inferred = true - linfo.ast = inftree - linfo.rettype = inftyp - linfo.inInference = false - end - return (inftree, inftyp, true) # code is a tuple (ast, type) + # otherwise this is an InferenceState from a different bootstrap stage's + # copy of the inference code; ignore it. end end end @@ -1617,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) @@ -1653,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) @@ -1670,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 @@ -1754,8 +1749,8 @@ function typeinf_frame(frame) else frame.cur_hand = frame.handler_at[pc] end - stmt = frame.body[pc] - changes = abstract_interpret(stmt, s[pc]::ObjectIdDict, frame) + 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, # need to continue to the next pc even though the return type was Bottom @@ -1766,9 +1761,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 @@ -1787,13 +1782,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) @@ -1801,10 +1796,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) @@ -1836,21 +1831,23 @@ 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) 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] 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 @@ -1870,8 +1867,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´ @@ -1979,40 +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.args) + 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] - # 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]) + 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) + 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.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 @@ -2028,48 +2031,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) @@ -2087,12 +2069,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) @@ -2112,14 +2090,15 @@ 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!(linfo::LambdaInfo, states::Array{Any,1}, sv::ANY, rettype::ANY, nargs) + nslots = length(states[1]) + decls = Any[ NF for i = 1:nslots ] + undefs = fill(false, nslots) # initialize decls with argument types - for 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} + body = linfo.code for i=1:length(body) st_i = states[i] if st_i !== () @@ -2127,85 +2106,44 @@ function type_annotate(ast::Expr, states::Array{Any,1}, sv::ANY, rettype::ANY, a 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 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 + if decls[i] !== NF + linfo.slottypes[i] = 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]) - vi[3] |= 32 + if undefs[i] + linfo.slotflags[i] |= 32 end end - ast.args[2][3] = sv.gensym_types - - return ast + nothing 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 + return NewvarNode(substitute!(e.slot, na, argexprs, spvals, offset)) end - if !isa(e,Expr) - return e - 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) @@ -2220,28 +2158,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) @@ -2282,19 +2215,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) @@ -2329,12 +2254,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 @@ -2376,20 +2295,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 #### @@ -2398,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, @@ -2493,12 +2398,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 @@ -2508,7 +2412,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 @@ -2521,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 @@ -2542,20 +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 - needcopy = true - if !isa(ast,Expr) + ast = linfo.code + + if !isa(ast,Array{Any,1}) 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} + 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 @@ -2569,6 +2474,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 @@ -2588,42 +2495,32 @@ 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 = linfo.nargs # check for vararg function - args = f_argnames(ast) - na = length(args) - 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 - 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 @@ -2640,11 +2537,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 @@ -2655,28 +2552,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 @@ -2695,7 +2570,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 @@ -2738,14 +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 - 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 linfo.slotflags[i] != 0 + islocal = true + aeitype = tmerge(aeitype, linfo.slottypes[i]) end # ok for argument to occur more than once if the actual argument @@ -2755,7 +2625,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)) @@ -2781,7 +2650,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 @@ -2799,9 +2668,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, aeitype, #=SSA=#false) + argexprs[i] = vnew end unshift!(stmts, Expr(:(=), vnew, aei)) stmts_free &= free @@ -2822,22 +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 = sym_replace(body, args, spnames, argexprs, spvals) + 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) @@ -2867,7 +2733,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 = linfo.rettype + local retval multiret = false lastexpr = pop!(body.args) if isa(lastexpr,LabelNode) @@ -2883,6 +2750,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, rettype, false) + end multiret = true unshift!(a.args, retval) a.head = :(=) @@ -2892,15 +2763,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 @@ -2957,7 +2826,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 @@ -2987,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,()) @@ -2997,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, @@ -3044,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) @@ -3098,7 +2972,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] @@ -3119,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]) @@ -3178,73 +3052,12 @@ 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] - 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)!(isa(x,Expr) && (x.head === :(=) || x.head === :const) && + isa(x.args[1],T) && x.args[1].id == id), + 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 - return false + linfo end -function delete_var!(ast, v) - if !isa(v, GenSym) - filter!(vi->!symequal(vi[1],v), ast.args[2][1]) +function _slot_replace!(e, id, rhs, T) + if isa(e,T) && e.id == id + return rhs end - filter!(x->!(isa(x,Expr) && (x.head === :(=) || x.head === :const) && - symequal(x.args[1],v)), - ast.args[3].args) - ast + 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, 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 -function remove_redundant_temp_vars(ast, sa) - varinfo = ast.args[2][1] - gensym_types = ast.args[2][3] - body = ast.args[3] +# T: Slot or Gensym +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,Symbol) || isa(init,SymbolNode)) && - _any(vi->symequal(vi[1],init), varinfo) && - !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 !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 (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] : linfo.slottypes[v]) + delete_var!(linfo, v, T) + slot_replace!(linfo, v, init, T) end end end end - ast + linfo 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 -function find_sa_vars(ast) - body = ast.args[3].args +# compute set of slots assigned once +function find_sa_vars(linfo::LambdaInfo) + body = linfo.code av = ObjectIdDict() av2 = ObjectIdDict() - vinfos = ast.args[2][1]::Array{Any,1} - args = ast.args[1] + gss = ObjectIdDict() + nargs = linfo.nargs 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) @@ -3407,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 @@ -3486,17 +3286,20 @@ 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 = find_sa_vars(ast) - remove_redundant_temp_vars(ast, vs) +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] - 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 @@ -3506,7 +3309,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 @@ -3529,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) && @@ -3551,16 +3354,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 + linfo.slottypes[val.id] = a.typ end elseif isa(val,GenSym) val = val::GenSym @@ -3571,7 +3369,32 @@ 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!(linfo::LambdaInfo, sv::InferenceState) + body = linfo.code + mapping = zeros(Int, sv.label_counter) + for i = 1:length(body) + el = body[i] + if isa(el,LabelNode) + mapping[el.label] = i + body[i] = LabelNode(i) + end + end + for i = 1:length(body) + el = body[i] + if isa(el,GotoNode) + body[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 @@ -3583,6 +3406,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 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 7b1a5ee9499da..27a21e4fd3c56 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, @@ -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.ast = ast + 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 linfo.inferred = infr linfo.module = mod - linfo.roots = roots 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 56c4e06cedfaf..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, @@ -320,7 +321,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 +495,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/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/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 diff --git a/src/alloc.c b/src/alloc.c index 24aa1d59708fc..71a49cea8d820 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; @@ -97,9 +96,9 @@ 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 *unused_sym; jl_sym_t *static_parameter_sym; typedef struct { int64_t a; @@ -272,6 +271,69 @@ 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) +{ + 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); + 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*)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); + 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))); + } + jl_lambda_info_init_properties(li); +} + 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) @@ -279,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; @@ -306,44 +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 && 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_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; @@ -671,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 @@ -872,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) @@ -879,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 dfd046e7f49a9..dbafa41d894a8 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)) @@ -484,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; } @@ -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)) @@ -914,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) { @@ -957,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; @@ -1014,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(); @@ -1075,32 +1041,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 +1048,16 @@ 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_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; diff --git a/src/builtins.c b/src/builtins.c index b4046aa892940..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; } @@ -1157,6 +1151,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 +1348,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..071adbb3243fc 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -848,23 +848,33 @@ 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)) { + if (jl_is_long(ctx->linfo->gensymtypes)) + return (jl_value_t*)jl_any_type; + int idx = ((jl_gensym_t*)e)->id; + 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); 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 +900,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 +1087,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 +1568,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..e9433b0867986 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -549,22 +549,19 @@ 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; - jl_expr_t *ast; jl_lambda_info_t *linfo; Value *spvals_ptr; 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 +590,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 +729,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(ctx->linfo->slotnames, s); +} + +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 +756,17 @@ 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 + // 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; } -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); } +} - jl_varinfo_t &vi = ctx->vars[sym]; +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); +} + +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(); @@ -3058,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) { @@ -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. @@ -3905,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(); @@ -3938,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; @@ -3990,15 +3945,13 @@ 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; + std::map arrayvars; std::map labels; std::map handlers; jl_codectx_t ctx; @@ -4006,22 +3959,19 @@ 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.vaName = NULL; + ctx.vaSlot = -1; ctx.vaStack = false; ctx.inbounds.push_back(false); ctx.boundsCheck.push_back(false); 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; @@ -4030,12 +3980,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 && lam->isva) { nreq--; va = 1; - jl_sym_t *vn = jl_decl_var(jl_cellref(largs,nreq)); + jl_sym_t *vn = (jl_sym_t*)jl_cellref(lam->slotnames,largslen-1); if (vn != unused_sym) - ctx.vaName = vn; + ctx.vaSlot = largslen-1; } ctx.nReqArgs = nreq; @@ -4046,44 +3996,43 @@ 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.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); } 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.vars[vname]; - varinfo.isAssigned = (jl_vinfo_assigned(vi)!=0); + jl_varinfo_t &varinfo = ctx.slots[i]; + 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.vars); + mark_volatile_vars(stmts, ctx.slots); // step 4. determine function signature jl_value_t *jlrettype = lam->rettype; @@ -4150,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); } @@ -4278,7 +4227,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)); } @@ -4327,9 +4276,9 @@ 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.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,33 +4303,33 @@ 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) #endif } 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_sym_t *s = (jl_sym_t*)jl_cellref(lam->slotnames,i); + jl_varinfo_t &varinfo = ctx.slots[i]; if (varinfo.isArgument) continue; #ifdef LLVM38 @@ -4441,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) { @@ -4459,10 +4407,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 = 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 if (!varinfo.usedUndef) { if (varinfo.value.constant) { @@ -4474,9 +4422,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 +4440,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 && 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 @@ -4516,7 +4464,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 @@ -4524,11 +4472,15 @@ 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); - 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 +4563,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..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,15 +610,7 @@ 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_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) || @@ -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,8 +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->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); @@ -1184,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); @@ -1420,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); @@ -1439,9 +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->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); @@ -2028,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 @@ -2050,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; } @@ -2063,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 @@ -2078,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; @@ -2363,7 +2370,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, @@ -2396,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), @@ -2415,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, @@ -2427,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, @@ -2435,7 +2442,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/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 2865b5ba0693a..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"); @@ -840,11 +839,7 @@ 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, - 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 af2dc188af829..fb55d5bc9dbc9 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,30 @@ 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 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 = 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 >= jl_linfo_ngensyms(lam) || genid < 0 || locals == NULL) jl_error("access to invalid GenSym location"); else - return locals[nl*2 + genid]; + return locals[jl_linfo_nslots(lam) + genid]; } if (jl_is_quotenode(e)) { return jl_fieldref(e,0); @@ -157,6 +148,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 > 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) + jl_undefined_var_error((jl_sym_t*)jl_cellref(lam->slotnames,n-1)); + 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 +166,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 <= jl_linfo_nslots(lam) && n > 0); + locals[n-1] = NULL; return (jl_value_t*)jl_nothing; } return e; @@ -182,47 +178,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 >= jl_linfo_ngensyms(lam) || genid < 0) jl_error("assignment to invalid GenSym location"); - locals[nl*2 + genid] = rhs; - return rhs; + locals[jl_linfo_nslots(lam) + 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 <= jl_linfo_nslots(lam) && 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 +224,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 +248,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 +260,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 +287,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 +300,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 +316,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 +332,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 +345,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 +364,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++) { @@ -453,18 +443,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; @@ -475,16 +453,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; @@ -495,20 +469,19 @@ 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)) { 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 = 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) { - 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) { @@ -516,19 +489,19 @@ 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_ 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; } } @@ -540,14 +513,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++; } @@ -555,39 +528,16 @@ 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_array_t *stmts = lam->code; 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, jl_linfo_nslots(lam) + jl_linfo_ngensyms(lam)); + 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/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; diff --git a/src/jltypes.c b/src/jltypes.c index 95460b29ebea3..d974caab9aadd 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; @@ -3369,9 +3370,17 @@ 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, 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(); @@ -3426,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, @@ -3457,8 +3469,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 +3493,9 @@ 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(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"), @@ -3490,18 +3504,21 @@ void jl_init_types(void) jl_symbol("specializations"), jl_symbol("module"), jl_symbol("def"), jl_symbol("file"), jl_symbol("line"), - jl_symbol("inferred"), - jl_symbol("pure"), + jl_symbol("nargs"), jl_symbol("inferred"), + jl_symbol("pure"), jl_symbol("isva"), jl_symbol("inInference")), - jl_svec(17, 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_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"), @@ -3619,6 +3636,9 @@ 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"); + compiler_temp_sym = jl_symbol("#temp#"); } #ifdef __cplusplus diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index c0b6e4dd8a5b6..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)) @@ -2731,7 +2730,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 @@ -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) @@ -3225,14 +3228,71 @@ 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! lam label2idx) + (let loop ((stmts (cdr (lam:body lam)))) + (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-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) + (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 (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) diff --git a/src/julia.h b/src/julia.h index d293159bca69b..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,12 +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 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, @@ -379,6 +385,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 +456,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,10 +488,10 @@ 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; +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; @@ -495,7 +501,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 ------------------------------------------------------------------------- @@ -642,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) @@ -650,11 +669,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 +788,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,37 +1238,34 @@ 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 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 72edff4c96b54..6ea7ae0f0a581 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); @@ -196,20 +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); -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); +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/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/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 752d5918aad25..c66ada585dadb 100644 --- a/test/inline.jl +++ b/test/inline.jl @@ -16,17 +16,18 @@ 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]) - ast = linfo.args[3] + nl = length(linfo.slottypes) + ast = Expr(:body); ast.args = Base.uncompressed_ast(linfo) 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 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, [])