# parameters limiting potentially-infinite types const MAX_TYPEUNION_LEN = 3 const MAX_TYPE_DEPTH = 4 const MAX_TUPLETYPE_LEN = 8 const MAX_TUPLE_DEPTH = 4 type NotFound end const NF = NotFound() type StaticVarInfo sp::Tuple # static parameters tuple cenv::ObjectIdDict # types of closed vars vars::Array{Any,1} # names of args and locals end type EmptyCallStack end type CallStack ast mod::Module types::Tuple recurred::Bool cycleid::Int result prev::Union(EmptyCallStack,CallStack) sv::StaticVarInfo CallStack(ast, mod, types, prev) = new(ast, mod, types, false, 0, None, prev) end inference_stack = EmptyCallStack() function is_static_parameter(sv::StaticVarInfo, s::Symbol) sp = sv.sp for i=1:2:length(sp) if is(sp[i].name,s) return true end end return false end function contains_is(itr, x::ANY) for y in itr if is(y,x) return true end end return false end is_local(sv::StaticVarInfo, s::Symbol) = contains_is(sv.vars, s) is_closed(sv::StaticVarInfo, s::Symbol) = haskey(sv.cenv, s) is_global(sv::StaticVarInfo, s::Symbol) = !is_local(sv,s) && !is_closed(sv,s) && !is_static_parameter(sv,s) function _iisconst(s::Symbol) m = (inference_stack::CallStack).mod isdefined(m,s) && (ccall(:jl_is_const, Int32, (Any, Any), m, s) != 0) end _ieval(x::ANY) = eval((inference_stack::CallStack).mod, x) _iisdefined(x::ANY) = isdefined((inference_stack::CallStack).mod, x) _iisconst(s::SymbolNode) = _iisconst(s.name) _iisconst(s::TopNode) = isconst(_basemod(), s.name) _iisconst(x::Expr) = false _iisconst(x::ANY) = true function _basemod() m = (inference_stack::CallStack).mod if m === Core || m === Base return m end return Main.Base end cmp_tfunc = (x,y)->Bool isType(t::ANY) = isa(t,DataType) && is((t::DataType).name,Type.name) isvarargtype(t::ANY) = isa(t,DataType)&&is((t::DataType).name,Vararg.name) const t_func = ObjectIdDict() #t_func[tuple] = (0, Inf, (args...)->limit_tuple_depth(args)) t_func[throw] = (1, 1, x->None) t_func[box] = (2, 2, (t,v)->(isType(t) ? t.parameters[1] : Any)) t_func[eq_int] = (2, 2, cmp_tfunc) t_func[ne_int] = (2, 2, cmp_tfunc) t_func[slt_int] = (2, 2, cmp_tfunc) t_func[ult_int] = (2, 2, cmp_tfunc) t_func[sle_int] = (2, 2, cmp_tfunc) t_func[ule_int] = (2, 2, cmp_tfunc) t_func[eq_float] = (2, 2, cmp_tfunc) t_func[ne_float] = (2, 2, cmp_tfunc) t_func[lt_float] = (2, 2, cmp_tfunc) t_func[le_float] = (2, 2, cmp_tfunc) t_func[eqfsi64] = (2, 2, cmp_tfunc) t_func[eqfui64] = (2, 2, cmp_tfunc) t_func[ltfsi64] = (2, 2, cmp_tfunc) t_func[ltfui64] = (2, 2, cmp_tfunc) t_func[lefsi64] = (2, 2, cmp_tfunc) t_func[lefui64] = (2, 2, cmp_tfunc) t_func[ltsif64] = (2, 2, cmp_tfunc) t_func[ltuif64] = (2, 2, cmp_tfunc) t_func[lesif64] = (2, 2, cmp_tfunc) t_func[leuif64] = (2, 2, cmp_tfunc) t_func[fpiseq] = (2, 2, cmp_tfunc) t_func[fpislt] = (2, 2, cmp_tfunc) t_func[nan_dom_err] = (2, 2, (a, b)->a) t_func[eval(Core.Intrinsics,:ccall)] = (3, Inf, (fptr, rt, at, a...)->(is(rt,Type{Void}) ? Nothing : isType(rt) ? rt.parameters[1] : Any)) t_func[eval(Core.Intrinsics,:cglobal)] = (1, 2, (fptr, t...)->(isempty(t) ? Ptr{Void} : isType(t[1]) ? Ptr{t[1].parameters[1]} : Ptr)) t_func[eval(Core.Intrinsics,:select_value)] = # TODO: return None if cnd is definitely not a Bool (3, 3, (cnd, x, y)->Union(x,y)) t_func[is] = (2, 2, cmp_tfunc) t_func[issubtype] = (2, 2, cmp_tfunc) t_func[isa] = (2, 2, cmp_tfunc) t_func[isdefined] = (1, 2, (args...)->Bool) t_func[Union] = (0, Inf, (args...)->(if all(isType,args) Type{Union(map(t->t.parameters[1],args)...)} else Type end)) t_func[method_exists] = (2, 2, cmp_tfunc) t_func[applicable] = (1, Inf, (f, args...)->Bool) t_func[tuplelen] = (1, 1, x->Int) t_func[arraylen] = (1, 1, x->Int) #t_func[arrayref] = (2,Inf,(a,i...)->(isa(a,DataType) && a<:Array ? # a.parameters[1] : Any)) #t_func[arrayset] = (3, Inf, (a,v,i...)->a) #arraysize_tfunc(a, d) = Int arraysize_tfunc = function (a, d...) if !is(d,()) return Int end if isa(a,DataType) && a<:Array N = a.parameters[2] return isa(N,Int) ? NTuple{N,Int} : (Int...) else return (Int...) end end t_func[arraysize] = (1, 2, arraysize_tfunc) t_func[pointerref] = (2,2,(a,i)->(isa(a,DataType) && a<:Ptr ? a.parameters[1] : Any)) t_func[pointerset] = (3, 3, (a,v,i)->a) function static_convert(to::ANY, from::ANY) if !isa(to,Tuple) || !isa(from,Tuple) if isa(to,TypeVar) return to end if from <: to return from end return typeintersect(from,to) end if is(to,Tuple) return from end pl = length(to)::Int; cl = length(from)::Int pseq = false result = Array(Any, cl) local pe # used across iterations for i=1:cl ce = from[i] if pseq elseif i <= pl pe = to[i] if isvarargtype(pe) pe = pe.parameters[1] pseq = true elseif isa(pe,TypeVar) && isvarargtype(pe.ub) pe = pe.ub.parameters[1] pseq = true end else return None end # tuple conversion calls convert recursively if isvarargtype(ce) R = abstract_call_gf(convert, (), (Type{pe}, ce.parameters[1]), ()) #R = static_convert(pe, ce.parameters[1]) isType(R) && (R = R.parameters[1]) result[i] = Vararg{R} else R = abstract_call_gf(convert, (), (Type{pe}, ce), ()) #R = static_convert(pe, ce) isType(R) && (R = R.parameters[1]) result[i] = R end end a2t(result) end t_func[convert_default] = (3, 3, (t,x,f)->(isType(t) ? static_convert(t.parameters[1],x) : Any)) t_func[convert_tuple] = (3, 3, (t,x,f)->(if isa(t,Tuple) && all(isType,t) t = Type{map(t->t.parameters[1],t)} end; isType(t) ? static_convert(t.parameters[1],x) : Any)) const typeof_tfunc = function (t) if isType(t) t = t.parameters[1] if isa(t,TypeVar) Type else Type{typeof(t)} end elseif isvarargtype(t) Vararg{typeof_tfunc(t.parameters[1])} elseif isa(t,DataType) if isleaftype(t) Type{t} else Type{TypeVar(:_,t)} end elseif isa(t,UnionType) Union(map(typeof_tfunc, t.types)...) elseif isa(t,Tuple) map(typeof_tfunc, t) elseif isa(t,TypeVar) Type{t} else Type end end t_func[typeof] = (1, 1, typeof_tfunc) # involving constants: typeassert, tupleref, getfield, fieldtype, apply_type # therefore they get their arguments unevaluated t_func[typeassert] = (2, 2, (A, v, t)->(isType(t) ? typeintersect(v,t.parameters[1]) : isa(t,Tuple) && all(isType,t) ? typeintersect(v,map(t->t.parameters[1],t)) : Any)) const tupleref_tfunc = function (A, t, i) if is(t,()) return None end if isa(t,DataType) && is(t.name,NTuple.name) return t.parameters[2] end if !isa(t,Tuple) return Any end n = length(t) last = tupleref(t,n) vararg = isvarargtype(last) if isa(A[2],Integer) # index is a constant i = A[2] if i > n if vararg return last.parameters[1] else return None end elseif i == n && vararg return last.parameters[1] else return tupleref(t,i) end else # index unknown, could be anything from the tuple if vararg types = tuple(t[1:(n-1)]..., last.parameters[1]) else types = t end return reduce(tmerge, None, types) end end t_func[tupleref] = (2, 2, tupleref_tfunc) const getfield_tfunc = function (A, s, name) if isType(s) s = typeof(s.parameters[1]) if s === TypeVar return Any end end if !isa(s,DataType) || s.abstract return Any end if isa(A[2],QuoteNode) && isa(A[2].value,Symbol) fld = A[2].value A1 = A[1] if isa(A1,Module) && isdefined(A1,fld) && isconst(A1, fld) return abstract_eval_constant(eval(A1,fld)) end if s === Module return Top end for i=1:length(s.names) if is(s.names[i],fld) return s.types[i] end end return None else return reduce(tmerge, None, s.types)#Union(s.types...) end end t_func[getfield] = (2, 2, getfield_tfunc) t_func[setfield] = (3, 3, (o, f, v)->v) const fieldtype_tfunc = function (A, s, name) if !isa(s,DataType) return Type end t = getfield_tfunc(A, s, name) if is(t,None) return t end Type{t} end t_func[fieldtype] = (2, 2, fieldtype_tfunc) t_func[Box] = (1, 1, (a,)->Box) valid_tparam(x::ANY) = isa(x,Int) || isa(x,Symbol) || isa(x,Bool) # TODO: handle e.g. apply_type(T, R::Union(Type{Int32},Type{Float64})) const apply_type_tfunc = function (A, args...) if !isType(args[1]) return Any end headtype = args[1].parameters[1] if isa(headtype,UnionType) || isa(headtype,Tuple) || isa(headtype,TypeVar) return args[1] end tparams = () uncertain = false lA = length(A) for i=2:max(lA,length(args)) ai = args[i] if isType(ai) tparams = tuple(tparams..., ai.parameters[1]) elseif isa(ai,Tuple) && all(isType,ai) tparams = tuple(tparams..., map(t->t.parameters[1], ai)) elseif i<=lA && (isa(A[i],Int) || isa(A[i],Bool)) tparams = tuple(tparams..., A[i]) elseif i<=lA && isa(A[i],QuoteNode) && valid_tparam(A[i].value) tparams = tuple(tparams..., A[i].value) else if i<=lA && isa(A[i],Symbol) && isa(inference_stack,CallStack) sp = inference_stack.sv.sp s = A[i] found = false for j=1:2:length(sp) if is(sp[j].name,s) # static parameter val = sp[j+1] if valid_tparam(val) tparams = tuple(tparams..., val) found = true break end end end if found continue end end if i-1 > length(headtype.parameters) # too many parameters for type return None end uncertain = true tparams = tuple(tparams..., headtype.parameters[i-1]) end end local appl # good, all arguments understood try appl = apply_type(headtype, tparams...) catch # type instantiation might fail if one of the type parameters # doesn't match, which could happen if a type estimate is too coarse appl = args[1] uncertain = true end if type_too_complex(appl,0) return Type{TypeVar(:_,headtype)} end uncertain ? Type{TypeVar(:_,appl)} : Type{appl} end t_func[apply_type] = (1, Inf, apply_type_tfunc) # other: apply function builtin_tfunction(f::ANY, args::ANY, argtypes::ANY) if is(f,tuple) return limit_tuple_depth(argtypes) elseif is(f,arrayset) if length(argtypes) < 3 return None end return argtypes[1] elseif is(f,arrayref) if length(argtypes) < 2 return None end a = argtypes[1] return (isa(a,DataType) && a<:Array ? a.parameters[1] : Any) elseif is(f,Expr) if length(argtypes) < 1 return None end return Expr end tf = get(t_func::ObjectIdDict, f, false) if is(tf,false) # struct constructor if isstructtype(f) return f end # unknown/unhandled builtin return Any end tf = tf::(Real, Real, Function) if !(tf[1] <= length(argtypes) <= tf[2]) # wrong # of args return None end if is(f,typeassert) || is(f,tupleref) || is(f,getfield) || is(f,apply_type) || is(f,fieldtype) # TODO: case of apply(), where we do not have the args return tf[3](args, argtypes...) end return tf[3](argtypes...) end function a2t(a::AbstractVector) n = length(a) if n==2 return (a[1],a[2]) end if n==1 return (a[1],) end if n==3 return (a[1],a[2],a[3]) end if n==0 return () end return tuple(a...) end function isconstantfunc(f::ANY, sv::StaticVarInfo) if isa(f,TopNode) m = _basemod() return isconst(m, f.name) && isdefined(m, f.name) && f end if isa(f,GetfieldNode) && isa(f.value,Module) M = f.value; s = f.name return isdefined(M,s) && isconst(M,s) && f end if isa(f,Expr) && (is(f.head,:call) || is(f.head,:call1)) if length(f.args) == 3 && isa(f.args[1], TopNode) && is(f.args[1].name,:getfield) && isa(f.args[3],QuoteNode) s = f.args[3].value if isa(f.args[2],Module) M = f.args[2] else M = isconstantfunc(f.args[2], sv) if M === false return false end M = _ieval(M) if !isa(M,Module) return false end end return isdefined(M,s) && isconst(M,s) && f end end if isa(f,QuoteNode) && isa(f.value, Function) return f.value end if isa(f,SymbolNode) f = f.name end return isa(f,Symbol) && is_global(sv, f) && _iisconst(f) && f end const isconstantref = isconstantfunc isvatuple(t::Tuple) = (n = length(t); n > 0 && isvarargtype(t[n])) const limit_tuple_depth = t->limit_tuple_depth_(t,0) const limit_tuple_depth_ = function (t,d::Int) if isa(t,UnionType) # also limit within Union types. # may have to recur into other stuff in the future too. return Union(limit_tuple_depth_(t.types,d)...) end if !isa(t,Tuple) return t end if d > MAX_TUPLE_DEPTH return Tuple end map(x->limit_tuple_depth_(x,d+1), t) end limit_tuple_type = t -> limit_tuple_type_n(t, MAX_TUPLETYPE_LEN) const limit_tuple_type_n = function (t::Tuple, lim::Int) n = length(t) if n > lim last = t[n] if isvarargtype(last) last = last.parameters[1] end tail = tuple(t[lim:(n-1)]..., last) tail = typeintersect(reduce(tmerge, None, tail), Any) return tuple(t[1:(lim-1)]..., Vararg{tail}) end return t end function abstract_call_gf(f, fargs, argtypes, e) if length(argtypes)>1 && isa(argtypes[1],Tuple) && argtypes[2]===Int # allow tuple indexing functions to take advantage of constant # index arguments. if f === Main.Base.getindex e.head = :call1 return tupleref_tfunc(fargs, argtypes[1], argtypes[2]) elseif f === Main.Base.next e.head = :call1 return (tupleref_tfunc(fargs, argtypes[1], argtypes[2]), Int) elseif f === Main.Base.indexed_next e.head = :call1 return (tupleref_tfunc(fargs, argtypes[1], argtypes[2]), Int) end end if (isdefined(Main.Base,:promote_type) && f === Main.Base.promote_type) || (isdefined(Main.Base,:typejoin) && f === Main.Base.typejoin) la = length(argtypes) c = cell(la) for i = 1:la t = argtypes[i] if isType(t) && !isa(t.parameters[1],TypeVar) c[i] = t.parameters[1] else return Type end end if f === Main.Base.promote_type try RT = Type{f(c...)} e.head = :call1 return RT catch end else e.head = :call1 return Type{f(c...)} end end # don't consider more than N methods. this trades off between # compiler performance and generated code performance. # typically, considering many methods means spending lots of time # obtaining poor type information. # It is important for N to be >= the number of methods in the error() # function, so we can still know that error() is always None. # here I picked 4. argtypes = limit_tuple_type(argtypes) applicable = _methods(f, argtypes, 4) rettype = None if is(applicable,false) # this means too many methods matched if isa(e,Expr) e.head = :call end return Any end x::Array{Any,1} = applicable if isempty(x) # no methods match return None end if isa(e,Expr) if length(x)==1 # method match is unique; mark it e.head = :call1 else e.head = :call end end for (m::Tuple) in x linfo = m[3].func.code sig = m[1] lsig = length(m[3].sig) # limit argument type tuple based on size of definition signature. # for example, given function f(T, Any...), limit to 3 arguments # instead of the default (MAX_TUPLETYPE_LEN) ls = length(sig) if ls > lsig+1 fst = sig[lsig+1] allsame = true # allow specializing on longer arglists if all the trailing # arguments are the same, since there is no exponential # blowup in this case. for i = lsig+2:ls if sig[i] != fst allsame = false break end end if !allsame sig = limit_tuple_type_n(sig, lsig+1) end end #print(m,"\n") (_tree,rt) = typeinf(linfo, sig, m[2], linfo) rettype = tmerge(rettype, rt) if is(rettype,Any) break end end # if rettype is None we've found a method not found error #print("=> ", rettype, "\n") return rettype end function invoke_tfunc(f, types, argtypes) argtypes = typeintersect(types,limit_tuple_type(argtypes)) if is(argtypes,None) return None end applicable = _methods(f, types, -1) if isempty(applicable) return Any end for (m::Tuple) in applicable linfo = m[3].func.code if typeseq(m[1],types) tvars = m[2][1:2:end] (ti, env) = ccall(:jl_match_method, Any, (Any,Any,Any), argtypes, m[1], tvars)::(Any,Any) (_tree,rt) = typeinf(linfo, ti, env, linfo) return rt end end return Any end function abstract_call(f, fargs, argtypes, vtypes, sv::StaticVarInfo, e) if is(f,apply) && length(fargs)>0 if isType(argtypes[1]) && isleaftype(argtypes[1].parameters[1]) af = argtypes[1].parameters[1] _methods(af,(),0) else af = isconstantfunc(fargs[1], sv) end if !is(af,false) aargtypes = argtypes[2:] if all(x->isa(x,Tuple), aargtypes) && !any(isvatuple, aargtypes[1:(length(aargtypes)-1)]) e.head = :call1 # apply with known func with known tuple types # can be collapsed to a call to the applied func at = length(aargtypes) > 0 ? limit_tuple_type(apply(tuple,aargtypes...)) : () return abstract_call(_ieval(af), (), at, vtypes, sv, ()) end af = _ieval(af) if is(af,tuple) && length(fargs)==2 # tuple(xs...) aat = aargtypes[1] if aat <: AbstractArray # tuple(array...) # TODO: > 1 array of the same type tn = AbstractArray.name while isa(aat, DataType) if is(aat.name, tn) et = aat.parameters[1] if !isa(et,TypeVar) return (et...) end end if is(aat, Any) break end aat = aat.super end end return Tuple end end end if isgeneric(f) return abstract_call_gf(f, fargs, argtypes, e) end if is(f,invoke) && length(fargs)>1 af = isconstantfunc(fargs[1], sv) if !is(af,false) && (af=_ieval(af);isgeneric(af)) sig = argtypes[2] if isa(sig,Tuple) && all(isType, sig) sig = map(t->t.parameters[1], sig) return invoke_tfunc(af, sig, argtypes[3:]) end end end if !is(f,apply) && isa(e,Expr) && (isa(f,Function) || isa(f,IntrinsicFunction)) e.head = :call1 end if is(f,getfield) val = isconstantref(e, sv) if !is(val,false) return abstract_eval_constant(_ieval(val)) end end if is(f,kwcall) if length(argtypes) < 3 return None end if length(fargs) < 2 return Any end ff = isconstantfunc(fargs[1], sv) if !(ff===false) ff = _ieval(ff) if isgeneric(ff) && isdefined(ff.env,:kwsorter) # use the fact that kwcall(...) calls ff.env.kwsorter kwcount = fargs[2] posargt = argtypes[(4+2*kwcount):end] return abstract_call_gf(ff.env.kwsorter, (), tuple(Array{Any,1}, posargt...), e) end end return Any end rt = builtin_tfunction(f, fargs, argtypes) #print("=> ", rt, "\n") return rt end function abstract_eval_arg(a::ANY, vtypes::ANY, sv::StaticVarInfo) t = abstract_eval(a, vtypes, sv) if isa(a,Symbol) || isa(a,SymbolNode) t = typeintersect(t,Any) # remove Undef end return t end function abstract_eval_call(e, vtypes, sv::StaticVarInfo) fargs = e.args[2:] argtypes = tuple([abstract_eval_arg(a, vtypes, sv) for a in fargs]...) if any(x->is(x,None), argtypes) return None end called = e.args[1] func = isconstantfunc(called, sv) if is(func,false) if isa(called, LambdaStaticData) # called lambda expression (let) (_, result) = typeinf(called, argtypes, called.sparams, called, true) return result end ft = abstract_eval(called, vtypes, sv) if isType(ft) st = ft.parameters[1] if isa(st,TypeVar) st = st.ub end if isa(st,DataType) _methods(st,(),0) if isgeneric(st) && isleaftype(st) return abstract_call_gf(st, fargs, argtypes, e) end # struct constructor return st end end return Any end #print("call ", e.args[1], argtypes, " ") f = _ieval(func) return abstract_call(f, fargs, argtypes, vtypes, sv, e) end function abstract_eval(e::ANY, vtypes, sv::StaticVarInfo) if isa(e,QuoteNode) return typeof(e.value) elseif isa(e,TopNode) return abstract_eval_global(_basemod(), e.name) elseif isa(e,Symbol) return abstract_eval_symbol(e, vtypes, sv) elseif isa(e,SymbolNode) return abstract_eval_symbol(e.name, vtypes, sv) elseif isa(e,LambdaStaticData) return Function elseif isa(e,GetfieldNode) return abstract_eval_global(e.value::Module, e.name) end if !isa(e,Expr) return abstract_eval_constant(e) end e = e::Expr # handle: # call null new & static_typeof if is(e.head,:call) || is(e.head,:call1) t = abstract_eval_call(e, vtypes, sv) elseif is(e.head,:null) t = Nothing elseif is(e.head,:new) t = abstract_eval(e.args[1], vtypes, sv) if isType(t) t = t.parameters[1] else t = Any end for i = 2:length(e.args) abstract_eval(e.args[i], vtypes, sv) end elseif is(e.head,:&) abstract_eval(e.args[1], vtypes, sv) t = Any elseif is(e.head,:static_typeof) t0 = abstract_eval(e.args[1], vtypes, sv) # intersect with Any to remove Undef t = typeintersect(t0, Any) if is(t,None) && Undef<:t0 # the first time we see this statement the variable will probably # be Undef; return None so this doesn't contribute to the type # we eventually pick. elseif is(t,None) t = Type{None} elseif isleaftype(t) t = Type{t} elseif isleaftype(inference_stack.types) if isa(t,TypeVar) t = Type{t.ub} else t = Type{t} end else # if there is any type uncertainty in the arguments, we are # effectively predicting what static_typeof will say when # the function is compiled with actual arguments. in that case # abstract types yield Type{<:T} instead of Type{T}. # this doesn't really model the situation perfectly, but # "isleaftype(inference_stack.types)" should be good enough. if isa(t,TypeVar) t = Type{t} else t = Type{TypeVar(:_,t)} end end elseif is(e.head,:method) t = Function elseif is(e.head,:copyast) t = abstract_eval(e.args[1], vtypes, sv) else t = Any end e.typ = t return t end const Type_Array = Type{Array} function abstract_eval_constant(x::ANY) if isa(x,Type) if is(x,Array) return Type_Array end return Type{x} end #if isa(x,Tuple) && all(e->isa(e,Type), x) # return Type{x} #end return typeof(x) end # Undef is the static type of a value location (e.g. variable) that is # undefined. The corresponding run-time type is None, since accessing an # undefined location is an error. A non-lvalue expression cannot have # type Undef, only None. # typealias Top Union(Any,Undef) abstract_eval_global(s::Symbol) = abstract_eval_global((inference_stack::CallStack).mod, s) function abstract_eval_global(M, s::Symbol) if isconst(M,s) return abstract_eval_constant(eval(M,s)) end if !isdefined(M,s) return Top end # TODO: change to Undef if there's a way to clear variables return Any end function abstract_eval_symbol(s::Symbol, vtypes, sv::StaticVarInfo) if haskey(sv.cenv,s) # consider closed vars to always have their propagated (declared) type return sv.cenv[s] end t = is(vtypes,()) ? NF : get(vtypes,s,NF) if is(t,NF) sp = sv.sp for i=1:2:length(sp) if is(sp[i].name,s) # static parameter val = sp[i+1] 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 # global return abstract_eval_global(s) end return t end typealias VarTable ObjectIdDict type StateUpdate var::Symbol 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 abstract_interpret(e, vtypes, sv::StaticVarInfo) = vtypes function abstract_interpret(e::Expr, vtypes, sv::StaticVarInfo) # handle assignment if is(e.head,:(=)) t = abstract_eval(e.args[2], vtypes, sv) lhs = e.args[1] if isa(lhs,SymbolNode) lhs = lhs.name end assert(isa(lhs,Symbol)) return StateUpdate(lhs, t, vtypes) elseif is(e.head,:call) || is(e.head,:call1) abstract_eval(e, vtypes, sv) elseif is(e.head,:gotoifnot) abstract_eval(e.args[1], vtypes, sv) elseif is(e.head,:method) fname = e.args[1] if isa(fname,Symbol) return StateUpdate(fname, Function, vtypes) end end return vtypes end tchanged(n::ANY, o::ANY) = is(o,NF) || (!is(n,NF) && !(n <: o)) function stchanged(new::Union(StateUpdate,VarTable), old, vars) if is(old,()) return true end for i = 1:length(vars) v = vars[i] if tchanged(new[v], get(old,v,NF)) return true end end return false end function type_too_complex(t::ANY, d) if d > MAX_TYPE_DEPTH return true end if isa(t,UnionType) p = t.types elseif isa(t,DataType) p = t.parameters elseif isa(t,Tuple) p = t elseif isa(t,TypeVar) return type_too_complex(t.lb,d+1) || type_too_complex(t.ub,d+1) else return false end for x in (p::Tuple) if type_too_complex(x, d+1) return true end end return false end function tmerge(typea::ANY, typeb::ANY) if is(typea,NF) return typeb end if is(typeb,NF) return typea end if typea <: typeb return typeb end if typeb <: typea return typea end u = Union(typea, typeb) if length(u.types) > MAX_TYPEUNION_LEN || type_too_complex(u, 0) # don't let type unions get too big # TODO: something smarter, like a common supertype return Undef<:u ? Top : Any end return u end function stupdate(state, changes::Union(StateUpdate,VarTable), vars) if is(state,()) state = ObjectIdDict() end for i = 1:length(vars) v = vars[i] newtype = changes[v] oldtype = get(state::ObjectIdDict,v,NF) if tchanged(newtype, oldtype) state[v] = tmerge(oldtype, newtype) end end state end function findlabel(body, l) for i=1:length(body) b = body[i] if isa(b,LabelNode) && b.label==l return i end end error("label ",l," not found") end f_argnames(ast) = map(x->(isa(x,Expr) ? x.args[1] : x), ast.args[1]::Array{Any,1}) is_rest_arg(arg::ANY) = (ccall(:jl_is_rest_arg,Int32,(Any,), arg) != 0) # function typeinf_task(caller) # result = () # while true # (caller, args) = yieldto(caller, result) # result = typeinf_ext_(args...) # end # end #Inference_Task = Task(typeinf_task, 2097152) #yieldto(Inference_Task, current_task()) #function typeinf_ext(linfo, atypes, sparams, cop) #C = current_task() #args = (linfo, atypes, sparams, cop) #if is(C, Inference_Task) # return typeinf_ext_(args...) #end #return yieldto(Inference_Task, C, args) #end function typeinf_ext(linfo, atypes::ANY, sparams::ANY, def) global inference_stack last = inference_stack inference_stack = EmptyCallStack() result = typeinf(linfo, atypes, sparams, def, true) inference_stack = last return result end typeinf(linfo,atypes::ANY,sparams::ANY) = typeinf(linfo,atypes,sparams,linfo,true) typeinf(linfo,atypes::ANY,sparams::ANY,def) = typeinf(linfo,atypes,sparams,def,true) CYCLE_ID = 1 # def is the original unspecialized version of a method. we aggregate all # saved type inference data there. function typeinf(linfo::LambdaStaticData,atypes::Tuple,sparams::Tuple, def, cop) #dbg = #dotrace = true local ast::Expr, tfunc_idx curtype = None redo = false # check cached t-functions tf = def.tfunc if !is(tf,()) tfarr = tf::Array{Any,1} for i = 1:3:length(tfarr) if typeseq(tfarr[i],atypes) code = tfarr[i+1] if tfarr[i+2] redo = true tfunc_idx = i+1 curtype = code break end curtype = ccall(:jl_ast_rettype, Any, (Any,Any), def, code) return (code, curtype) end end end ast0 = def.ast #if dbg # print("typeinf ", linfo.name, " ", object_id(ast0), "\n") #end #print("typeinf ", linfo.name, " ", atypes, " ", linfo.file,":",linfo.line,"\n") # if isdefined(:STDOUT) # write(STDOUT, "typeinf ") # write(STDOUT, string(linfo.name)) # write(STDOUT, string(atypes)) # write(STDOUT, '\n') # end #print("typeinf ", ast0, " ", sparams, " ", atypes, "\n") global inference_stack, CYCLE_ID # check for recursion f = inference_stack while !isa(f,EmptyCallStack) if is(f.ast,ast0) && typeseq(f.types, atypes) # return best guess so far (f::CallStack).recurred = true (f::CallStack).cycleid = CYCLE_ID r = inference_stack while !is(r, f) # mark all frames that are part of the cycle r.recurred = true r.cycleid = CYCLE_ID r = r.prev end CYCLE_ID += 1 #print("*==> ", f.result,"\n") return ((),f.result) end f = f.prev end #if dbg print("typeinf ", linfo.name, " ", atypes, "\n") end if cop sparams = tuple(sparams..., linfo.sparams...) ast = ccall(:jl_prepare_ast, Any, (Any,Any), linfo, sparams)::Expr else ast = linfo.ast end args = f_argnames(ast) la = length(args) assert(is(ast.head,:lambda)) locals = (ast.args[2][1])::Array{Any,1} vars = [args, locals] body = (ast.args[3].args)::Array{Any,1} n = length(body) # our stack frame frame = CallStack(ast0, linfo.module, atypes, inference_stack) inference_stack = frame frame.result = curtype rec = false toprec = false s = { () for i=1:n } recpts = IntSet() # statements that depend recursively on our value W = IntSet() # initial set of pc push!(W,1) # initial types s[1] = ObjectIdDict() for v in vars s[1][v] = Undef end if la > 0 lastarg = ast.args[1][la] if is_rest_arg(lastarg) if atypes === Tuple if la > 1 atypes = tuple(NTuple{la-1,Any}..., Tuple[1]) end s[1][args[la]] = Tuple else s[1][args[la]] = limit_tuple_depth(atypes[la:]) end la -= 1 else if atypes === Tuple atypes = tuple(NTuple{la,Any}..., Tuple[1]) end end end for i=1:la s[1][args[i]] = atypes[i] end # types of closed vars cenv = ObjectIdDict() for vi = ((ast.args[2][3])::Array{Any,1}) vi::Array{Any,1} vname = vi[1] vtype = vi[2] cenv[vname] = vtype s[1][vname] = vtype end for vi = ((ast.args[2][2])::Array{Any,1}) vi::Array{Any,1} if (vi[3]&4)!=0 # variables assigned by inner functions are treated like # closed variables; we only use the declared type vname = vi[1] vtype = vi[2] cenv[vname] = vtype s[1][vname] = vtype end end sv = StaticVarInfo(sparams, cenv, vars) frame.sv = sv # exception handlers cur_hand = () handler_at = { () for i=1:n } while !isempty(W) pc = first(W) while true #print(pc,": ",s[pc],"\n") delete!(W, pc) if is(handler_at[pc],()) handler_at[pc] = cur_hand else cur_hand = handler_at[pc] end stmt = body[pc] changes = abstract_interpret(stmt, s[pc], sv) if frame.recurred rec = true if !(isa(frame.prev,CallStack) && frame.prev.cycleid == frame.cycleid) toprec = true end push!(recpts, pc) #if dbg # show(pc); print(" recurred\n") #end frame.recurred = false end if !is(cur_hand,()) # propagate type info to exception handler l = cur_hand[1]::Int if stchanged(changes, s[l], vars) push!(W, l) s[l] = stupdate(s[l], changes, vars) end end pc´ = pc+1 if isa(stmt,GotoNode) pc´ = findlabel(body,stmt.label) elseif isa(stmt,Expr) hd = stmt.head if is(hd,:gotoifnot) condexpr = stmt.args[1] l = findlabel(body,stmt.args[2]) # constant conditions if is(condexpr,true) elseif is(condexpr,false) pc´ = l else # general case handler_at[l] = cur_hand if stchanged(changes, s[l], vars) push!(W, l) s[l] = stupdate(s[l], changes, vars) end end elseif is(hd,:type_goto) l = findlabel(body,stmt.args[1]) for i = 2:length(stmt.args) var = stmt.args[i] if isa(var,SymbolNode) var = var.name end # type_goto provides a special update rule for the # listed vars: it feeds types directly to the # target statement as long as they are *different*, # not !issubtype like usual. this is because we want # the specific type inferred at the point of the # type_goto, not just any type containing it. # Otherwise "None" doesn't work; see issue #3821 vt = changes[var] ot = get(s[l],var,NF) if ot === NF || !typeseq(vt,ot) # l+1 is the statement after the label, where the # static_typeof occurs. push!(W, l+1) s[l+1][var] = vt end end elseif is(hd,:return) pc´ = n+1 rt = abstract_eval_arg(stmt.args[1], s[pc], sv) if frame.recurred rec = true if !(isa(frame.prev,CallStack) && frame.prev.cycleid == frame.cycleid) toprec = true end push!(recpts, pc) #if dbg # show(pc); print(" recurred\n") #end frame.recurred = false end #if dbg # print("at "); show(pc) # print(" result is "); show(frame.result) # print(" and rt is "); show(rt) # print("\n") #end if tchanged(rt, frame.result) frame.result = tmerge(frame.result, rt) # revisit states that recursively depend on this for r in recpts #if dbg # print("will revisit ") # show(r) # print("\n") #end push!(W,r) end end elseif is(hd,:enter) l = findlabel(body,stmt.args[1]::Int) cur_hand = (l,cur_hand) handler_at[l] = cur_hand elseif is(hd,:leave) for i=1:((stmt.args[1])::Int) cur_hand = cur_hand[2] end end end if pc´<=n && (handler_at[pc´] = cur_hand; true) && stchanged(changes, s[pc´], vars) s[pc´] = stupdate(s[pc´], changes, vars) pc = pc´ else break end end end #print("\n",ast,"\n") #if dbg print("==> ", frame.result,"\n") end if (toprec && typeseq(curtype, frame.result)) || !isa(frame.prev,CallStack) rec = false end fulltree = type_annotate(ast, s, sv, frame.result, args) if !rec fulltree.args[3] = inlining_pass(fulltree.args[3], sv, fulltree)[1] # inlining can add variables sv.vars = append_any(f_argnames(fulltree), fulltree.args[2][1]) tuple_elim_pass(fulltree) linfo.inferred = true fulltree = ccall(:jl_compress_ast, Any, (Any,Any), def, fulltree) end if !redo if is(def.tfunc,()) def.tfunc = {} end push!(def.tfunc::Array{Any,1}, atypes) # in the "rec" state this tree will not be used again, so store # just the return type in place of it. push!(def.tfunc::Array{Any,1}, rec ? frame.result : fulltree) push!(def.tfunc::Array{Any,1}, rec) else def.tfunc[tfunc_idx] = rec ? frame.result : fulltree def.tfunc[tfunc_idx+1] = rec end inference_stack = (inference_stack::CallStack).prev return (fulltree, frame.result) end function record_var_type(e::Symbol, t::ANY, decls) otherTy = get(decls::ObjectIdDict, e, false) # keep track of whether a variable is always the same type if !is(otherTy,false) if !is(otherTy, t) decls[e] = Any end else decls[e] = t end end function eval_annotate(e::ANY, vtypes::ANY, sv::StaticVarInfo, decls, clo) if isa(e, Symbol) e = e::Symbol if !is_local(sv, e) && !is_closed(sv, e) # can get types of globals and static params from the environment return e end t = abstract_eval(e, vtypes, sv) 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) if !(curtype <: t) || typeseq(curtype, t) record_var_type(e.name, t, decls) e.typ = t end return e end if isa(e, LambdaStaticData) push!(clo, e) return e end if !isa(e,Expr) return e end e = e::Expr head = e.head if is(head,:static_typeof) || is(head,:line) || is(head,:const) return e #elseif is(head,:gotoifnot) || is(head,:return) # e.typ = Any elseif is(head,:(=)) # e.typ = Any s = e.args[1] # assignment LHS not subject to all-same-type variable checking, # but the type of the RHS counts as one of its types. if isa(s,SymbolNode) # we don't use types on assignment LHS #s.typ = abstract_eval(s.name, vtypes, sv) s = s.name else #e.args[1] = SymbolNode(s, abstract_eval(s, vtypes, sv)) end e.args[2] = eval_annotate(e.args[2], vtypes, sv, decls, clo) # TODO: if this def does not reach any uses, maybe don't do this rhstype = exprtype(e.args[2]) if !is(rhstype,None) record_var_type(s, rhstype, decls) end return e end i0 = is(head,:method) ? 2 : 1 for i=i0:length(e.args) subex = e.args[i] if !(isa(subex,Number) || isa(subex,String)) e.args[i] = eval_annotate(subex, vtypes, sv, decls, clo) end end if (head === :call || head === :call1) && isa(e.args[1],LambdaStaticData) called = e.args[1] fargs = e.args[2:] argtypes = tuple([abstract_eval_arg(a, vtypes, sv) for a in fargs]...) # recur inside inner functions once we have all types tr,ty = typeinf(called, argtypes, called.sparams, called, false) called.ast = tr end e end # annotate types of all symbols in AST function type_annotate(ast::Expr, states::Array{Any,1}, sv::ANY, rettype::ANY, args) decls = ObjectIdDict() # initialize decls with argument types for arg in args decls[arg] = states[1][arg] end closures = {} body = ast.args[3].args::Array{Any,1} for i=1:length(body) body[i] = eval_annotate(body[i], states[i], sv, decls, closures) end ast.args[3].typ = rettype # add declarations for variables that are always the same type for vi in ast.args[2][2]::Array{Any,1} if (vi[3]&4)==0 vi[2] = get(decls, vi[1], vi[2]) end end for vi in ast.args[2][3]::Array{Any,1} if (vi[3]&4)==0 vi[2] = get(decls, vi[1], vi[2]) end end for (li::LambdaStaticData) in closures if !li.inferred a = li.ast # pass on declarations of captured vars for vi in a.args[2][3]::Array{Any,1} if (vi[3]&4)==0 vi[2] = get(decls, vi[1], vi[2]) end end # NOTE: this is disabled, as it leads to inlining too early. # See issue #4688. We should wait until inner functions are called # to optimize them; this will be done by the method cache or # builtins.c:jl_trampoline. However if jl_trampoline is changed then # this code will need to be restored. #na = length(a.args[1]) #li.ast, _ = typeinf(li, ntuple(na+1, i->(i>na ? (Tuple)[1] : Any)), # li.sparams, li, false) end end ast end function sym_replace(e::ANY, from1, from2, to1, to2) if isa(e,Symbol) return _sym_repl(e::Symbol, from1, from2, to1, to2, e) end if isa(e,SymbolNode) return _sym_repl(e.name, from1, from2, to1, to2, e) end if !isa(e,Expr) return e end e = e::Expr if !is(e.head,:line) for i=1:length(e.args) e.args[i] = sym_replace(e.args[i], from1, from2, to1, to2) end end e end function _sym_repl(s::Symbol, 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 # return an expr to evaluate "from.sym" in module "to" function resolve_relative(sym, from, to, typ, orig) if is(from,to) return orig end const_from = (isconst(from,sym) && isdefined(from,sym)) const_to = (isconst(to,sym) && isdefined(to,sym)) if const_from if const_to && is(eval(from,sym), eval(to,sym)) return orig end m = _basemod() if is(from,m) || is(from,Core) return tn(sym) end end return GetfieldNode(from, sym, typ) end # annotate symbols with their original module for inlining function resolve_globals(e::ANY, from, to, env1, env2) if isa(e,Symbol) s = e::Symbol if contains_is(env1, s) || contains_is(env2, s) return s end return resolve_relative(s, from, to, Any, s) end if isa(e,SymbolNode) s = e::SymbolNode name = s.name if contains_is(env1, name) || contains_is(env2, name) return s end return resolve_relative(name, from, to, s.typ, s) end if !isa(e,Expr) return e end e = e::Expr if !is(e.head,:line) for i=1:length(e.args) subex = e.args[i] if !(isa(subex,Number) || isa(subex,String)) e.args[i] = resolve_globals(subex, from, to, env1, env2) end end end e end # count occurrences up to n+1 function occurs_more(e::ANY, pred, n) if isa(e,Expr) e = e::Expr c = 0 for a = e.args c += occurs_more(a, pred, n) if c>n return c end end return c end if pred(e) || (isa(e,SymbolNode) && pred(e.name)) return 1 end return 0 end function exprtype(x::ANY) if isa(x,Expr) return x.typ elseif isa(x,SymbolNode) return x.typ elseif isa(x,TopNode) return abstract_eval_global(_basemod(), x.name) elseif isa(x,Symbol) sv = inference_stack.sv if is_local(sv, x) return Any end return abstract_eval(x, (), sv) elseif isa(x,QuoteNode) return typeof(x.value) elseif isa(x,Type) return Type{x} elseif isa(x,LambdaStaticData) return Function elseif isa(x,GetfieldNode) return x.typ else return typeof(x) end end function without_linenums(a::Array{Any,1}) l = {} for x in a if (isa(x,Expr) && is(x.head,:line)) || isa(x,LineNumberNode) else push!(l, x) end end l end _pure_builtins = {getfield, tuple, tupleref, tuplelen, fieldtype} # detect some important side-effect-free calls function effect_free(e::ANY, sv) if isa(e,Symbol) || isa(e,SymbolNode) || isa(e,Number) || isa(e,String) || isa(e,TopNode) || isa(e,QuoteNode) return true end if isa(e,Expr) if e.head === :static_typeof return true end ea = e.args if e.head === :call || e.head === :call1 for a in ea if !effect_free(a,sv) return false end end if is_known_call(e, _pure_builtins, sv) return true end elseif e.head === :new for a in ea if !effect_free(a,sv) return false end end return true end end return false end # for now, only inline functions whose bodies are of the form "return " # where doesn't contain any argument more than once. # functions with closure environments or varargs are also excluded. # static parameters are ok if all the static parameter values are leaf types, # meaning they are fully known. function inlineable(f, e::Expr, sv, enclosing_ast) if !(isa(f,Function) || isstructtype(f) || isa(f,IntrinsicFunction)) return NF end argexprs = e.args[2:] atypes = tuple(map(exprtype, argexprs)...) if length(atypes) > MAX_TUPLETYPE_LEN atypes = limit_tuple_type(atypes) end if is(f, convert_default) && length(atypes)==3 # builtin case of convert. convert(T,x::S) => x, when S<:T if isType(atypes[1]) && isleaftype(atypes[1]) && atypes[2] <: atypes[1].parameters[1] # todo: if T expression has side effects??! return (e.args[3],()) end end if length(atypes)==2 && is(f,unbox) && isa(atypes[2],DataType) # remove redundant unbox return (e.args[3],()) end if isdefined(Main.Base,:isbits) && is(f,Main.Base.isbits) && length(atypes)==1 && isType(atypes[1]) && effect_free(argexprs[1],sv) && isleaftype(atypes[1].parameters[1]) return (isbits(atypes[1].parameters[1]),()) end # special-case inliners for known pure functions that compute types if isType(e.typ) if (is(f,apply_type) || is(f,fieldtype) || (isdefined(Main.Base,:typejoin) && is(f,Main.Base.typejoin)) || (isdefined(Main.Base,:promote_type) && is(f,Main.Base.promote_type))) && isleaftype(e.typ.parameters[1]) return (e.typ.parameters[1],()) end if is(f,Union) union = e.typ.parameters[1] if isa(union,UnionType) && all(isleaftype, (union::UnionType).types) return (union,()) end end end if isa(f,IntrinsicFunction) return NF end meth = _methods(f, atypes, 1) if meth === false || length(meth) != 1 return NF end meth = meth[1]::Tuple linfo = meth[3].func.code ## This code tries to limit the argument list length only when it is ## growing due to recursion. ## It might be helpful for some things, but turns out not to be ## necessary to get max performance from recursive varargs functions. # if length(atypes) > MAX_TUPLETYPE_LEN # # 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) # atypes = limit_tuple_type(atypes) # meth = _methods(f, atypes, 1) # if meth === false || length(meth) != 1 # return NF # end # meth = meth[1]::Tuple # linfo2 = meth[3].func.code # if linfo2 !== linfo # return NF # end # linfo = linfo2 # break # end # st = st.prev # end # end # when 1 method matches the inferred types, there is still a chance # of a no-method error at run time, unless the inferred types are a # subset of the method signature. if !(atypes <: meth[1]) return NF end if !isa(linfo,LambdaStaticData) || meth[3].func.env !== () return NF end sp = meth[2]::Tuple sp = tuple(sp..., linfo.sparams...) spvals = { sp[i] for i in 2:2:length(sp) } for i=1:length(spvals) if isa(spvals[i],TypeVar) return NF end if isa(spvals[i],Symbol) spvals[i] = qn(spvals[i]) end end (ast, ty) = typeinf(linfo, meth[1], meth[2], linfo) if is(ast,()) return NF end needcopy = true if !isa(ast,Expr) ast = ccall(:jl_uncompress_ast, Any, (Any,Any), linfo, ast) needcopy = false end ast = ast::Expr for vi in ast.args[2][2] if (vi[3]&1)!=0 # captures variables (TODO) return NF end end body = without_linenums(ast.args[3].args)::Array{Any,1} # see if body is only "return " if length(body) != 1 return NF end assert(isa(body[1],Expr)) assert(is(body[1].head,:return)) # check for vararg function args = f_argnames(ast) expr = body[1].args[1] na = length(args) if na>0 && is_rest_arg(ast.args[1][na]) vaname = args[na] len_argexprs = length(argexprs) valen = len_argexprs-na+1 if valen>0 && !occurs_outside_tupleref(expr, 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 expr = astcopy(expr); needcopy = false end replace_tupleref!(ast, Expr(:return, expr), vaname, newnames, sv, 1) args = vcat(args[1:na-1], newnames) else # construct tuple-forming expression for argument tail vararg = mk_tuplecall(argexprs[na:end]) argexprs = {argexprs[1:(na-1)]..., vararg} end end # avoid capture if the function has free variables with the same name # as our vars if occurs_more(expr, x->(isa(x,Symbol)&&!is_global(sv,x)&&!contains_is(args,x)), 0) > 0 return NF end stmts = {} # see if each argument occurs only once in the body expression for i=1:length(args) a = args[i] occ = occurs_more(expr, x->is(x,a), 1) if occ != 1 aei = argexprs[i]; aeitype = exprtype(aei) # ok for argument to occur more than once if the actual argument # is a symbol or constant if !effect_free(aei,sv) || (occ==0 && is(aeitype,None)) # introduce variable for this argument if occ > 1 vnew = unique_name(enclosing_ast) add_variable(enclosing_ast, vnew, aeitype) push!(stmts, Expr(:(=), vnew, aei)) argexprs[i] = aeitype===Any ? vnew : SymbolNode(vnew,aeitype) elseif !isType(aeitype) && !effect_free(aei,sv) push!(stmts, aei) end end end end # ok, substitute argument expressions for argument names in the body spnames = { sp[i].name for i=1:2:length(sp) } if needcopy; expr = astcopy(expr); end mfrom = linfo.module; mto = (inference_stack::CallStack).mod if !is(mfrom, mto) expr = resolve_globals(expr, mfrom, mto, args, spnames) end return (sym_replace(expr, args, spnames, argexprs, spvals), stmts) end tn(sym::Symbol) = ccall(:jl_new_struct, Any, (Any,Any...), TopNode, sym, Any) qn(v) = ccall(:jl_new_struct, Any, (Any,Any...), QuoteNode, v) const top_tupleref = tn(:tupleref) const top_tuple = tn(:tuple) function mk_tupleref(texpr, i) e = :(($top_tupleref)($texpr, $i)) e.typ = exprtype(texpr)[i] e end function mk_tuplecall(args) e = Expr(:call1, top_tuple, args...) e.typ = tuple(map(exprtype, args)...) e end const basenumtype = Union(Int32,Int64,Float32,Float64,Complex64,Complex128,Rational) function inlining_pass(e::Expr, sv, ast) # don't inline first argument of ccall, as this needs to be evaluated # by the interpreter and inlining might put in something it can't handle, # like another ccall. eargs = e.args if length(eargs)<1 return (e,()) end arg1 = eargs[1] if is_known_call(e, Core.Intrinsics.ccall, sv) i0 = 3 isccall = true else i0 = 1 isccall = false end stmts = {} if e.head === :body i = i0 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 else for i=i0:length(eargs) ei = eargs[i] if isa(ei,Expr) res = inlining_pass(ei, sv, ast) eargs[i] = res[1] if isa(res[2],Array) append!(stmts,res[2]::Array{Any,1}) end end end end if isccall le = length(eargs) for i=5:2:le if i f(x,y,...) newargs[i-2] = aarg.args[2:] elseif isa(t,Tuple) && !isvatuple(t) && effect_free(aarg,sv) # apply(f,t::(x,y)) => f(t[1],t[2]) newargs[i-2] = { mk_tupleref(aarg,j) for j=1:length(t) } else # not all args expandable return (e,stmts) end end e.args = [{e.args[2]}, newargs...] # now try to inline the simplified call f = isconstantfunc(e.args[1], sv) if f===false return (e,stmts) end f = _ieval(f) else return (e,stmts) end end end return (e,stmts) end function add_variable(ast, name, typ) vinf = {name,typ,2} locllist = ast.args[2][1]::Array{Any,1} vinflist = ast.args[2][2]::Array{Any,1} push!(locllist, name) push!(vinflist, vinf) end const some_names = {:_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 unique_name(ast) locllist = ast.args[2][1]::Array{Any,1} for g in some_names if !contains_is(locllist, g) return g end end g = gensym() while contains_is(locllist, 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_is(locllist, g) push!(ns, g) if length(ns)==n return ns end end end while length(ns)!symequal(vi[1],v), ast.args[2][2]) filter!(x->!symequal(x,v), ast.args[2][1]) filter!(x->!(isa(x,Expr) && x.head === :(=) && symequal(x.args[1],v)), ast.args[3].args) ast end # 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][2] 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 !occurs_undef(v, ast.args[3]) # 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). delete_var!(ast, v) sym_replace(ast.args[3], {v}, {}, {init}, {}) end end end ast end occurs_undef(var, expr) = occurs_more(expr, e->(isa(e,SymbolNode) && symequal(var,e) && issubtype(Undef,e.typ)), 0)>0 # compute set of vars assigned once function find_sa_vars(ast) body = ast.args[3].args av = ObjectIdDict() av2 = ObjectIdDict() vnames = ast.args[2][1] for i = 1:length(body) e = body[i] if isa(e,Expr) && is(e.head,:(=)) lhs = e.args[1] if contains_is(vnames, lhs) # exclude globals if !haskey(av, lhs) av[lhs] = e.args[2] else av2[lhs] = true end end end end filter!((var,_)->!haskey(av2,var), av) for vi in ast.args[2][2] if (vi[3]&1)!=0 # remove captured vars delete!(av, vi[1]) end end av 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::ANY , y::ANY) = is(x,y) function occurs_outside_tupleref(e::ANY, sym::ANY, sv::StaticVarInfo, tuplen::Int) if is(e, sym) || (isa(e, SymbolNode) && is(e.name, sym)) return true end if isa(e,Expr) e = e::Expr if is_known_call(e, tupleref, sv) && symequal(e.args[2],sym) targ = e.args[2] if !(exprtype(targ) <: Tuple) return true end idx = e.args[3] if !isa(idx,Int) || !(1 <= idx <= tuplen) return true end return false end if is(e.head,:(=)) return occurs_outside_tupleref(e.args[2], sym, sv, tuplen) else for a in e.args if occurs_outside_tupleref(a, sym, sv, tuplen) return true end end end end return false end # eliminate allocation of unnecessary tuples function tuple_elim_pass(ast::Expr) sv = inference_stack.sv bexpr = ast.args[3]::Expr body = (ast.args[3].args)::Array{Any,1} vs = find_sa_vars(ast) remove_redundant_temp_vars(ast, vs) i = 1 while i < length(body) e = body[i] if !(isa(e,Expr) && is(e.head,:(=)) && haskey(vs, e.args[1])) i += 1 continue end var = e.args[1] rhs = e.args[2] if isa(rhs,Expr) && is_known_call(rhs, tuple, sv) tup = rhs.args nv = length(tup)-1 if occurs_outside_tupleref(bexpr, var, sv, nv) || !is_local(sv, var) i += 1 continue end splice!(body, i) # remove tuple allocation # convert tuple allocation to a series of local var assignments vals = cell(nv) n_ins = 0 for j=1:nv tupelt = tup[j+1] if isa(tupelt,Number) || isa(tupelt,String) || isa(tupelt,QuoteNode) vals[j] = tupelt else elty = exprtype(tupelt) tmpv = unique_name(ast) tmp = Expr(:(=), tmpv, tupelt) add_variable(ast, tmpv, elty) insert!(body, i+n_ins, tmp) vals[j] = SymbolNode(tmpv, elty) n_ins += 1 end end i += n_ins replace_tupleref!(ast, bexpr, var, vals, sv, i) else i += 1 end end end function replace_tupleref!(ast, e::ANY, tupname, vals, sv, i0) if !isa(e,Expr) return end for i = i0:length(e.args) a = e.args[i] if isa(a,Expr) && is_known_call(a, tupleref, sv) && symequal(a.args[2],tupname) val = vals[a.args[3]] if isa(val,SymbolNode) && a.typ <: val.typ && !typeseq(a.typ,val.typ) # original expression might have better type info than # the tuple element expression that's replacing it. val.typ = a.typ for vi in ast.args[2][2]::Array{Any,1} if vi[1] === val.name vi[2] = a.typ break end end end e.args[i] = val else replace_tupleref!(ast, a, tupname, vals, sv, 1) end end end function code_typed(f::Callable, types) asts = {} for x in _methods(f,types,-1) linfo = x[3].func.code (tree, ty) = typeinf(linfo, x[1], x[2]) if !isa(tree,Expr) push!(asts, ccall(:jl_uncompress_ast, Any, (Any,Any), linfo, tree)) else push!(asts, tree) end end asts end #tfunc(f,t) = methods(f,t)[1].func.code.tfunc ccall(:jl_enable_inference, Void, ())