Skip to content

Commit

Permalink
Merge pull request #13555 from JuliaLang/jn/pure
Browse files Browse the repository at this point in the history
implement a pure function annotation
  • Loading branch information
vtjnash committed Oct 13, 2015
2 parents 83eac1e + b7278d1 commit 2b8773f
Show file tree
Hide file tree
Showing 14 changed files with 238 additions and 110 deletions.
9 changes: 0 additions & 9 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,6 @@ linearindexing(A::AbstractArray, B::AbstractArray...) = linearindexing(linearind
linearindexing(::LinearFast, ::LinearFast) = LinearFast()
linearindexing(::LinearIndexing, ::LinearIndexing) = LinearSlow()

# The real @inline macro is not available this early in the bootstrap, so this
# internal macro splices the meta Expr directly into the function body.
macro _inline_meta()
Expr(:meta, :inline)
end
macro _noinline_meta()
Expr(:meta, :noinline)
end

## Bounds checking ##
@generated function trailingsize{T,N,n}(A::AbstractArray{T,N}, ::Type{Val{n}})
n > N && return 1
Expand Down
9 changes: 7 additions & 2 deletions base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ end
macro _noinline_meta()
Expr(:meta, :noinline)
end
macro _pure_meta()
Expr(:meta, :pure)
end


# constructors for Core types in boot.jl
Expand Down Expand Up @@ -74,15 +77,17 @@ macro generated(f)
end


@generated function tuple_type_head{T<:Tuple}(::Type{T})
function tuple_type_head{T<:Tuple}(::Type{T})
@_pure_meta
T.parameters[1]
end

isvarargtype(t::ANY) = isa(t,DataType)&&is((t::DataType).name,Vararg.name)
isvatuple(t::DataType) = (n = length(t.parameters); n > 0 && isvarargtype(t.parameters[n]))
unwrapva(t::ANY) = isvarargtype(t) ? t.parameters[1] : t

@generated function tuple_type_tail{T<:Tuple}(::Type{T})
function tuple_type_tail{T<:Tuple}(::Type{T})
@_pure_meta
if isvatuple(T) && length(T.parameters) == 1
return T
end
Expand Down
12 changes: 5 additions & 7 deletions base/expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,16 @@ macro eval(x)
end

macro inline(ex)
esc(_inline(ex))
esc(isa(ex, Expr) ? pushmeta!(ex, :inline) : ex)
end

_inline(ex::Expr) = pushmeta!(ex, :inline)
_inline(arg) = arg

macro noinline(ex)
esc(_noinline(ex))
esc(isa(ex, Expr) ? pushmeta!(ex, :noinline) : ex)
end

_noinline(ex::Expr) = pushmeta!(ex, :noinline)
_noinline(arg) = arg
macro pure(ex)
esc(isa(ex, Expr) ? pushmeta!(ex, :pure) : ex)
end

## some macro utilities ##

Expand Down
198 changes: 148 additions & 50 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const MAX_TYPE_DEPTH = 5
const MAX_TUPLETYPE_LEN = 8
const MAX_TUPLE_DEPTH = 4

type NotFound
immutable NotFound
end

const NF = NotFound()
Expand Down Expand Up @@ -535,7 +535,7 @@ function builtin_tfunction(f::ANY, args::ANY, argtype::ANY, vtypes::ObjectIdDict
return tf[3](argtypes...)
end

function isconstantfunc(f::ANY, sv::StaticVarInfo)
function isconstantref(f::ANY, sv::StaticVarInfo)
if isa(f,TopNode)
m = _topmod()
return isconst(m, f.name) && isdefined(m, f.name) && f
Expand All @@ -544,39 +544,46 @@ function isconstantfunc(f::ANY, sv::StaticVarInfo)
M = f.mod; s = f.name
return isdefined(M,s) && isconst(M,s) && f
end
if isa(f,Expr) && is(f.head,:call)
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
if isa(f,Expr)
if is(f.head,:call)
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 = isconstantref(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
return isdefined(M,s) && isconst(M,s) && f
elseif is(f.head,:inert)
return f
end
return false
end

if isa(f,QuoteNode) && (isa(f.value, Function) || isa(f.value, IntrinsicFunction))
return f.value
end
if isa(f,Function) || isa(f,IntrinsicFunction)
if isa(f,QuoteNode)
return f
end
if isa(f,SymbolNode)
f = f.name
end
return isa(f,Symbol) && is_global(sv, f) && _iisconst(f) && f
if isa(f,Symbol)
return is_global(sv, f) && _iisconst(f) && f
end
if isa(f,GenSym) || isa(f,LambdaStaticData)
return false
end
return f
end

const isconstantref = isconstantfunc
const isconstantfunc = isconstantref

const limit_tuple_depth = t->limit_tuple_depth_(t,0)

Expand Down Expand Up @@ -651,25 +658,7 @@ function abstract_call_gf(f, fargs, argtype, e)
end
end
if istopfunction(tm, f, :promote_type) || istopfunction(tm, f, :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 istopfunction(tm, f, :promote_type)
try
RT = Type{f(c...)}
return RT
catch
end
else
return Type{f(c...)}
end
return Type
end
# don't consider more than N methods. this trades off between
# compiler performance and generated code performance.
Expand Down Expand Up @@ -822,7 +811,109 @@ function abstract_apply(af, fargs, aargtypes::Vector{Any}, vtypes, sv, e)
return abstract_call(af, (), Any[Vararg{Any}], vtypes, sv, ())
end

function isconstantargs(args, argtypes::Vector{Any}, sv::StaticVarInfo)
if isempty(argtypes)
return true
end
if is(args,()) || isa(argtypes[end], Vararg)
return false
end
for i = 1:length(args)
arg = args[i]
t = argtypes[i]
if !isType(t) || has_typevars(t.parameters[1])
if isconstantref(arg, sv) === false
return false
end
end
end
return true
end

function _ieval_args(args, argtypes::Vector{Any}, sv::StaticVarInfo)
c = cell(length(args))
for i = 1:length(argtypes)
t = argtypes[i]
if isType(t) && !has_typevars(t.parameters[1])
c[i] = t.parameters[1]
else
c[i] = _ieval(isconstantref(args[i], sv))
end
end
return c
end

@pure function type_typeof(v::ANY)
if isa(v, Type)
return Type{v}
end
return typeof(v)
end

function pure_eval_call(f, fargs, argtypes, sv, e)
if !isa(f, Function) # TODO: maybe replace with :call?
return false
end
if !isconstantargs(fargs, argtypes, sv)
return false
end

args = _ieval_args(fargs, argtypes, sv)
tm = _topmod()
if isgeneric(f)
atype = Tuple{Any[type_typeof(a) for a in args]...}
meth = _methods(f, atype, 1)
if meth === false || length(meth) != 1
return false
end
meth = meth[1]::SimpleVector
linfo = try
func_for_method(meth[3], atype, meth[2])
catch
NF
end
if linfo === NF
return false
end
if !linfo.pure
typeinf(linfo, meth[1], meth[2], linfo)
if !linfo.pure
return false
end
end
elseif !isdefined(f, :code) || !f.code.pure
return false
end

local v
try
v = f(args...)
catch
return false
end
if isa(e, Expr) # replace Expr with a constant
stmts = Any[] # check if any arguments aren't effect_free and need to be kept around
for i = 1:length(fargs)
arg = fargs[i]
if !effect_free(arg, sv, false)
push!(stmts, arg)
end
end
if isempty(stmts)
e.head = :inert # eval_annotate will turn this into a QuoteNode
e.args = Any[v]
else
e.head = :call # should get cleaned up by tuple elimination
e.args = Any[top_getfield, Expr(:call, top_tuple, stmts..., v), length(stmts) + 1]
end
end
return type_typeof(v)
end


function abstract_call(f, fargs, argtypes::Vector{Any}, vtypes, sv::StaticVarInfo, e)
t = pure_eval_call(f, fargs, argtypes, sv, e)
t !== false && return t
if is(f,_apply) && length(fargs)>1
af = isconstantfunc(fargs[2], sv)
if !is(af,false)
Expand Down Expand Up @@ -926,7 +1017,8 @@ function abstract_eval_call(e, vtypes, sv::StaticVarInfo)
if !(Function <: ft) && _iisdefined(:call)
call_func = _ieval(:call)
if isa(call_func,Function)
return abstract_call(call_func, e.args, Any[ft,argtypes...], vtypes, sv, e)
unshift!(argtypes, ft)
return abstract_call(call_func, e.args, argtypes, vtypes, sv, e)
end
end
return Any
Expand All @@ -943,7 +1035,8 @@ end

function abstract_eval(e::ANY, vtypes, sv::StaticVarInfo)
if isa(e,QuoteNode)
return typeof((e::QuoteNode).value)
v = (e::QuoteNode).value
return type_typeof(v)
elseif isa(e,TopNode)
return abstract_eval_global(_topmod(), (e::TopNode).name)
elseif isa(e,Symbol)
Expand Down Expand Up @@ -1019,6 +1112,9 @@ function abstract_eval(e::ANY, vtypes, sv::StaticVarInfo)
t = Function
elseif is(e.head,:copyast)
t = abstract_eval(e.args[1], vtypes, sv)
elseif is(e.head,:inert)
v = e.args[1]
return type_typeof(v)
else
t = Any
end
Expand Down Expand Up @@ -1713,6 +1809,9 @@ function typeinf_uncached(linfo::LambdaStaticData, atypes::ANY, sparams::SimpleV
getfield_elim_pass(fulltree.args[3], sv)
end
linfo.inferred = true
body = Expr(:block)
body.args = fulltree.args[3].args::Array{Any,1}
linfo.pure = popmeta!(body, :pure)[1]
fulltree = ccall(:jl_compress_ast, Any, (Any,Any), def, fulltree)
end

Expand Down Expand Up @@ -1779,6 +1878,8 @@ function eval_annotate(e::ANY, vtypes::ANY, sv::StaticVarInfo, decls, clo, undef
return e
#elseif is(head,:gotoifnot) || is(head,:return)
# e.typ = Any
elseif is(head,:inert)
return QuoteNode(e.args[1])
elseif is(head,:(=))
# e.typ = Any
s = e.args[1]
Expand Down Expand Up @@ -1968,10 +2069,7 @@ function exprtype(x::ANY, sv::StaticVarInfo)
return abstract_eval(x::Symbol, emptydict, sv)
elseif isa(x,QuoteNode)
v = (x::QuoteNode).value
if isa(v,Type)
return Type{v}
end
return typeof(v)
return type_typeof(v)
elseif isa(x,Type)
return Type{x}
elseif isa(x,LambdaStaticData)
Expand Down Expand Up @@ -2033,7 +2131,7 @@ function effect_free(e::ANY, sv, allow_volatile::Bool)
allow_volatile && return true
return isconst(e.mod, e.name)
end
if isconstantfunc(e, sv) !== false
if isconstantref(e, sv) !== false
return true
end
if isa(e,Expr)
Expand Down
3 changes: 2 additions & 1 deletion base/iterator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ immutable Zip{I, Z<:AbstractZipIterator} <: AbstractZipIterator
end
zip(a, b, c...) = Zip(a, zip(b, c...))
length(z::Zip) = min(length(z.a), length(z.z))
@generated function tuple_type_cons{S,T<:Tuple}(::Type{S}, ::Type{T})
function tuple_type_cons{S,T<:Tuple}(::Type{S}, ::Type{T})
@_pure_meta
Tuple{S, T.parameters...}
end
eltype{I,Z}(::Type{Zip{I,Z}}) = tuple_type_cons(eltype(I), eltype(Z))
Expand Down
Loading

0 comments on commit 2b8773f

Please sign in to comment.