Skip to content
This repository has been archived by the owner on Dec 16, 2022. It is now read-only.

Commit

Permalink
rewrite convert_tuple in julia
Browse files Browse the repository at this point in the history
along with an optimization pass that is helpful for it

this is less code, runs faster in basically all cases, and allows
specialized code generation, constant folding, etc. of tuple conversions
  • Loading branch information
JeffBezanson committed Apr 10, 2014
1 parent 951e027 commit 8f8d7fa
Show file tree
Hide file tree
Showing 8 changed files with 58 additions and 117 deletions.
19 changes: 16 additions & 3 deletions base/base.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# important core definitions

using Core: Intrinsics, arraylen, arrayref, arrayset, arraysize,
tuplelen, tupleref, convert_default, convert_tuple, kwcall,
tuplelen, tupleref, convert_default, kwcall,
typeassert, apply_type

import Core.Array # to add methods
Expand All @@ -10,8 +10,21 @@ const NonTupleType = Union(DataType,UnionType,TypeConstructor)

typealias Callable Union(Function,DataType)

convert(T, x) = convert_default(T, x, convert)
convert(T::Tuple, x::Tuple) = convert_tuple(T, x, convert)
convert(T, x) = convert_default(T, x, convert)

convert(::(), ::()) = ()
convert(::Type{Tuple}, x::Tuple) = x

argtail(x, rest...) = rest
tupletail(x::Tuple) = argtail(x...)

convert(T::(Any, Any...), x::(Any, Any...)) =
tuple(convert(T[1],x[1]), convert(tupletail(T), tupletail(x))...)

convert{T}(::Type{(T...)}, x::Tuple) = cnvt_all(T, x...)
cnvt_all(T) = ()
cnvt_all(T, x, rest...) = tuple(convert(T,x), cnvt_all(T, rest...)...)


ptr_arg_convert{T}(::Type{Ptr{T}}, x) = convert(T, x)
ptr_arg_convert(::Type{Ptr{Void}}, x) = x
Expand Down
2 changes: 1 addition & 1 deletion base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export
# object model functions
apply, fieldtype, getfield, setfield!, yieldto, throw, tuple, is, ===, isdefined,
# arraylen, arrayref, arrayset, arraysize, tuplelen, tupleref, convert_default,
# convert_tuple, kwcall,
# kwcall,
# type reflection
issubtype, typeof, isa,
# typeassert, apply_type,
Expand Down
102 changes: 39 additions & 63 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -154,61 +154,20 @@ 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)
const convert_default_tfunc = function (to, from, f)
!isType(to) && return Any
to = to.parameters[1]

if isa(to,TypeVar)
return to
end
if is(to,Tuple)
if from <: to
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))
return typeintersect(from,to)
end
t_func[convert_default] = (3, 3, convert_default_tfunc)

const typeof_tfunc = function (t)
if isType(t)
t = t.parameters[1]
Expand Down Expand Up @@ -1516,6 +1475,7 @@ function typeinf(linfo::LambdaStaticData,atypes::Tuple,sparams::Tuple, def, cop)
# inlining can add variables
sv.vars = append_any(f_argnames(fulltree), fulltree.args[2][1])
tuple_elim_pass(fulltree)
tupleref_elim_pass(fulltree.args[3], sv)
linfo.inferred = true
fulltree = ccall(:jl_compress_ast, Any, (Any,Any), def, fulltree)
end
Expand Down Expand Up @@ -1954,17 +1914,6 @@ function inlineable(f, e::Expr, atypes, sv, enclosing_ast)
return (e.args[3],())
end
end
if is(f, convert_tuple) && length(atypes)==3
# convert((T...),x::(S...)) => x, when S<:T
istuple = isa(atypes[1],Tuple) # if true, is (Type{T}, Type{S}) not Type{(T, S)}
if (istuple && all(isType,atypes[1])) || (atypes[1] <: Tuple && isType(atypes[1])) && isleaftype(atypes[1])
tparams = (istuple ? map(t->t.parameters[1], atypes[1]) : atypes[1].parameters[1])
if atypes[2] <: tparams
# todo: if T expression has side effects??!
return (e.args[3],())
end
end
end
if length(atypes)==2 && is(f,unbox) && isa(atypes[2],DataType) && !atypes[2].mutable && atypes[2].pointerfree
# remove redundant unbox
return (e.args[3],())
Expand Down Expand Up @@ -2679,6 +2628,33 @@ function occurs_outside_tupleref(e::ANY, sym::ANY, sv::StaticVarInfo, tuplen::In
return false
end

# replace tupleref(tuple(exprs...), i) with exprs[i]
function tupleref_elim_pass(e::Expr, sv)
for i = 1:length(e.args)
ei = e.args[i]
if isa(ei,Expr)
tupleref_elim_pass(ei, sv)
if is_known_call(ei, tupleref, sv) && length(ei.args)==3 &&
isa(ei.args[2],Expr) && isa(ei.args[3],Int)
e1 = ei.args[2]
j = ei.args[3]
if is_known_call(e1, tuple, sv) && (1 <= j < length(e1.args))
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
end
end
end
end
end

# eliminate allocation of unnecessary tuples
function tuple_elim_pass(ast::Expr)
sv = inference_stack.sv
Expand Down
2 changes: 1 addition & 1 deletion base/multidimensional.jl
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ end

## permutedims

for (V, PT, BT) in [((:N,), BitArray, BitArray), ((:T,:N), Array, StridedArray)]
for (V, PT, BT) in {((:N,), BitArray, BitArray), ((:T,:N), Array, StridedArray)}
@eval @ngenerate N typeof(P) function permutedims!{$(V...)}(P::$PT{$(V...)}, B::$BT{$(V...)}, perm)
dimsB = size(B)
length(perm) == N || error("expected permutation of size $N, but length(perm)=$(length(perm))")
Expand Down
1 change: 0 additions & 1 deletion base/precompile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ precompile(rr2id, (RemoteRef,))
precompile(isequal, (RemoteRef, WeakRef))
precompile(isequal, (RemoteRef, RemoteRef))
precompile(_ieval, (Symbol,))
precompile(static_convert, (Nothing, Nothing))
precompile(setindex!, (Array{Any,1}, WeakRef, Int))
precompile(isequal, ((Int,Int),(Int,Int)))
precompile(isequal, (Int,Int))
Expand Down
1 change: 0 additions & 1 deletion src/builtin_proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ JL_CALLABLE(jl_f_arrayset);
JL_CALLABLE(jl_f_arraysize);
JL_CALLABLE(jl_f_instantiate_type);
JL_CALLABLE(jl_f_convert_default);
JL_CALLABLE(jl_f_convert_tuple);
JL_CALLABLE(jl_f_new_type_constructor);
JL_CALLABLE(jl_f_typevar);
JL_CALLABLE(jl_f_union);
Expand Down
46 changes: 0 additions & 46 deletions src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -564,51 +564,6 @@ JL_CALLABLE(jl_f_field_type)

// conversion -----------------------------------------------------------------

static jl_value_t *convert(jl_value_t *to, jl_value_t *x, jl_function_t *conv_f)
{
jl_value_t *args[2];
if (jl_subtype(x, (jl_value_t*)to, 1))
return x;
args[0] = (jl_value_t*)to; args[1] = x;
return jl_apply(conv_f, args, 2);
}

JL_CALLABLE(jl_f_convert_tuple)
{
jl_tuple_t *to = (jl_tuple_t*)args[0];
jl_tuple_t *x = (jl_tuple_t*)args[1];
if (to == jl_tuple_type)
return (jl_value_t*)x;
size_t i, cl=jl_tuple_len(x), pl=jl_tuple_len(to);
jl_tuple_t *out = jl_alloc_tuple(cl);
JL_GC_PUSH1(&out);
jl_value_t *ce, *pe=NULL;
int pseq=0;
jl_function_t *f = (jl_function_t*)args[2];
for(i=0; i < cl; i++) {
ce = jl_tupleref(x,i);
if (pseq) {
}
else if (i < pl) {
pe = jl_tupleref(to,i);
if (jl_is_vararg_type(pe)) {
pe = jl_tparam0(pe);
pseq = 1;
}
}
else {
out = NULL;
break;
}
assert(pe != NULL);
jl_tupleset(out, i, convert((jl_value_t*)pe, ce, f));
}
JL_GC_POP();
if (out == NULL)
jl_error("convert: invalid tuple conversion");
return (jl_value_t*)out;
}

JL_CALLABLE(jl_f_convert_default)
{
jl_value_t *to = args[0];
Expand Down Expand Up @@ -1015,7 +970,6 @@ void jl_init_primitives(void)

// functions for internal use
add_builtin_func("convert_default", jl_f_convert_default);
add_builtin_func("convert_tuple", jl_f_convert_tuple);
add_builtin_func("tupleref", jl_f_tupleref);
add_builtin_func("tuplelen", jl_f_tuplelen);
add_builtin_func("getfield", jl_f_get_field);
Expand Down
2 changes: 1 addition & 1 deletion src/dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -1329,7 +1329,7 @@ void jl_init_serializer(void)
jl_f_arraylen, jl_f_arrayref,
jl_f_arrayset, jl_f_arraysize,
jl_f_instantiate_type, jl_f_kwcall,
jl_f_convert_default, jl_f_convert_tuple,
jl_f_convert_default,
jl_trampoline, jl_f_new_type_constructor,
jl_f_typevar, jl_f_union,
jl_f_methodexists, jl_f_applicable,
Expand Down

0 comments on commit 8f8d7fa

Please sign in to comment.