diff --git a/base/base.jl b/base/base.jl index 26a38bf55ddd0..21518918faebd 100644 --- a/base/base.jl +++ b/base/base.jl @@ -11,6 +11,12 @@ convert(T, x) = convert_default(T, x, convert) convert(::(), ::()) = () convert(::Type{Tuple}, x::Tuple) = x +# fall back to Core.call +call(args...) = Core.call(args...) + +# allow convert to be called as if it were a single-argument constructor +# call(T::Type, x) = convert(T, x) + argtail(x, rest...) = rest tupletail(x::Tuple) = argtail(x...) diff --git a/base/boot.jl b/base/boot.jl index 0503e5ec50055..ffe6e6d3cb353 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -137,9 +137,9 @@ export Expr, GotoNode, LabelNode, LineNumberNode, QuoteNode, SymbolNode, TopNode, GetfieldNode, NewvarNode, # object model functions - apply, fieldtype, getfield, setfield!, yieldto, throw, tuple, is, ===, isdefined, + fieldtype, getfield, setfield!, yieldto, throw, tuple, is, ===, isdefined, # arraylen, arrayref, arrayset, arraysize, tuplelen, tupleref, convert_default, - # kwcall, + # _apply, kwcall, # sizeof # not exported, to avoid conflicting with Base.sizeof # type reflection issubtype, typeof, isa, @@ -168,6 +168,11 @@ export const (===) = is +# simple convert for use by constructors of types in Core +convert(T, x) = convert_default(T, x, convert) + +call(T::Type, arg) = convert(T, arg) + abstract Number abstract Real <: Number abstract FloatingPoint <: Real @@ -217,9 +222,6 @@ type InterruptException <: Exception end abstract String abstract DirectIndexString <: String -# simple convert for use by constructors of types in Core -convert(T, x) = convert_default(T, x, convert) - type SymbolNode name::Symbol typ diff --git a/base/deprecated.jl b/base/deprecated.jl index a4714f9d82687..726767442f7ca 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -186,6 +186,12 @@ const Nothing = Void export None const None = Union() +export apply +function apply(f, args...) + depwarn("apply() is deprecated, use `...` instead", :apply) + return Core._apply(call, f, args...) +end + @deprecate median(v::AbstractArray; checknan::Bool=true) median(v) @deprecate median(v::AbstractArray, region; checknan::Bool=true) median(v, region) @deprecate median!(v::AbstractVector; checknan::Bool=true) median!(v) diff --git a/base/exports.jl b/base/exports.jl index 22847d26f05ba..ac9df7c3df390 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -269,6 +269,7 @@ export At_mul_Bt!, At_rdiv_B, At_rdiv_Bt, + call, # scalar math @evalpoly, diff --git a/base/inference.jl b/base/inference.jl index 739b636673ab2..65d318fdd034e 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -476,8 +476,6 @@ const apply_type_tfunc = function (A, args...) end t_func[apply_type] = (1, Inf, apply_type_tfunc) -# other: apply - function tuple_tfunc(argtypes::ANY, limit) t = argtypes if limit @@ -787,54 +785,74 @@ function to_tuple_of_Types(t::ANY) return t end +# do apply(af, fargs...), where af is a function value +function abstract_apply(af, aargtypes, vtypes, sv, e) + 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(tuple(append_any(aargtypes...)...)) : () + return abstract_call(af, (), at, vtypes, sv, ()) + end + if is(af,tuple) && length(aargtypes)==1 + # 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 + if is(af,kwcall) + return Any + end + # apply known function with unknown args => f(Any...) + return abstract_call(af, (), Tuple, vtypes, sv, ()) +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] + if is(f,_apply) && length(fargs)>1 + a2type = argtypes[2] + if isType(a2type) && isleaftype(a2type.parameters[1]) + af = a2type.parameters[1] _methods(af,(),0) else - af = isconstantfunc(fargs[1], sv) + af = isconstantfunc(fargs[2], sv) end + if !is(af,false) - aargtypes = map(to_tuple_of_Types, argtypes[2:end]) - 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 - if is(af,kwcall) - return Any - end - # apply known function with unknown args => f(Any...) - return abstract_call(af, (), Tuple, vtypes, sv, ()) - end + if isa(af,Function) || isa(af,DataType) + aargtypes = map(to_tuple_of_Types, argtypes[3:end]) + return abstract_apply(af, aargtypes, vtypes, sv, e) + end + end + # TODO: this slows down inference a lot + # if !(a2type===Function || a2type===DataType) && isleaftype(a2type) + # # would definitely use call() + # call_func = isconstantfunc(fargs[1]) + # if !is(call_func,false) + # aargtypes = Any[ to_tuple_of_Types(argtypes[i]) for i=2:length(argtypes) ] + # aargtypes[1] = (aargtypes[1],) # don't splat "function" + # return abstract_apply(_ieval(call_func), tuple(aargtypes...), vtypes, sv, e) + # end + # end + return Any end if isgeneric(f) return abstract_call_gf(f, fargs, argtypes, e) @@ -849,7 +867,7 @@ function abstract_call(f, fargs, argtypes, vtypes, sv::StaticVarInfo, e) end end end - if !is(f,apply) && isa(e,Expr) && (isa(f,Function) || isa(f,IntrinsicFunction)) + if !is(f,_apply) && isa(e,Expr) && (isa(f,Function) || isa(f,IntrinsicFunction)) e.head = :call1 end if is(f,getfield) @@ -859,25 +877,30 @@ function abstract_call(f, fargs, argtypes, vtypes, sv::StaticVarInfo, e) end end if is(f,kwcall) - if length(argtypes) < 3 + if length(argtypes) < 4 return Bottom end - if length(fargs) < 2 + if length(fargs) < 3 return Any end - ff = isconstantfunc(fargs[1], sv) + kwcount = fargs[2] + ff = isconstantfunc(fargs[4 + 2*kwcount], 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] + posargt = argtypes[(5+2*kwcount):end] return abstract_call_gf(ff.env.kwsorter, (), tuple(Array{Any,1}, posargt...), e) end end + # TODO: call() case return Any end + if !isa(f,Function) && !isa(f,DataType) && !isa(f,IntrinsicFunction) && _iisdefined(:call) + call_func = _ieval(:call) + return abstract_call(call_func, e.args, tuple(Any[typeof(f),argtypes...]...), vtypes, sv, e) + end rt = builtin_tfunction(f, fargs, argtypes) #print("=> ", rt, "\n") return rt @@ -924,6 +947,10 @@ function abstract_eval_call(e, vtypes, sv::StaticVarInfo) return st end end + if !(Function <: ft) && typeintersect(DataType, ft)==Bottom && _iisdefined(:call) + call_func = _ieval(:call) + return abstract_call(call_func, e.args, tuple(Any[ft,argtypes...]...), vtypes, sv, e) + end return Any end #print("call ", e.args[1], argtypes, " ") @@ -1313,6 +1340,8 @@ function typeinf(linfo::LambdaStaticData,atypes::Tuple,sparams::Tuple, def, cop) end #if dbg print("typeinf ", linfo.name, " ", atypes, "\n") end + #ccall(:jl_,Void,(Any,),linfo.name) + #ccall(:jl_,Void,(Any,),atypes) if cop sparams = tuple(sparams..., linfo.sparams...) @@ -2650,7 +2679,14 @@ function inlining_pass(e::Expr, sv, ast) if isType(ET) f = ET.parameters[1] else - f = _ieval(arg1) + f1 = f = isconstantfunc(arg1, sv) + if !is(f,false) + f = _ieval(f) + end + if f1===false || !(isa(f,Function) || isa(f,Type) || isa(f,IntrinsicFunction)) + f = _ieval(:call) + e.args = Any[f, e.args...] + end end if is(f, ^) || is(f, .^) @@ -2685,34 +2721,34 @@ function inlining_pass(e::Expr, sv, ast) if !is(res,NF) # iteratively inline apply(f, tuple(...), tuple(...), ...) in order # to simplify long vararg lists as in multi-arg + - if isa(res,Expr) && is_known_call(res, apply, sv) + if isa(res,Expr) && is_known_call(res, _apply, sv) e = res::Expr - f = apply + f = _apply else return (res,stmts) end end - if is(f,apply) + if is(f,_apply) na = length(e.args) - newargs = cell(na-2) - for i = 3:na + newargs = cell(na-3) + for i = 4:na aarg = e.args[i] t = to_tuple_of_Types(exprtype(aarg)) if isa(aarg,Expr) && is_known_call(aarg, tuple, sv) # apply(f,tuple(x,y,...)) => f(x,y,...) - newargs[i-2] = aarg.args[2:end] + newargs[i-3] = aarg.args[2:end] elseif isa(aarg, Tuple) - newargs[i-2] = Any[ QuoteNode(x) for x in aarg ] + newargs[i-3] = Any[ QuoteNode(x) for x in aarg ] elseif isa(t,Tuple) && !isvatuple(t) && effect_free(aarg,sv,true) # apply(f,t::(x,y)) => f(t[1],t[2]) - newargs[i-2] = Any[ mk_tupleref(aarg,j,t[j]) for j=1:length(t) ] + newargs[i-3] = Any[ mk_tupleref(aarg,j,t[j]) for j=1:length(t) ] else # not all args expandable return (e,stmts) end end - e.args = [Any[e.args[2]], newargs...] + e.args = [Any[e.args[3]], newargs...] # now try to inline the simplified call diff --git a/base/linalg/bidiag.jl b/base/linalg/bidiag.jl index e83403ded6590..f7febba36574c 100644 --- a/base/linalg/bidiag.jl +++ b/base/linalg/bidiag.jl @@ -92,7 +92,7 @@ function +(A::Bidiagonal, B::Bidiagonal) if A.isupper==B.isupper Bidiagonal(A.dv+B.dv, A.ev+B.ev, A.isupper) else - apply(Tridiagonal, A.isupper ? (B.ev,A.dv+B.dv,A.ev) : (A.ev,A.dv+B.dv,B.ev)) + Tridiagonal((A.isupper ? (B.ev,A.dv+B.dv,A.ev) : (A.ev,A.dv+B.dv,B.ev))...) end end @@ -100,7 +100,7 @@ function -(A::Bidiagonal, B::Bidiagonal) if A.isupper==B.isupper Bidiagonal(A.dv-B.dv, A.ev-B.ev, A.isupper) else - apply(Tridiagonal, A.isupper ? (-B.ev,A.dv-B.dv,A.ev) : (A.ev,A.dv-B.dv,-B.ev)) + Tridiagonal((A.isupper ? (-B.ev,A.dv-B.dv,A.ev) : (A.ev,A.dv-B.dv,-B.ev))...) end end diff --git a/base/operators.jl b/base/operators.jl index ded6dfd9e42ed..34916c15fd5a0 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -498,12 +498,13 @@ export getindex, setindex!, transpose, - ctranspose + ctranspose, + call import Base: !, !=, $, %, .%, &, *, +, -, .!=, .+, .-, .*, ./, .<, .<=, .==, .>, .>=, .\, .^, /, //, <, <:, <<, <=, ==, >, >=, >>, .>>, .<<, >>>, <|, |>, \, ^, |, ~, !==, >:, colon, hcat, vcat, hvcat, getindex, setindex!, - transpose, ctranspose, + transpose, ctranspose, call, ≥, ≤, ≠, .≥, .≤, .≠, ÷, ⋅, ×, ∈, ∉, ∋, ∌, ⊆, ⊈, ⊊, ∩, ∪, √, ∛ end diff --git a/base/sysimg.jl b/base/sysimg.jl index b31e71e221707..4f8d4fd44ca26 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -9,7 +9,7 @@ eval(m,x) = Core.eval(m,x) include = Core.include using Core: Intrinsics, arraylen, arrayref, arrayset, arraysize, - tuplelen, tupleref, convert_default, kwcall, + tuplelen, tupleref, convert_default, kwcall, _apply, typeassert, apply_type include("exports.jl") diff --git a/src/builtins.c b/src/builtins.c index 7225f486bf97e..4adb71c505160 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -284,10 +284,21 @@ extern size_t jl_page_size; JL_CALLABLE(jl_f_apply) { - JL_NARGSV(apply, 1); - JL_TYPECHK(apply, function, args[0]); + JL_NARGSV(apply, 2); + jl_function_t *f; + jl_function_t *call_func = (jl_function_t*)args[0]; + assert(jl_is_function(call_func)); + if (jl_is_function(args[1])) { + f = (jl_function_t*)args[1]; + --nargs; ++args; /* args[1] becomes args[0] */ + } + else { /* do generic call(args...) instead */ + f = call_func; + // protect "function" arg from splicing + args[1] = (jl_value_t*)jl_tuple1(args[1]); + } if (nargs == 2) { - if (((jl_function_t*)args[0])->fptr == &jl_f_tuple) { + if (f->fptr == &jl_f_tuple) { if (jl_is_tuple(args[1])) return args[1]; if (jl_is_array(args[1])) { @@ -302,7 +313,7 @@ JL_CALLABLE(jl_f_apply) } } if (jl_is_tuple(args[1])) { - return jl_apply((jl_function_t*)args[0], &jl_tupleref(args[1],0), + return jl_apply(f, &jl_tupleref(args[1],0), jl_tuple_len(args[1])); } } @@ -330,7 +341,7 @@ JL_CALLABLE(jl_f_apply) } argarr = jl_apply(jl_append_any_func, &args[1], nargs-1); assert(jl_typeis(argarr, jl_array_any_type)); - result = jl_apply((jl_function_t*)args[0], jl_cell_data(argarr), jl_array_len(argarr)); + result = jl_apply(f, jl_cell_data(argarr), jl_array_len(argarr)); JL_GC_POP(); return result; } @@ -357,7 +368,7 @@ JL_CALLABLE(jl_f_apply) newargs[n++] = jl_cellref(args[i], j); } } - result = jl_apply((jl_function_t*)args[0], newargs, n); + result = jl_apply(f, newargs, n); JL_GC_POP(); return result; } @@ -366,10 +377,28 @@ void jl_add_constructors(jl_datatype_t *t); JL_CALLABLE(jl_f_kwcall) { - if (nargs < 3) + if (nargs < 4) jl_error("internal error: malformed keyword argument call"); - JL_TYPECHK(apply, function, args[0]); - jl_function_t *f = (jl_function_t*)args[0]; + jl_function_t *f; + jl_function_t *call_func = (jl_function_t*)args[0]; + assert(jl_is_function(call_func)); + size_t nkeys = jl_unbox_long(args[1]); + size_t pa = 4 + 2*nkeys; + jl_array_t *container = (jl_array_t*)args[pa-2]; + assert(jl_array_len(container) > 0); + f = (jl_function_t*)args[pa-1]; + if (!jl_is_function(f)) { + // do generic call(args...; kws...) instead + f = call_func; + pa--; + } + else { + // switch (container f pa...) to (f container pa...) + // TODO: this is not as legitimate as it could be. + args[pa-1] = args[pa-2]; + args[pa-2] = (jl_value_t*)f; + } + if (f->fptr == jl_f_ctor_trampoline) jl_add_constructors((jl_datatype_t*)f); if (!jl_is_gf(f)) @@ -380,22 +409,20 @@ JL_CALLABLE(jl_f_kwcall) jl_gf_name(f)->name); } - size_t nkeys = jl_unbox_long(args[1]); - size_t pa = 3 + 2*nkeys; - jl_array_t *container = (jl_array_t*)args[pa-1]; - assert(jl_array_len(container) > 0); for(size_t i=0; i < nkeys*2; i+=2) { jl_cellset(container, i , args[2+i]); jl_cellset(container, i+1, args[2+i+1]); } + args += pa-1; + nargs -= pa-1; assert(jl_is_gf(sorter)); - jl_function_t *m = jl_method_lookup((jl_methtable_t*)sorter->env, &args[pa-1], nargs-(pa-1), 1); + jl_function_t *m = jl_method_lookup((jl_methtable_t*)sorter->env, args, nargs, 1); if (m == jl_bottom_func) { - return jl_no_method_error(f, &args[pa], nargs-pa); + return jl_no_method_error(f, args+1, nargs-1); } - return jl_apply(m, &args[pa-1], nargs-(pa-1)); + return jl_apply(m, args, nargs); } // eval ----------------------------------------------------------------------- @@ -1030,7 +1057,7 @@ void jl_init_primitives(void) add_builtin_func("issubtype", jl_f_subtype); add_builtin_func("isa", jl_f_isa); add_builtin_func("typeassert", jl_f_typeassert); - add_builtin_func("apply", jl_f_apply); + add_builtin_func("_apply", jl_f_apply); add_builtin_func("kwcall", jl_f_kwcall); add_builtin_func("throw", jl_f_throw); add_builtin_func("tuple", jl_f_tuple); diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 7f2b519c34c97..46edf3e0381d7 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -825,29 +825,6 @@ static Value *emit_bounds_check(Value *i, Value *len, jl_codectx_t *ctx) return im1; } -static void emit_func_check(Value *x, jl_codectx_t *ctx) -{ - Value *xty = emit_typeof(x); - Value *isfunc = - builder. - CreateOr(builder. - CreateICmpEQ(xty, - literal_pointer_val((jl_value_t*)jl_function_type)), - builder. - CreateICmpEQ(xty, - literal_pointer_val((jl_value_t*)jl_datatype_type))); - BasicBlock *elseBB1 = BasicBlock::Create(getGlobalContext(),"notf", ctx->f); - BasicBlock *mergeBB1 = BasicBlock::Create(getGlobalContext(),"isf"); - builder.CreateCondBr(isfunc, mergeBB1, elseBB1); - - builder.SetInsertPoint(elseBB1); - emit_type_error(x, (jl_value_t*)jl_function_type, "apply", ctx); - - builder.CreateBr(mergeBB1); - ctx->f->getBasicBlockList().push_back(mergeBB1); - builder.SetInsertPoint(mergeBB1); -} - // --- loading and storing --- static Value *emit_nthptr_addr(Value *v, size_t n) diff --git a/src/codegen.cpp b/src/codegen.cpp index 2d0b2b3da05d7..84ff537beae60 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1189,8 +1189,8 @@ static void simple_escape_analysis(jl_value_t *expr, bool esc, jl_codectx_t *ctx jl_function_t *ff = (jl_function_t*)fv; if (ff->fptr == jl_f_tuplelen || ff->fptr == jl_f_tupleref || - (ff->fptr == jl_f_apply && alen==3 && - expr_type(jl_exprarg(e,1),ctx) == (jl_value_t*)jl_function_type)) { + (ff->fptr == jl_f_apply && alen==4 && + expr_type(jl_exprarg(e,2),ctx) == (jl_value_t*)jl_function_type)) { esc = false; } } @@ -1811,10 +1811,10 @@ static Value *emit_known_call(jl_value_t *ff, jl_value_t **args, size_t nargs, } } } - else if (f->fptr == &jl_f_apply && nargs==2 && ctx->vaStack && - symbol_eq(args[2], ctx->vaName)) { - Value *theF = emit_expr(args[1],ctx); - Value *theFptr = emit_nthptr_recast(theF,1, tbaa_func,jl_pfptr_llvmt); + else if (f->fptr == &jl_f_apply && nargs==3 && ctx->vaStack && + symbol_eq(args[3], ctx->vaName) && expr_type(args[2],ctx) == (jl_value_t*)jl_function_type) { + Value *theF = emit_expr(args[2],ctx); + Value *theFptr = emit_nthptr_recast(theF,1, tbaa_func, jl_pfptr_llvmt); Value *nva = emit_n_varargs(ctx); #ifdef _P64 nva = builder.CreateTrunc(nva, T_int32); @@ -2259,75 +2259,29 @@ static Value *emit_jlcall(Value *theFptr, Value *theF, jl_value_t **args, } // call - Value *myargs; + Value *myargs = Constant::getNullValue(jl_ppvalue_llvmt); if (ctx->argTemp != NULL && nargs > 0) { myargs = builder.CreateGEP(ctx->argTemp, ConstantInt::get(T_size, argStart+ctx->argSpaceOffs)); } - else { - myargs = Constant::getNullValue(jl_ppvalue_llvmt); - } Value *result = builder.CreateCall3(prepare_call(theFptr), theF, myargs, ConstantInt::get(T_int32,nargs)); ctx->argDepth = argStart; return result; } -static Value *emit_call(jl_value_t **args, size_t arglen, jl_codectx_t *ctx, - jl_value_t *expr) +static Value *emit_call_function_object(jl_function_t *f, Value *theF, Value *theFptr, + bool specialized, + jl_value_t **args, size_t nargs, + jl_codectx_t *ctx) { - size_t nargs = arglen-1; - Value *theFptr=NULL, *theF=NULL; - jl_value_t *a0 = args[0]; - jl_value_t *hdtype; - bool headIsGlobal = false; - - jl_function_t *f = (jl_function_t*)static_eval(a0, ctx, true); - if (f != NULL) { - headIsGlobal = true; - Value *result = emit_known_call((jl_value_t*)f, args, nargs, ctx, - &theFptr, &f, expr); - if (result != NULL) return result; - } - bool specialized = true; - int last_depth = ctx->argDepth; - hdtype = expr_type(a0, ctx); - if (theFptr == NULL) { - specialized = false; - Value *theFunc = emit_expr(args[0], ctx); - if (theFunc->getType() != jl_pvalue_llvmt || jl_is_tuple(hdtype)) { - // we know it's not a function - emit_type_error(theFunc, (jl_value_t*)jl_function_type, "apply", ctx); - ctx->argDepth = last_depth; - return V_null; - } -#ifdef JL_GC_MARKSWEEP - if (!headIsGlobal && (jl_is_expr(a0) || jl_is_lambda_info(a0))) { - make_gcroot(boxed(theFunc,ctx), ctx); - } -#endif - if (hdtype!=(jl_value_t*)jl_function_type && - hdtype!=(jl_value_t*)jl_datatype_type && - !(jl_is_type_type(hdtype) && - jl_is_datatype(jl_tparam0(hdtype)))) { - emit_func_check(theFunc, ctx); - } - // extract pieces of the function object - // TODO: try extractvalue instead - theFptr = emit_nthptr_recast(theFunc, 1, tbaa_func, jl_pfptr_llvmt); - theF = theFunc; - } - else { - theF = literal_pointer_val((jl_value_t*)f); - } - Value *result; if (f!=NULL && specialized && f->linfo!=NULL && f->linfo->cFunctionObject!=NULL) { // emit specialized call site Function *cf = (Function*)f->linfo->cFunctionObject; FunctionType *cft = cf->getFunctionType(); size_t nfargs = cft->getNumParams(); - Value **argvals = (Value**) alloca(nfargs*sizeof(Value*)); + Value **argvals = (Value**)alloca(nfargs*sizeof(Value*)); unsigned idx = 0; for(size_t i=0; i < nargs; i++) { Type *at = cft->getParamType(idx); @@ -2350,8 +2304,8 @@ static Value *emit_call(jl_value_t **args, size_t arglen, jl_codectx_t *ctx, } else { assert(at == et); - argvals[idx] = emit_unbox(at, - emit_unboxed(args[i+1], ctx),jl_tupleref(f->linfo->specTypes,i)); + argvals[idx] = emit_unbox(at, emit_unboxed(args[i+1], ctx), + jl_tupleref(f->linfo->specTypes,i)); assert(dyn_cast(argvals[idx]) == 0); } idx++; @@ -2363,6 +2317,142 @@ static Value *emit_call(jl_value_t **args, size_t arglen, jl_codectx_t *ctx, else { result = emit_jlcall(theFptr, theF, &args[1], nargs, ctx); } + return result; +} + +static Value *emit_is_function(Value *x, jl_codectx_t *ctx) +{ + Value *xty = emit_typeof(x); + Value *isfunc = + builder. + CreateOr(builder. + CreateICmpEQ(xty, + literal_pointer_val((jl_value_t*)jl_function_type)), + builder. + CreateICmpEQ(xty, + literal_pointer_val((jl_value_t*)jl_datatype_type))); + return isfunc; +} + +static Value *emit_call(jl_value_t **args, size_t arglen, jl_codectx_t *ctx, jl_value_t *expr) +{ + size_t nargs = arglen-1; + Value *theFptr=NULL, *theF=NULL; + jl_value_t *a0 = args[0]; + jl_value_t *hdtype; + bool headIsGlobal = false; + bool definitely_function = false; + bool definitely_not_function = false; + + jl_function_t *f = (jl_function_t*)static_eval(a0, ctx, true); + if (f != NULL) { + // function is a compile-time constant + Value *result; + headIsGlobal = true; + definitely_function = jl_is_func(f); + definitely_not_function = !definitely_function; + if (jl_typeis(f, jl_intrinsic_type) || jl_is_func(f)) { + result = emit_known_call((jl_value_t*)f, args, nargs, ctx, + &theFptr, &f, expr); + assert(!jl_typeis(f,jl_intrinsic_type) || result!=NULL); + } + else { + result = emit_known_call((jl_value_t*)jl_module_call_func(ctx->module), + args-1, nargs+1, ctx, &theFptr, &f, expr); + } + if (result != NULL) return result; + } + + hdtype = expr_type(a0, ctx); + definitely_function |= (hdtype == (jl_value_t*)jl_function_type || + hdtype == (jl_value_t*)jl_datatype_type || + (jl_is_type_type(hdtype) && + jl_is_datatype(jl_tparam0(hdtype)))); + definitely_not_function |= (jl_is_leaf_type(hdtype) && !definitely_function); + + assert(!(definitely_function && definitely_not_function)); + + int last_depth = ctx->argDepth; + Value *result; + + if (definitely_not_function) { + if (f == NULL) { + f = jl_module_call_func(ctx->module); + Value *r = emit_known_call((jl_value_t*)f, + args-1, nargs+1, ctx, &theFptr, &f, expr); + assert(r == NULL); + assert(theFptr != NULL); + } + theF = literal_pointer_val((jl_value_t*)f); + result = emit_call_function_object(f, theF, theFptr, true, args-1, nargs+1, ctx); + } + else if (definitely_function) { + bool specialized = true; + if (theFptr == NULL) { + specialized = false; + Value *theFunc = emit_expr(args[0], ctx); +#ifdef JL_GC_MARKSWEEP + if (!headIsGlobal && (jl_is_expr(a0) || jl_is_lambda_info(a0))) { + make_gcroot(boxed(theFunc,ctx), ctx); + } +#endif + // extract pieces of the function object + // TODO: try extractvalue instead + theFptr = emit_nthptr_recast(theFunc, 1, tbaa_func, jl_pfptr_llvmt); + theF = theFunc; + } + else { + theF = literal_pointer_val((jl_value_t*)f); + } + result = emit_call_function_object(f, theF, theFptr, specialized, args, nargs, ctx); + } + else { + // either direct function, or use call(), based on run-time branch + + // emit "function" and arguments + int argStart = ctx->argDepth; + Value *theFunc = boxed(emit_expr(args[0], ctx), ctx); + make_gcroot(theFunc, ctx); + for(size_t i=0; i < nargs; i++) { + Value *anArg = emit_expr(args[i+1], ctx); + // put into argument space + make_gcroot(boxed(anArg, ctx, expr_type(args[i+1],ctx)), ctx); + } + + Value *isfunc = emit_is_function(theFunc, ctx); + BasicBlock *funcBB1 = BasicBlock::Create(getGlobalContext(),"isf", ctx->f); + BasicBlock *elseBB1 = BasicBlock::Create(getGlobalContext(),"notf"); + BasicBlock *mergeBB1 = BasicBlock::Create(getGlobalContext(),"mergef"); + builder.CreateCondBr(isfunc, funcBB1, elseBB1); + + builder.SetInsertPoint(funcBB1); + // is function + Value *myargs = Constant::getNullValue(jl_ppvalue_llvmt); + if (ctx->argTemp != NULL && nargs > 0) { + myargs = builder.CreateGEP(ctx->argTemp, + ConstantInt::get(T_size, argStart+1+ctx->argSpaceOffs)); + } + theFptr = emit_nthptr_recast(theFunc, 1, tbaa_func, jl_pfptr_llvmt); + Value *r1 = builder.CreateCall3(prepare_call(theFptr), theFunc, myargs, + ConstantInt::get(T_int32,nargs)); + builder.CreateBr(mergeBB1); + ctx->f->getBasicBlockList().push_back(elseBB1); + builder.SetInsertPoint(elseBB1); + // not function + myargs = builder.CreateGEP(ctx->argTemp, + ConstantInt::get(T_size, argStart+ctx->argSpaceOffs)); + Value *r2 = builder.CreateCall3(prepare_call(jlapplygeneric_func), + literal_pointer_val((jl_value_t*)jl_module_call_func(ctx->module)), + myargs, + ConstantInt::get(T_int32,nargs+1)); + builder.CreateBr(mergeBB1); + ctx->f->getBasicBlockList().push_back(mergeBB1); + builder.SetInsertPoint(mergeBB1); + PHINode *ph = builder.CreatePHI(jl_pvalue_llvmt, 2); + ph->addIncoming(r1, funcBB1); + ph->addIncoming(r2, elseBB1); + result = ph; + } ctx->argDepth = last_depth; return result; diff --git a/src/interpreter.c b/src/interpreter.c index 0f412890e27e9..6bfdf341f731b 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -53,15 +53,22 @@ DLLEXPORT jl_value_t *jl_interpret_toplevel_expr_in(jl_module_t *m, jl_value_t * } static jl_value_t *do_call(jl_function_t *f, jl_value_t **args, size_t nargs, - jl_value_t **locals, size_t nl) + jl_value_t *eval0, jl_value_t **locals, size_t nl) { jl_value_t **argv; JL_GC_PUSHARGS(argv, nargs+1); size_t i; argv[0] = (jl_value_t*)f; for(i=1; i < nargs+1; i++) argv[i] = NULL; - for(i=0; i < nargs; i++) - argv[i+1] = eval(args[i], locals, nl); + if (eval0) { /* 0-th argument has already been evaluated */ + argv[1] = eval0; + for(i=1; i < nargs; i++) + argv[i+1] = eval(args[i], locals, nl); + } + else { + for(i=0; i < nargs; i++) + argv[i+1] = eval(args[i], locals, nl); + } jl_value_t *result = jl_apply(f, &argv[1], nargs); JL_GC_POP(); return result; @@ -201,10 +208,10 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, size_t nl) } } jl_function_t *f = (jl_function_t*)eval(args[0], locals, nl); - if (!jl_is_func(f)) - jl_type_error("apply", (jl_value_t*)jl_function_type, - (jl_value_t*)f); - return do_call(f, &args[1], nargs-1, locals, nl); + if (jl_is_func(f)) + return do_call(f, &args[1], nargs-1, NULL, locals, nl); + else + return do_call(jl_module_call_func(jl_current_module), args, nargs, (jl_value_t*)f, locals, nl); } else if (ex->head == assign_sym) { jl_value_t *sym = args[0]; diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 4c216d7502ac3..da54ef8fd3c3e 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1469,9 +1469,9 @@ `((quote ,(cadr a)) ,(caddr a))) keys)))) (if (null? restkeys) - `(call (top kwcall) ,f ,(length keys) ,@keyargs + `(call (top kwcall) call ,(length keys) ,@keyargs (call (top Array) (top Any) ,(* 2 (length keys))) - ,@pa) + ,f ,@pa) (let ((container (gensy))) `(block (= ,container (call (top Array) (top Any) ,(* 2 (length keys)))) @@ -1491,8 +1491,8 @@ restkeys)) (if (call (top isempty) ,container) (call ,f ,@pa) - (call (top kwcall) ,f ,(length keys) ,@keyargs - ,container ,@pa)))))))) + (call (top kwcall) call ,(length keys) ,@keyargs + ,container ,f ,@pa)))))))) (define (expand-transposed-op e ops) (let ((a (caddr e)) @@ -1737,7 +1737,7 @@ (tuple-wrap (cdr a) '()))) (tuple-wrap (cdr a) (cons x run)))))) (expand-forms - `(call (top apply) ,f ,@(tuple-wrap argl '()))))) + `(call (top _apply) call ,f ,@(tuple-wrap argl '()))))) ((and (eq? (cadr e) '*) (length= e 4)) (expand-transposed-op diff --git a/src/julia.h b/src/julia.h index f12ccbd3ae884..2952851f27df2 100644 --- a/src/julia.h +++ b/src/julia.h @@ -255,6 +255,7 @@ typedef struct _jl_module_t { htable_t bindings; arraylist_t usings; // modules with all bindings potentially imported jl_array_t *constant_table; + jl_function_t *call_func; // cached lookup of `call` within this module } jl_module_t; typedef struct _jl_methlist_t { @@ -817,6 +818,7 @@ STATIC_INLINE jl_function_t *jl_get_function(jl_module_t *m, const char *name) } DLLEXPORT int jl_module_has_initializer(jl_module_t *m); DLLEXPORT void jl_module_run_initializer(jl_module_t *m); +jl_function_t *jl_module_call_func(jl_module_t *m); // eq hash tables DLLEXPORT jl_array_t *jl_eqtable_put(jl_array_t *h, void *key, void *val); diff --git a/src/module.c b/src/module.c index 41aa9513e707d..5f8ed958d4e14 100644 --- a/src/module.c +++ b/src/module.c @@ -22,6 +22,7 @@ jl_module_t *jl_new_module(jl_sym_t *name) assert(jl_is_symbol(name)); m->name = name; m->constant_table = NULL; + m->call_func = NULL; htable_new(&m->bindings, 0); arraylist_new(&m->usings, 0); if (jl_core_module) { @@ -446,6 +447,17 @@ void jl_module_run_initializer(jl_module_t *m) } } +jl_function_t *jl_module_call_func(jl_module_t *m) +{ + if (m->call_func == NULL) { + jl_function_t *cf = (jl_function_t*)jl_get_global(m, call_sym); + if (cf == NULL || !jl_is_function(cf)) + cf = jl_bottom_func; + m->call_func = cf; + } + return m->call_func; +} + #ifdef __cplusplus } #endif diff --git a/test/core.jl b/test/core.jl index ee7517b818b58..16048d465535c 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1882,3 +1882,20 @@ let ex = Expr(:(=), :(f8338(x;y=4)), :(x*y)) eval(ex) @test f8338(2) == 8 end + +# call overloading (#2403) +Base.call(x::Int, y::Int) = x + 3y +issue2403func(f) = f(7) +let x = 10 + @test x(3) == 19 + @test x((3,)...) == 19 + @test issue2403func(x) == 31 +end +type Issue2403 + x +end +Base.call(i::Issue2403, y) = i.x + 2y +let x = Issue2403(20) + @test x(3) == 26 + @test issue2403func(x) == 34 +end diff --git a/test/perf/perfgeneric.jl b/test/perf/perfgeneric.jl index 4ebb96f338096..203e8f9236a42 100644 --- a/test/perf/perfgeneric.jl +++ b/test/perf/perfgeneric.jl @@ -1,7 +1,6 @@ #Generic benchmark driver for (testfunc, testname, longtestname, problem_sizes) in testdata for (n, t, size) in problem_sizes - @timeit apply(testfunc, n, t) string(testname,"_",size) string(uppercase(size[1]),size[2:end]," ",longtestname," test") + @timeit testfunc(n, t) string(testname,"_",size) string(uppercase(size[1]),size[2:end]," ",longtestname," test") end end -