From 1f527b7a4799fbfd45ee5e7dccead01de864b72c Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Tue, 12 Aug 2014 14:16:51 -0400 Subject: [PATCH 01/10] initial implementation of call overloading: f(x...) gets turned into call(f, x...) as a fallback (if f is not a function); to do: update inference, fallback to call when f is a type --- base/boot.jl | 5 ++++- src/alloc.c | 1 + src/codegen.cpp | 31 ++++++++++++++++++------------- src/init.c | 2 ++ src/interpreter.c | 22 +++++++++++++++------- src/julia_internal.h | 2 ++ 6 files changed, 42 insertions(+), 21 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index 72d74827a1129..1a4a764736fdd 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -135,7 +135,7 @@ export Expr, GotoNode, LabelNode, LineNumberNode, QuoteNode, SymbolNode, TopNode, GetfieldNode, NewvarNode, # object model functions - apply, fieldtype, getfield, setfield!, yieldto, throw, tuple, is, ===, isdefined, + apply, fieldtype, getfield, setfield!, yieldto, throw, tuple, is, ===, isdefined, call, # arraylen, arrayref, arrayset, arraysize, tuplelen, tupleref, convert_default, # kwcall, # type reflection @@ -243,3 +243,6 @@ end typealias ByteString Union(ASCIIString,UTF8String) include(fname::ByteString) = ccall(:jl_load_, Any, (Any,), fname) + +call(f::Function, args...) = f(args...) +call{T}(::Type{T}, x) = convert(T, x) diff --git a/src/alloc.c b/src/alloc.c index 901267b6fe9d3..28c5155571bc4 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -66,6 +66,7 @@ jl_value_t *jl_undefref_exception; jl_value_t *jl_interrupt_exception; jl_value_t *jl_bounds_exception; jl_value_t *jl_memory_exception; +jl_function_t *jl_call_func; jl_sym_t *call_sym; jl_sym_t *dots_sym; jl_sym_t *call1_sym; jl_sym_t *module_sym; diff --git a/src/codegen.cpp b/src/codegen.cpp index 132de23c44ce6..c487ef28c75b2 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2214,25 +2214,30 @@ static Value *emit_call(jl_value_t **args, size_t arglen, jl_codectx_t *ctx, 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; + headIsGlobal = true; + Value *result = emit_known_call((jl_value_t*)jl_call_func, + --args, ++nargs, ctx, + &theFptr, &f, expr); + if (result != NULL) return result; + theF = literal_pointer_val((jl_value_t*)f); } + else { #ifdef JL_GC_MARKSWEEP - if (!headIsGlobal && (jl_is_expr(a0) || jl_is_lambda_info(a0))) { + 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)))) { + 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 = builder.CreateBitCast(emit_nthptr(theFunc, 1, tbaa_func), jl_fptr_llvmt); + theF = theFunc; } - // extract pieces of the function object - // TODO: try extractvalue instead - theFptr = builder.CreateBitCast(emit_nthptr(theFunc, 1, tbaa_func), jl_fptr_llvmt); - theF = theFunc; } else { theF = literal_pointer_val((jl_value_t*)f); diff --git a/src/init.c b/src/init.c index 9ee349a8dc5bc..59b0bba872093 100644 --- a/src/init.c +++ b/src/init.c @@ -1090,6 +1090,8 @@ void jl_get_builtin_hooks(void) jl_array_uint8_type = jl_apply_type((jl_value_t*)jl_array_type, jl_tuple2(jl_uint8_type, jl_box_long(1))); + + jl_call_func = (jl_function_t*)core("call"); } DLLEXPORT void jl_get_system_hooks(void) diff --git a/src/interpreter.c b/src/interpreter.c index be4e1c6d48c7b..0b0bac92fe1e3 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,11 @@ 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_call_func, 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_internal.h b/src/julia_internal.h index 157740f37f110..cff0a90698935 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -129,6 +129,8 @@ DLLEXPORT void jl_raise_debugger(void); // Returns time in nanosec DLLEXPORT uint64_t jl_hrtime(void); +extern DLLEXPORT jl_function_t *jl_call_func; + #ifdef __cplusplus } #endif From 4a58fa499a4cf7238cf734f33d1f531a0190fb7c Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Tue, 12 Aug 2014 15:19:36 -0400 Subject: [PATCH 02/10] make sure call passes through keywords --- base/boot.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/boot.jl b/base/boot.jl index 1a4a764736fdd..490d8c8426354 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -244,5 +244,5 @@ typealias ByteString Union(ASCIIString,UTF8String) include(fname::ByteString) = ccall(:jl_load_, Any, (Any,), fname) -call(f::Function, args...) = f(args...) +call(f::Function, args...; kws...) = f(args...; kws...) call{T}(::Type{T}, x) = convert(T, x) From 5b0aedd38e73c0bc3a071c46b9f459e4d92166b9 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Tue, 12 Aug 2014 16:03:41 -0400 Subject: [PATCH 03/10] fix call overloading for type constructors, add some tests --- base/base.jl | 5 ++++- base/boot.jl | 1 - src/codegen.cpp | 11 +++++++++-- src/interpreter.c | 2 +- test/core.jl | 18 ++++++++++++++++++ 5 files changed, 32 insertions(+), 5 deletions(-) diff --git a/base/base.jl b/base/base.jl index 05b8c2a9e20ed..a726c55202145 100644 --- a/base/base.jl +++ b/base/base.jl @@ -4,7 +4,7 @@ using Core: Intrinsics, arraylen, arrayref, arrayset, arraysize, tuplelen, tupleref, convert_default, kwcall, typeassert, apply_type -import Core.Array # to add methods +import Core: Array, call # to add methods const NonTupleType = Union(DataType,UnionType,TypeConstructor) @@ -15,6 +15,9 @@ convert(T, x) = convert_default(T, x, convert) convert(::(), ::()) = () convert(::Type{Tuple}, x::Tuple) = x +# allow convert to be called as if it were a single-argument constructor +call{T}(::Type{T}, x) = convert(T, x) + argtail(x, rest...) = rest tupletail(x::Tuple) = argtail(x...) diff --git a/base/boot.jl b/base/boot.jl index 490d8c8426354..2f964530110a7 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -245,4 +245,3 @@ typealias ByteString Union(ASCIIString,UTF8String) include(fname::ByteString) = ccall(:jl_load_, Any, (Any,), fname) call(f::Function, args...; kws...) = f(args...; kws...) -call{T}(::Type{T}, x) = convert(T, x) diff --git a/src/codegen.cpp b/src/codegen.cpp index c487ef28c75b2..a2348f7b1e01a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2201,9 +2201,16 @@ static Value *emit_call(jl_value_t **args, size_t arglen, jl_codectx_t *ctx, jl_function_t *f = (jl_function_t*)static_eval(a0, ctx, true); if (f != NULL) { + Value *result; headIsGlobal = true; - Value *result = emit_known_call((jl_value_t*)f, args, nargs, ctx, - &theFptr, &f, expr); + if (f->fptr != jl_f_no_function) { + result = emit_known_call((jl_value_t*)f, args, nargs, ctx, + &theFptr, &f, expr); + } + else { + result = emit_known_call((jl_value_t*)jl_call_func, + --args, ++nargs, ctx, &theFptr, &f, expr); + } if (result != NULL) return result; } bool specialized = true; diff --git a/src/interpreter.c b/src/interpreter.c index 0b0bac92fe1e3..7e006cae03761 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -208,7 +208,7 @@ 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)) + if (jl_is_func(f) && f->fptr != jl_f_no_function) return do_call(f, &args[1], nargs-1, NULL, locals, nl); else return do_call(jl_call_func, args, nargs, (jl_value_t*)f, diff --git a/test/core.jl b/test/core.jl index a04bf1443ac20..3e7ca148a8d9e 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1829,3 +1829,21 @@ end # issue #7582 aₜ = "a variable using Unicode 6" + +# call and constructor overloading (#1470, #2403) +typealias FooInt32 Int32 +@test isa(FooInt32(3), FooInt32) +Base.call(x::Int, y::Int) = x + 3y +issue2403func(f) = f(7) +let x = 10 + @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 -- FIXME +end From 0df037e87c774bb34c5054f1bbede2ffe27a28ab Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 14 Aug 2014 11:53:03 -0400 Subject: [PATCH 04/10] support generic call in apply(f, ...) --- src/builtins.c | 45 +++++++++++++++++++++++++++++++++++---------- test/core.jl | 1 + 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/builtins.c b/src/builtins.c index c39e0dc14ff9f..77de8b2504293 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -256,9 +256,15 @@ extern size_t jl_page_size; JL_CALLABLE(jl_f_apply) { JL_NARGSV(apply, 1); - JL_TYPECHK(apply, function, args[0]); + jl_function_t *f; + if (jl_is_function(args[0])) + f = (jl_function_t*)args[0]; + else { /* do generic call(args...) instead */ + f = jl_call_func; + ++nargs; --args; /* args[0] becomes 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])) { @@ -273,7 +279,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])); } } @@ -301,7 +307,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; } @@ -328,7 +334,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; } @@ -339,8 +345,16 @@ JL_CALLABLE(jl_f_kwcall) { if (nargs < 3) 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_value_t *args0; + if (jl_is_function(args[0])) { + f = (jl_function_t*)args[0]; + args0 = NULL; + } + else { /* do generic call(args...; kws...) instead */ + f = jl_call_func; + args0 = args[0]; + } if (f->fptr == jl_f_ctor_trampoline) jl_add_constructors((jl_datatype_t*)f); if (!jl_is_gf(f)) @@ -360,13 +374,24 @@ JL_CALLABLE(jl_f_kwcall) jl_cellset(container, i+1, args[2+i+1]); } + args += pa-1; + nargs -= pa-1; + if (args0) { + jl_value_t **newargs = (jl_value_t**)alloca((nargs+1) * sizeof(jl_value_t*)); + newargs[0] = args[0]; + newargs[1] = args0; /* original 0th argument = "function" */ + memcpy(newargs+2, args+1, sizeof(jl_value_t*) * (nargs-1)); + args = newargs; + nargs += 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 ----------------------------------------------------------------------- diff --git a/test/core.jl b/test/core.jl index 3e7ca148a8d9e..cf782197d8d07 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1837,6 +1837,7 @@ Base.call(x::Int, y::Int) = x + 3y issue2403func(f) = f(7) let x = 10 @test x(3) == 19 + @test apply(x, (3,)) == 19 @test issue2403func(x) == 31 end type Issue2403 From 762a7279a40e4eababa14b29b704e7d057b6f347 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 14 Aug 2014 12:15:22 -0400 Subject: [PATCH 05/10] (broken) patch to eliminate emit_func_check --- src/cgutils.cpp | 23 ----------------------- src/codegen.cpp | 14 ++++++-------- 2 files changed, 6 insertions(+), 31 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index b341b84ee81fe..ba5f9b264c5d3 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -824,29 +824,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 a2348f7b1e01a..5c33908080823 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2219,8 +2219,12 @@ static Value *emit_call(jl_value_t **args, size_t arglen, jl_codectx_t *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 + 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)))) || + jl_is_tuple(hdtype) || theFunc->getType() != jl_pvalue_llvmt) { + // it may not be a function, use call(f, ...) instead headIsGlobal = true; Value *result = emit_known_call((jl_value_t*)jl_call_func, --args, ++nargs, ctx, @@ -2234,12 +2238,6 @@ static Value *emit_call(jl_value_t **args, size_t arglen, jl_codectx_t *ctx, 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 = builder.CreateBitCast(emit_nthptr(theFunc, 1, tbaa_func), jl_fptr_llvmt); From b430c48e46c875306c4baaf00b44d991a9fe1fe3 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 1 Sep 2014 14:47:04 -0400 Subject: [PATCH 06/10] get basic call overloading working with 1 bootstrap stage --- base/base.jl | 2 +- base/boot.jl | 4 +- src/codegen.cpp | 208 ++++++++++++++++++++++++++++++++-------------- src/init.c | 3 +- src/interpreter.c | 5 +- test/core.jl | 6 +- 6 files changed, 151 insertions(+), 77 deletions(-) diff --git a/base/base.jl b/base/base.jl index a726c55202145..05dbba7b4c1eb 100644 --- a/base/base.jl +++ b/base/base.jl @@ -4,7 +4,7 @@ using Core: Intrinsics, arraylen, arrayref, arrayset, arraysize, tuplelen, tupleref, convert_default, kwcall, typeassert, apply_type -import Core: Array, call # to add methods +import Core: Array # to add methods const NonTupleType = Union(DataType,UnionType,TypeConstructor) diff --git a/base/boot.jl b/base/boot.jl index 2f964530110a7..72d74827a1129 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -135,7 +135,7 @@ export Expr, GotoNode, LabelNode, LineNumberNode, QuoteNode, SymbolNode, TopNode, GetfieldNode, NewvarNode, # object model functions - apply, fieldtype, getfield, setfield!, yieldto, throw, tuple, is, ===, isdefined, call, + apply, fieldtype, getfield, setfield!, yieldto, throw, tuple, is, ===, isdefined, # arraylen, arrayref, arrayset, arraysize, tuplelen, tupleref, convert_default, # kwcall, # type reflection @@ -243,5 +243,3 @@ end typealias ByteString Union(ASCIIString,UTF8String) include(fname::ByteString) = ccall(:jl_load_, Any, (Any,), fname) - -call(f::Function, args...; kws...) = f(args...; kws...) diff --git a/src/codegen.cpp b/src/codegen.cpp index 5c33908080823..afad7f79405a5 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2176,85 +2176,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) { - Value *result; - headIsGlobal = true; - if (f->fptr != jl_f_no_function) { - result = emit_known_call((jl_value_t*)f, args, nargs, ctx, - &theFptr, &f, expr); - } - else { - result = emit_known_call((jl_value_t*)jl_call_func, - --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 ((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)))) || - jl_is_tuple(hdtype) || theFunc->getType() != jl_pvalue_llvmt) { - // it may not be a function, use call(f, ...) instead - headIsGlobal = true; - Value *result = emit_known_call((jl_value_t*)jl_call_func, - --args, ++nargs, ctx, - &theFptr, &f, expr); - if (result != NULL) return result; - theF = literal_pointer_val((jl_value_t*)f); - } - else { -#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 = builder.CreateBitCast(emit_nthptr(theFunc, 1, tbaa_func), jl_fptr_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); @@ -2277,8 +2221,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++; @@ -2290,6 +2234,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_call_func, + 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_call_func; + Value *r = emit_known_call((jl_value_t*)jl_call_func, + 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 = builder.CreateBitCast(emit_nthptr(theFunc, 1, tbaa_func), jl_fptr_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 = builder.CreateBitCast(emit_nthptr(theFunc, 1, tbaa_func), jl_fptr_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_call_func), + 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/init.c b/src/init.c index 59b0bba872093..8bb89eb632344 100644 --- a/src/init.c +++ b/src/init.c @@ -1090,8 +1090,6 @@ void jl_get_builtin_hooks(void) jl_array_uint8_type = jl_apply_type((jl_value_t*)jl_array_type, jl_tuple2(jl_uint8_type, jl_box_long(1))); - - jl_call_func = (jl_function_t*)core("call"); } DLLEXPORT void jl_get_system_hooks(void) @@ -1103,6 +1101,7 @@ DLLEXPORT void jl_get_system_hooks(void) jl_methoderror_type = (jl_datatype_t*)basemod("MethodError"); jl_loaderror_type = (jl_datatype_t*)basemod("LoadError"); jl_weakref_type = (jl_datatype_t*)basemod("WeakRef"); + jl_call_func = (jl_function_t*)basemod("call"); } DLLEXPORT void jl_exit_on_sigint(int on) {exit_on_sigint = on;} diff --git a/src/interpreter.c b/src/interpreter.c index 7e006cae03761..8d7f312771271 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -208,11 +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) && f->fptr != jl_f_no_function) + if (jl_is_func(f)) return do_call(f, &args[1], nargs-1, NULL, locals, nl); else - return do_call(jl_call_func, args, nargs, (jl_value_t*)f, - locals, nl); + return do_call(jl_call_func, args, nargs, (jl_value_t*)f, locals, nl); } else if (ex->head == assign_sym) { jl_value_t *sym = args[0]; diff --git a/test/core.jl b/test/core.jl index cf782197d8d07..9d95a2b4eebd8 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1830,9 +1830,7 @@ end # issue #7582 aₜ = "a variable using Unicode 6" -# call and constructor overloading (#1470, #2403) -typealias FooInt32 Int32 -@test isa(FooInt32(3), FooInt32) +# call overloading (#2403) Base.call(x::Int, y::Int) = x + 3y issue2403func(f) = f(7) let x = 10 @@ -1846,5 +1844,5 @@ end Base.call(i::Issue2403, y) = i.x + 2y let x = Issue2403(20) @test x(3) == 26 - # @test issue2403func(x) == 34 -- FIXME + @test issue2403func(x) == 34 end From bd955f2d08dfe295056c55aa9677d1cc7039188a Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 1 Sep 2014 15:01:56 -0400 Subject: [PATCH 07/10] update emit_call for jl_fptr_llvmt => jl_pfptr_llvmt change --- src/codegen.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index e724878ff9124..fd40638c3f427 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2387,7 +2387,7 @@ static Value *emit_call(jl_value_t **args, size_t arglen, jl_codectx_t *ctx, jl_ #endif // extract pieces of the function object // TODO: try extractvalue instead - theFptr = builder.CreateBitCast(emit_nthptr(theFunc, 1, tbaa_func), jl_fptr_llvmt); + theFptr = emit_nthptr_recast(theFunc, 1, tbaa_func, jl_pfptr_llvmt); theF = theFunc; } else { @@ -2421,7 +2421,7 @@ static Value *emit_call(jl_value_t **args, size_t arglen, jl_codectx_t *ctx, jl_ myargs = builder.CreateGEP(ctx->argTemp, ConstantInt::get(T_size, argStart+1+ctx->argSpaceOffs)); } - theFptr = builder.CreateBitCast(emit_nthptr(theFunc, 1, tbaa_func), jl_fptr_llvmt); + 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); From aeae50f4e84870020bb4d65d8a26d7ba7e08cb69 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 9 Oct 2014 20:22:27 -0400 Subject: [PATCH 08/10] some work on call overloading - look up call in the right module instead of using jl_call_func - some inference support for call overloading - pass `call` to _apply and kwcall so they use the containing module's definition - deprecate explicit apply(), rename the builtin to _apply --- base/base.jl | 5 +- base/boot.jl | 12 +-- base/deprecated.jl | 6 ++ base/exports.jl | 1 + base/inference.jl | 159 +++++++++++++++++++++++++-------------- base/linalg/bidiag.jl | 4 +- base/operators.jl | 5 +- base/sysimg.jl | 2 +- src/alloc.c | 1 - src/builtins.c | 56 +++++++------- src/codegen.cpp | 8 +- src/init.c | 1 - src/interpreter.c | 2 +- src/julia-syntax.scm | 10 +-- src/julia.h | 2 + src/julia_internal.h | 2 - src/module.c | 12 +++ test/core.jl | 2 +- test/perf/perfgeneric.jl | 3 +- 19 files changed, 182 insertions(+), 111 deletions(-) diff --git a/base/base.jl b/base/base.jl index 47d0e7bd1343a..4eab0c592c5cc 100644 --- a/base/base.jl +++ b/base/base.jl @@ -11,8 +11,11 @@ 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{T}, x) = convert(T, x) +# 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 466e270d5b389..aeb537b761b7f 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -185,3 +185,9 @@ 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 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 0ecb096029962..e022c01a3e478 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,53 +785,80 @@ 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 + if isa(af,Function) || isa(af,DataType) + aargtypes = map(to_tuple_of_Types, argtypes[3:end]) + return abstract_apply(af, aargtypes, vtypes, sv, e) + else + call_func = isconstantfunc(fargs[1]) + if !is(call_func,false) + aargtypes = map(to_tuple_of_Types, argtypes[2:end]) + aargtypes[1] = (aargtypes[1],) # don't splat "function" + return abstract_apply(_ieval(call_func), aargtypes, vtypes, sv, e) end - return Tuple end - if is(af,kwcall) - return Any + else + # non-constant function + if !(Function <: a2type) && typeintersect(DataType, a2type)==Bottom + # would definitely use call() + call_func = isconstantfunc(fargs[1]) + if !is(call_func,false) + aargtypes = map(to_tuple_of_Types, argtypes[2:end]) + aargtypes[1] = (aargtypes[1],) # don't splat "function" + return abstract_apply(_ieval(call_func), aargtypes, vtypes, sv, e) + end end - # apply known function with unknown args => f(Any...) - return abstract_call(af, (), Tuple, vtypes, sv, ()) end end if isgeneric(f) @@ -849,7 +874,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 +884,32 @@ 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) + call_func = isconstantfunc(:call, sv) + if !is(call_func,false) + return abstract_call(_ieval(call_func), e.args, tuple(typeof(f),argtypes...), vtypes, sv, e) + end + end rt = builtin_tfunction(f, fargs, argtypes) #print("=> ", rt, "\n") return rt @@ -924,6 +956,12 @@ function abstract_eval_call(e, vtypes, sv::StaticVarInfo) return st end end + if !(Function <: ft) && typeintersect(DataType, ft)==Bottom + call_func = isconstantfunc(:call, sv) + if !is(call_func,false) + return abstract_call(_ieval(call_func), e.args, tuple(ft,argtypes...), vtypes, sv, e) + end + end return Any end #print("call ", e.args[1], argtypes, " ") @@ -1313,6 +1351,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...) @@ -2648,7 +2688,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, .^) @@ -2683,34 +2730,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] = { QuoteNode(x) for x in aarg } + newargs[i-3] = { 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] = { mk_tupleref(aarg,j,t[j]) for j=1:length(t) } + newargs[i-3] = { mk_tupleref(aarg,j,t[j]) for j=1:length(t) } else # not all args expandable return (e,stmts) end end - e.args = [{e.args[2]}, newargs...] + e.args = [{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/alloc.c b/src/alloc.c index 2eb1975728eae..daaa3f64ac007 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -67,7 +67,6 @@ jl_value_t *jl_undefref_exception; jl_value_t *jl_interrupt_exception; jl_value_t *jl_bounds_exception; jl_value_t *jl_memory_exception; -jl_function_t *jl_call_func; jl_sym_t *call_sym; jl_sym_t *dots_sym; jl_sym_t *call1_sym; jl_sym_t *module_sym; diff --git a/src/builtins.c b/src/builtins.c index 796f9bfa2fa14..4adb71c505160 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -284,13 +284,18 @@ extern size_t jl_page_size; JL_CALLABLE(jl_f_apply) { - JL_NARGSV(apply, 1); + JL_NARGSV(apply, 2); jl_function_t *f; - if (jl_is_function(args[0])) - f = (jl_function_t*)args[0]; + 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 = jl_call_func; - ++nargs; --args; /* args[0] becomes args[1] */ + f = call_func; + // protect "function" arg from splicing + args[1] = (jl_value_t*)jl_tuple1(args[1]); } if (nargs == 2) { if (f->fptr == &jl_f_tuple) { @@ -372,18 +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_function_t *f; - jl_value_t *args0; - if (jl_is_function(args[0])) { - f = (jl_function_t*)args[0]; - args0 = NULL; + 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 { /* do generic call(args...; kws...) instead */ - f = jl_call_func; - args0 = args[0]; + 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)) @@ -394,10 +409,6 @@ 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]); @@ -405,15 +416,6 @@ JL_CALLABLE(jl_f_kwcall) args += pa-1; nargs -= pa-1; - if (args0) { - jl_value_t **newargs = (jl_value_t**)alloca((nargs+1) * sizeof(jl_value_t*)); - newargs[0] = args[0]; - newargs[1] = args0; /* original 0th argument = "function" */ - memcpy(newargs+2, args+1, sizeof(jl_value_t*) * (nargs-1)); - args = newargs; - nargs += 1; - } - assert(jl_is_gf(sorter)); jl_function_t *m = jl_method_lookup((jl_methtable_t*)sorter->env, args, nargs, 1); if (m == jl_bottom_func) { @@ -1055,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/codegen.cpp b/src/codegen.cpp index 9a86481f9e805..b711ad4284d5f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2357,7 +2357,7 @@ static Value *emit_call(jl_value_t **args, size_t arglen, jl_codectx_t *ctx, jl_ assert(!jl_typeis(f,jl_intrinsic_type) || result!=NULL); } else { - result = emit_known_call((jl_value_t*)jl_call_func, + 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; @@ -2377,8 +2377,8 @@ static Value *emit_call(jl_value_t **args, size_t arglen, jl_codectx_t *ctx, jl_ if (definitely_not_function) { if (f == NULL) { - f = jl_call_func; - Value *r = emit_known_call((jl_value_t*)jl_call_func, + 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); @@ -2442,7 +2442,7 @@ static Value *emit_call(jl_value_t **args, size_t arglen, jl_codectx_t *ctx, jl_ 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_call_func), + literal_pointer_val((jl_value_t*)jl_module_call_func(ctx->module)), myargs, ConstantInt::get(T_int32,nargs+1)); builder.CreateBr(mergeBB1); diff --git a/src/init.c b/src/init.c index a327370eaee99..02dee85ab1ab7 100644 --- a/src/init.c +++ b/src/init.c @@ -1121,7 +1121,6 @@ DLLEXPORT void jl_get_system_hooks(void) jl_methoderror_type = (jl_datatype_t*)basemod("MethodError"); jl_loaderror_type = (jl_datatype_t*)basemod("LoadError"); jl_weakref_type = (jl_datatype_t*)basemod("WeakRef"); - jl_call_func = (jl_function_t*)basemod("call"); } DLLEXPORT void jl_exit_on_sigint(int on) {exit_on_sigint = on;} diff --git a/src/interpreter.c b/src/interpreter.c index 1fbb6410dd7c3..6bfdf341f731b 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -211,7 +211,7 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, size_t nl) if (jl_is_func(f)) return do_call(f, &args[1], nargs-1, NULL, locals, nl); else - return do_call(jl_call_func, args, nargs, (jl_value_t*)f, locals, nl); + 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/julia_internal.h b/src/julia_internal.h index cff0a90698935..157740f37f110 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -129,8 +129,6 @@ DLLEXPORT void jl_raise_debugger(void); // Returns time in nanosec DLLEXPORT uint64_t jl_hrtime(void); -extern DLLEXPORT jl_function_t *jl_call_func; - #ifdef __cplusplus } #endif diff --git a/src/module.c b/src/module.c index 41aa9513e707d..328024264535e 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)) + return 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 e637d2358a701..55fe81148f8fe 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1885,7 +1885,7 @@ Base.call(x::Int, y::Int) = x + 3y issue2403func(f) = f(7) let x = 10 @test x(3) == 19 - @test apply(x, (3,)) == 19 + @test x((3,)...) == 19 @test issue2403func(x) == 31 end type Issue2403 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 - From 24637047860ab7f4cfc0c0d51dd523f6aeca3dc1 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 9 Oct 2014 21:57:20 -0400 Subject: [PATCH 09/10] fix codegen for changes to apply() for call overloading --- src/codegen.cpp | 12 ++++++------ src/module.c | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index b711ad4284d5f..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); diff --git a/src/module.c b/src/module.c index 328024264535e..5f8ed958d4e14 100644 --- a/src/module.c +++ b/src/module.c @@ -452,7 +452,7 @@ 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)) - return jl_bottom_func; + cf = jl_bottom_func; m->call_func = cf; } return m->call_func; From 5b03766bf728692fdb1563447dab5601b06b78a0 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 9 Oct 2014 23:44:25 -0400 Subject: [PATCH 10/10] temporarily restore inference/startup performance --- base/inference.jl | 45 +++++++++++++++++---------------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/base/inference.jl b/base/inference.jl index 81588a8603b6d..65d318fdd034e 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -840,26 +840,19 @@ function abstract_call(f, fargs, argtypes, vtypes, sv::StaticVarInfo, e) if isa(af,Function) || isa(af,DataType) aargtypes = map(to_tuple_of_Types, argtypes[3:end]) return abstract_apply(af, aargtypes, vtypes, sv, e) - else - call_func = isconstantfunc(fargs[1]) - if !is(call_func,false) - aargtypes = map(to_tuple_of_Types, argtypes[2:end]) - aargtypes[1] = (aargtypes[1],) # don't splat "function" - return abstract_apply(_ieval(call_func), aargtypes, vtypes, sv, e) - end - end - else - # non-constant function - if !(Function <: a2type) && typeintersect(DataType, a2type)==Bottom - # would definitely use call() - call_func = isconstantfunc(fargs[1]) - if !is(call_func,false) - aargtypes = map(to_tuple_of_Types, argtypes[2:end]) - aargtypes[1] = (aargtypes[1],) # don't splat "function" - return abstract_apply(_ieval(call_func), aargtypes, vtypes, sv, e) - end 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) @@ -904,11 +897,9 @@ function abstract_call(f, fargs, argtypes, vtypes, sv::StaticVarInfo, e) # TODO: call() case return Any end - if !isa(f,Function) && !isa(f,DataType) && !isa(f,IntrinsicFunction) - call_func = isconstantfunc(:call, sv) - if !is(call_func,false) - return abstract_call(_ieval(call_func), e.args, tuple(typeof(f),argtypes...), vtypes, sv, e) - 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") @@ -956,11 +947,9 @@ function abstract_eval_call(e, vtypes, sv::StaticVarInfo) return st end end - if !(Function <: ft) && typeintersect(DataType, ft)==Bottom - call_func = isconstantfunc(:call, sv) - if !is(call_func,false) - return abstract_call(_ieval(call_func), e.args, tuple(ft,argtypes...), vtypes, sv, e) - 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