diff --git a/base/inference.jl b/base/inference.jl index c74697d6b62a2..eb228bc2fd4b9 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -977,10 +977,8 @@ function abstract_call(f::ANY, fargs, argtypes::Vector{Any}, vtypes::VarTable, s elseif is(f,Core.kwfunc) if length(fargs) == 2 ft = widenconst(argtypes[2]) - if isa(ft,DataType) && !ft.abstract - if isdefined(ft.name.mt, :kwsorter) - return typeof(ft.name.mt.kwsorter) - end + if isa(ft,DataType) && isdefined(ft.name, :mt) && isdefined(ft.name.mt, :kwsorter) + return Const(ft.name.mt.kwsorter) end end return Any @@ -1017,7 +1015,7 @@ function abstract_call(f::ANY, fargs, argtypes::Vector{Any}, vtypes::VarTable, s return abstract_call_gf_by_type(f, atype, sv) end -function abstract_eval_call(e, vtypes::VarTable, sv::InferenceState) +function abstract_eval_call(e::Expr, vtypes::VarTable, sv::InferenceState) argtypes = Any[abstract_eval(a, vtypes, sv) for a in e.args] #print("call ", e.args[1], argtypes, "\n\n") for x in argtypes @@ -1548,16 +1546,17 @@ function typeinf_ext(linfo::LambdaInfo) # This case occurs when the IR for a function has been deleted. # `code` will be a newly-created LambdaInfo, and we need to copy its # contents to the existing one to copy the info to the method cache. - linfo.inferred = true - linfo.inInference = false + linfo.inInference = true linfo.code = code.code linfo.slotnames = code.slotnames linfo.slottypes = code.slottypes linfo.slotflags = code.slotflags linfo.ssavaluetypes = code.ssavaluetypes - linfo.rettype = code.rettype linfo.pure = code.pure linfo.inlineable = code.inlineable + ccall(:jl_set_lambda_rettype, Void, (Any, Any), linfo, code.rettype) + linfo.inferred = true + linfo.inInference = false end return code else @@ -1931,15 +1930,15 @@ function finish(me::InferenceState) # determine and cache inlineability me.linfo.inlineable = isinlineable(me.linfo) - me.linfo.inferred = true - me.linfo.inInference = false - me.linfo.pure = ispure if isdefined(me.linfo, :def) # compress code for non-toplevel thunks compressedtree = ccall(:jl_compress_ast, Any, (Any,Any), me.linfo, me.linfo.code) me.linfo.code = compressedtree end - me.linfo.rettype = me.bestguess + me.linfo.pure = ispure + ccall(:jl_set_lambda_rettype, Void, (Any, Any), me.linfo, me.bestguess) + me.linfo.inferred = true + me.linfo.inInference = false # lazy-delete the item from active for several reasons: # efficiency, correctness, and recursion-safety @@ -2310,9 +2309,10 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference end # special-case inliners for known pure functions that compute types if isType(e.typ) && !has_typevars(e.typ.parameters[1],true) - if (is(f,apply_type) || is(f,fieldtype) || is(f,typeof) || + if (is(f, apply_type) || is(f, fieldtype) || is(f, typeof) || istopfunction(topmod, f, :typejoin) || istopfunction(topmod, f, :promote_type)) + # XXX: compute effect_free for the actual arguments if length(argexprs) < 2 || effect_free(argexprs[2], sv, true) return (e.typ.parameters[1],()) else @@ -2320,17 +2320,36 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference end end end - if isa(f,IntrinsicFunction) || ft ⊑ IntrinsicFunction + if is(f, Core.kwfunc) && length(argexprs) == 2 && isa(e.typ, Const) + if effect_free(argexprs[2], sv, true) + return (e.typ.val, ()) + else + return (e.typ.val, Any[argexprs[2]]) + end + end + if isa(f, IntrinsicFunction) || ft ⊑ IntrinsicFunction || + isa(f, Builtin) || ft ⊑ Builtin return NF end - atype = argtypes_to_type(atypes) - if length(atype.parameters) - 1 > MAX_TUPLETYPE_LEN - atype = limit_tuple_type(atype) + atype_unlimited = argtypes_to_type(atypes) + if length(atype_unlimited.parameters) - 1 > MAX_TUPLETYPE_LEN + atype = limit_tuple_type(atype_unlimited) + else + atype = atype_unlimited + end + function invoke_NF() + # converts a :call to :invoke + cache_linfo = ccall(:jl_get_spec_lambda, Any, (Any,), atype_unlimited) + if cache_linfo !== nothing + e.head = :invoke + unshift!(e.args, cache_linfo) + end + return NF end meth = _methods_by_ftype(atype, 1) if meth === false || length(meth) != 1 - return NF + return invoke_NF() end meth = meth[1]::SimpleVector metharg = meth[1]::Type @@ -2364,7 +2383,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference if !inline_incompletematch_allowed || !isdefined(Main,:Base) # provide global disable if this optimization is not desirable # need Main.Base defined for MethodError - return NF + return invoke_NF() end end @@ -2395,9 +2414,10 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference # end (linfo, ty, inferred) = typeinf(method, metharg, methsp, false) - if !inferred || linfo === nothing - return NF - elseif !linfo.inlineable + if linfo === nothing || !inferred + return invoke_NF() + end + if !linfo.inlineable # TODO #= if incompletematch @@ -2420,15 +2440,15 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference body.args = Any[Expr(:return, newcall)] ast = Expr(:lambda, newnames, Any[[], locals, [], 0], body) else - return NF + return invoke_NF() end =# - return NF + return invoke_NF() elseif linfo.code === nothing (linfo, ty, inferred) = typeinf(method, metharg, methsp, true) end - if linfo === nothing || !inferred || !linfo.inlineable - return NF + if linfo === nothing || !inferred || !linfo.inlineable || (ast = linfo.code) === nothing + return invoke_NF() end na = linfo.nargs @@ -2468,10 +2488,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference methargs = metharg.parameters nm = length(methargs) - ast = linfo.code - if ast === nothing - return NF - elseif !isa(ast,Array{Any,1}) + if !isa(ast, Array{Any,1}) ast = ccall(:jl_uncompress_ast, Any, (Any,Any), linfo, ast) else ast = copy_exprargs(ast) diff --git a/base/show.jl b/base/show.jl index 492dda654f4b4..74ea4ea153449 100644 --- a/base/show.jl +++ b/base/show.jl @@ -276,7 +276,15 @@ end function show(io::IO, l::LambdaInfo) if isdefined(l, :def) - println(io, "LambdaInfo for ", l.def.name) + if (l === l.def.lambda_template) + print(io, "LambdaInfo template for ") + show(io, l.def) + println(io) + else + print(io, "LambdaInfo for ") + show_lambda_types(io, l.specTypes.parameters) + println(io) + end else println(io, "Toplevel LambdaInfo thunk") end @@ -946,11 +954,40 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) show(io, ex.head) for arg in args print(io, ", ") - show(io, arg) + if isa(arg, LambdaInfo) && isdefined(arg, :specTypes) + show_lambda_types(io, arg.specTypes.parameters) + else + show(io, arg) + end end print(io, "))") end show_type && show_expr_type(io, ex.typ, emphstate) + nothing +end + +function show_lambda_types(io::IO, sig::SimpleVector) + # print a method signature tuple + ft = sig[1] + if ft <: Function && isempty(ft.parameters) && + isdefined(ft.name.module, ft.name.mt.name) && + ft == typeof(getfield(ft.name.module, ft.name.mt.name)) + print(io, ft.name.mt.name) + elseif isa(ft, DataType) && is(ft.name, Type.name) && isleaftype(ft) + f = ft.parameters[1] + print(io, f) + else + print(io, "(::", ft, ")") + end + first = true + print(io, '(') + for i = 2:length(sig) # fixme (iter): `eachindex` with offset? + first || print(io, ", ") + first = false + print(io, "::", sig[i]) + end + print(io, ')') + nothing end function ismodulecall(ex::Expr) @@ -988,6 +1025,7 @@ function show(io::IO, tv::TypeVar) print(io, "<:") show(io, tv.ub) end + nothing end function dump(io::IO, x::SimpleVector, n::Int, indent) @@ -1007,6 +1045,7 @@ function dump(io::IO, x::SimpleVector, n::Int, indent) end end end + nothing end function dump(io::IO, x::ANY, n::Int, indent) @@ -1068,6 +1107,7 @@ function dump(io::IO, x::Array, n::Int, indent) end end end + nothing end dump(io::IO, x::Symbol, n::Int, indent) = print(io, typeof(x), " ", x) @@ -1089,6 +1129,7 @@ function dump(io::IO, x::DataType, n::Int, indent) end end end + nothing end # dumptype is for displaying abstract type hierarchies like Jameson diff --git a/base/stacktraces.jl b/base/stacktraces.jl index 04eb029641073..9be7b64ca9195 100644 --- a/base/stacktraces.jl +++ b/base/stacktraces.jl @@ -162,32 +162,8 @@ function show_spec_linfo(io::IO, frame::StackFrame) end else linfo = get(frame.linfo) - params = - if isdefined(linfo, :specTypes) - linfo.specTypes.parameters - else - nothing - end - if params !== nothing - ft = params[1] - if ft <: Function && isempty(ft.parameters) && - isdefined(ft.name.module, ft.name.mt.name) && - ft == typeof(getfield(ft.name.module, ft.name.mt.name)) - print(io, ft.name.mt.name) - elseif isa(ft, DataType) && is(ft.name, Type.name) && isleaftype(ft) - f = ft.parameters[1] - print(io, f) - else - print(io, "(::", ft, ")") - end - first = true - print(io, '(') - for i = 2:length(params) - first || print(io, ", ") - first = false - print(io, "::", params[i]) - end - print(io, ')') + if isdefined(linfo, :specTypes) + Base.show_lambda_types(io, linfo.specTypes.parameters) else print(io, linfo.name) end diff --git a/base/strings/string.jl b/base/strings/string.jl index d9051d8cbc5a1..57dabebe6925a 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -9,6 +9,11 @@ ## basic UTF-8 decoding & iteration ## +is_surrogate_lead(c::Unsigned) = ((c & ~0x003ff) == 0xd800) +is_surrogate_trail(c::Unsigned) = ((c & ~0x003ff) == 0xdc00) +is_surrogate_codeunit(c::Unsigned) = ((c & ~0x007ff) == 0xd800) +is_valid_continuation(c) = ((c & 0xc0) == 0x80) + const utf8_offset = [ 0x00000000, 0x00003080, 0x000e2080, 0x03c82080, diff --git a/base/sysimg.jl b/base/sysimg.jl index 7f7067759f9ea..dac5f9c4b1727 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -136,6 +136,7 @@ include("iobuffer.jl") # strings & printing include("char.jl") +include("intfuncs.jl") include("strings/strings.jl") include("unicode/unicode.jl") include("parse.jl") @@ -151,7 +152,6 @@ using .Libc: getpid, gethostname, time include("libdl.jl") using .Libdl: DL_LOAD_PATH include("env.jl") -include("intfuncs.jl") # nullable types include("nullable.jl") diff --git a/base/unicode/checkstring.jl b/base/unicode/checkstring.jl index 50bece61b683d..72e9eb9d31062 100644 --- a/base/unicode/checkstring.jl +++ b/base/unicode/checkstring.jl @@ -3,11 +3,6 @@ ## Functions to check validity of UTF-8, UTF-16, and UTF-32 encoded strings, # and also to return information necessary to convert to other encodings -is_surrogate_lead(c::Unsigned) = ((c & ~0x003ff) == 0xd800) -is_surrogate_trail(c::Unsigned) = ((c & ~0x003ff) == 0xdc00) -is_surrogate_codeunit(c::Unsigned) = ((c & ~0x007ff) == 0xd800) -is_valid_continuation(c) = ((c & 0xc0) == 0x80) - ## Return flags for check_string function const UTF_LONG = 1 ##< Long encodings are present diff --git a/src/alloc.c b/src/alloc.c index 78afa45f542aa..5c3541b71200f 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -71,7 +71,8 @@ jl_value_t *jl_memory_exception; jl_value_t *jl_readonlymemory_exception; union jl_typemap_t jl_cfunction_list; -jl_sym_t *call_sym; jl_sym_t *dots_sym; +jl_sym_t *call_sym; jl_sym_t *invoke_sym; +jl_sym_t *dots_sym; jl_sym_t *module_sym; jl_sym_t *slot_sym; jl_sym_t *empty_sym; jl_sym_t *export_sym; jl_sym_t *import_sym; diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 72de27e164378..491ccddf3ee7b 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -1355,12 +1355,12 @@ static Value *init_bits_value(Value *newv, Value *jt, Value *v, MDNode *tbaa) return newv; } static Value *as_value(Type *t, const jl_cgval_t&); -static Value *init_bits_cgval(Value *newv, const jl_cgval_t& v, MDNode *tbaa, Type *t, jl_codectx_t *ctx) +static Value *init_bits_cgval(Value *newv, const jl_cgval_t& v, MDNode *tbaa, jl_codectx_t *ctx) { Value *jt = literal_pointer_val(v.typ); if (v.ispointer()) { init_tag(newv, jt); - builder.CreateMemCpy(newv, data_pointer(v,ctx,PointerType::get(t,0)), jl_datatype_size(v.typ), sizeof(void*)); + builder.CreateMemCpy(newv, data_pointer(v, ctx, T_pint8), jl_datatype_size(v.typ), sizeof(void*)); return newv; } else { @@ -1530,7 +1530,7 @@ static Value *boxed(const jl_cgval_t &vinfo, jl_codectx_t *ctx, bool gcrooted) return literal_pointer_val(jb->instance); } else { - box = init_bits_cgval(emit_allocobj(jl_datatype_size(jt)), vinfo, jb->mutabl ? tbaa_mutab : tbaa_immut, t, ctx); + box = init_bits_cgval(emit_allocobj(jl_datatype_size(jt)), vinfo, jb->mutabl ? tbaa_mutab : tbaa_immut, ctx); } if (gcrooted) { diff --git a/src/codegen.cpp b/src/codegen.cpp index 0dcf783927ae2..054a7a56795f9 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -370,6 +370,7 @@ static Function *jlcopyast_func; static Function *jltuple_func; static Function *jlnsvec_func; static Function *jlapplygeneric_func; +static Function *jlinvoke_func; static Function *jlapply2va_func; static Function *jlgetfield_func; static Function *jlmethod_func; @@ -868,7 +869,8 @@ static void to_function(jl_lambda_info_t *li) // if not inlineable, code won't be needed again if (JL_DELETE_NON_INLINEABLE && - li->def && li->inferred && !li->inlineable && !jl_options.outputji) { + li->def && li->inferred && !li->inlineable && + li != li->def->lambda_template && !jl_options.outputji) { li->code = jl_nothing; li->slottypes = jl_nothing; li->ssavaluetypes = jl_box_long(jl_array_len(li->ssavaluetypes)); jl_gc_wb(li, li->ssavaluetypes); @@ -1014,28 +1016,6 @@ extern "C" void jl_generate_fptr(jl_lambda_info_t *li) JL_UNLOCK(&codegen_lock); // Might GC } -extern "C" jl_lambda_info_t *jl_compile_for_dispatch(jl_lambda_info_t *li) -{ - if (li->inInference || li->inCompile) { - // if inference is running on this function, get a copy - // of the function to be compiled without inference and run. - assert(li->def != NULL); - li = jl_get_unspecialized(li); - } - if (li->fptr == NULL) { - if (li->functionObjectsDecls.functionObject == NULL) { - if (li->code == jl_nothing) - jl_type_infer(li, 0); - if (li->functionObjectsDecls.functionObject == NULL && li->code == jl_nothing) { - li = jl_get_unspecialized(li); - } - jl_compile_linfo(li); - } - jl_generate_fptr(li); - } - return li; -} - // this generates llvm code for the lambda info // (and adds it to the shadow module), but doesn't yet compile // or generate object code for it @@ -1138,17 +1118,22 @@ void *jl_get_llvmf(jl_tupletype_t *tt, bool getwrapper, bool getdeclarations) JL_GC_POP(); return NULL; } + // make sure to compile this normally first, + // since `emit_function` doesn't handle recursive compilation correctly + linfo = jl_compile_for_dispatch(linfo); - if (linfo->code == jl_nothing) { - // re-infer if we've deleted the code - linfo = jl_type_infer(linfo, 0); + if (!getdeclarations) { if (linfo->code == jl_nothing) { - JL_GC_POP(); - return NULL; + // re-infer if we've deleted the code + // XXX: this only /partially/ corrupts linfo + // (by confusing the compiler about the + // validity of the code it already generated) + linfo = jl_type_infer(linfo, 0); + if (linfo->code == jl_nothing || linfo->inInference) { + JL_GC_POP(); + return NULL; + } } - } - - if (!getdeclarations) { // emit this function into a new module Function *f, *specf; jl_llvm_functions_t declarations; @@ -1185,7 +1170,6 @@ void *jl_get_llvmf(jl_tupletype_t *tt, bool getwrapper, bool getdeclarations) return specf; } } - jl_compile_linfo(linfo); Function *llvmf; if (!getwrapper && linfo->functionObjectsDecls.specFunctionObject != NULL) { llvmf = (Function*)linfo->functionObjectsDecls.specFunctionObject; @@ -2713,15 +2697,53 @@ static jl_cgval_t emit_call_function_object(jl_lambda_info_t *li, const jl_cgval expr_type(callexpr, ctx), ctx); } +static jl_cgval_t emit_invoke(jl_expr_t *ex, jl_codectx_t *ctx) +{ + jl_value_t **args = (jl_value_t**)jl_array_data(ex->args); + size_t arglen = jl_array_dim0(ex->args); + size_t nargs = arglen - 1; + assert(arglen >= 2); + jl_lambda_info_t *li = (jl_lambda_info_t*)args[0]; + assert(jl_is_lambda_info(li)); + + jl_cgval_t result; + if (li->functionObjectsDecls.functionObject == NULL) { + assert(!li->inCompile); + if (li->code == jl_nothing && !li->inInference && li->inferred) { + // XXX: it was inferred in the past, so it's almost valid to re-infer it now + jl_type_infer(li, 0); + } + if (!li->inInference && li->inferred && li->code != jl_nothing) { + jl_compile_linfo(li); + } + } + Value *theFptr = (Value*)li->functionObjectsDecls.functionObject; + if (theFptr) { + jl_cgval_t fval = emit_expr(args[1], ctx); + result = emit_call_function_object(li, fval, theFptr, &args[1], nargs - 1, (jl_value_t*)ex, ctx); + } + else { + result = mark_julia_type(emit_jlcall(prepare_call(jlinvoke_func), literal_pointer_val((jl_value_t*)li), + &args[1], nargs, ctx), + true, expr_type((jl_value_t*)ex, ctx), ctx); + } + + if (result.typ == jl_bottom_type) { + CreateTrap(builder); + } + return result; +} + static jl_cgval_t emit_call(jl_expr_t *ex, jl_codectx_t *ctx) { jl_value_t *expr = (jl_value_t*)ex; jl_value_t **args = (jl_value_t**)jl_array_data(ex->args); size_t arglen = jl_array_dim0(ex->args); size_t nargs = arglen - 1; + assert(arglen >= 1); Value *theFptr = NULL; - jl_cgval_t result; jl_value_t *aty = NULL; + jl_cgval_t result; jl_function_t *f = (jl_function_t*)static_eval(args[0], ctx, true); JL_GC_PUSH2(&f, &aty); @@ -2765,7 +2787,8 @@ static jl_cgval_t emit_call(jl_expr_t *ex, jl_codectx_t *ctx) jl_sprint((jl_value_t*)aty)); }*/ jl_lambda_info_t *li = jl_get_specialization1((jl_tupletype_t*)aty); - if (li != NULL) { + if (li != NULL && !li->inInference) { + jl_compile_linfo(li); assert(li->functionObjectsDecls.functionObject != NULL); theFptr = (Value*)li->functionObjectsDecls.functionObject; jl_cgval_t fval; @@ -3207,6 +3230,9 @@ static jl_cgval_t emit_expr(jl_value_t *expr, jl_codectx_t *ctx) builder.CreateCondBr(isfalse, ifnot, ifso); builder.SetInsertPoint(ifso); } + else if (head == invoke_sym) { + return emit_invoke(ex, ctx); + } else if (head == call_sym) { if (ctx->linfo->def) { // don't bother codegen constant-folding for toplevel jl_value_t *c = static_eval(expr, ctx, true, true); @@ -3532,9 +3558,18 @@ static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_t jl_error("va_arg syntax not allowed for cfunction argument list"); const char *name = "cfunction"; + // try to look up this function for direct invoking jl_lambda_info_t *lam = jl_get_specialization1((jl_tupletype_t*)sigt); jl_value_t *astrt = (jl_value_t*)jl_any_type; + // infer it first, if necessary + if (lam && lam->inInference) + lam = NULL; // TODO: use emit_invoke framework to dispatch these + if (lam && (lam->code == jl_nothing || !lam->inferred)) + jl_type_infer(lam, 0); + if (lam && (lam->inInference || !lam->inferred)) + lam = NULL; // TODO: use emit_invoke framework to dispatch these if (lam != NULL) { + jl_compile_linfo(lam); name = jl_symbol_name(lam->def->name); astrt = lam->rettype; if (astrt != (jl_value_t*)jl_bottom_type && @@ -5360,6 +5395,15 @@ static void init_julia_llvm_env(Module *m) "jl_apply_generic", m); add_named_global(jlapplygeneric_func, &jl_apply_generic); + std::vector invokeargs(0); + invokeargs.push_back(T_pjlvalue); + invokeargs.push_back(T_ppjlvalue); + invokeargs.push_back(T_uint32); + jlinvoke_func = Function::Create(FunctionType::get(T_pjlvalue, invokeargs, false), + Function::ExternalLinkage, + "jl_invoke", m); + add_named_global(jlinvoke_func, &jl_invoke); + std::vector exp_args(0); exp_args.push_back(T_int1); expect_func = Intrinsic::getDeclaration(m, Intrinsic::expect, exp_args); diff --git a/src/dump.c b/src/dump.c index d75522ab8cb0d..4b26f2ad40947 100644 --- a/src/dump.c +++ b/src/dump.c @@ -867,7 +867,6 @@ static void jl_serialize_value_(ios_t *s, jl_value_t *v) jl_serialize_value(s, (jl_value_t*)li->sparam_syms); jl_serialize_value(s, (jl_value_t*)li->sparam_vals); jl_serialize_value(s, (jl_value_t*)li->specTypes); - jl_serialize_value(s, (jl_value_t*)li->unspecialized_ducttape); write_int8(s, li->inferred); write_int8(s, li->pure); write_int8(s, li->inlineable); @@ -1501,8 +1500,7 @@ static jl_value_t *jl_deserialize_value_(ios_t *s, jl_value_t *vtag, jl_value_t jl_gc_wb(li, li->sparam_vals); li->specTypes = (jl_tupletype_t*)jl_deserialize_value(s, (jl_value_t**)&li->specTypes); if (li->specTypes) jl_gc_wb(li, li->specTypes); - li->unspecialized_ducttape = (jl_lambda_info_t*)jl_deserialize_value(s, (jl_value_t**)&li->unspecialized_ducttape); - if (li->unspecialized_ducttape) jl_gc_wb(li, li->unspecialized_ducttape); + li->unspecialized_ducttape = NULL; li->inferred = read_int8(s); li->pure = read_int8(s); li->inlineable = read_int8(s); @@ -2413,7 +2411,7 @@ void jl_init_serializer(void) // everything above here represents a class of object rather than only a literal jl_emptysvec, jl_emptytuple, jl_false, jl_true, jl_nothing, jl_any_type, - call_sym, goto_ifnot_sym, return_sym, body_sym, line_sym, + call_sym, invoke_sym, goto_ifnot_sym, return_sym, body_sym, line_sym, lambda_sym, jl_symbol("tuple"), assign_sym, // empirical list of very common symbols diff --git a/src/gf.c b/src/gf.c index 4de0b42c7ec47..98a50b4694296 100644 --- a/src/gf.c +++ b/src/gf.c @@ -24,6 +24,11 @@ extern "C" { #endif +JL_DLLEXPORT jl_value_t *jl_invoke(jl_lambda_info_t *meth, jl_value_t **args, uint32_t nargs) +{ + return jl_call_method_internal(meth, args, nargs); +} + /// ----- Handling for Julia callbacks ----- /// int in_pure_callback; @@ -232,6 +237,20 @@ jl_lambda_info_t *jl_type_infer(jl_lambda_info_t *li, int force) return li; } +JL_DLLEXPORT void jl_set_lambda_rettype(jl_lambda_info_t *li, jl_value_t *rettype) +{ + // changing rettype changes the llvm signature, + // so clear all of the llvm state at the same time + assert(li->inInference); + li->rettype = rettype; + jl_gc_wb(li, rettype); + li->functionObjectsDecls.functionObject = NULL; + li->functionObjectsDecls.specFunctionObject = NULL; + li->functionID = 0; + li->specFunctionID = 0; + li->jlcall_api = 0; +} + static int get_spec_unspec_list(jl_typemap_entry_t *l, void *closure) { if (jl_is_lambda_info(l->func.value) && !l->func.linfo->inferred) @@ -658,11 +677,6 @@ static jl_lambda_info_t *cache_method(jl_methtable_t *mt, union jl_typemap_t *ca jl_typemap_insert(cache, parent, origtype, jl_emptysvec, type, guardsigs, (jl_value_t*)newmeth, jl_cachearg_offset(mt), &lambda_cache, NULL); - if (!newmeth->inferred && !newmeth->inInference) { - if (jl_options.compile_enabled != JL_OPTIONS_COMPILE_OFF) // don't bother with typeinf if compile is off - if (jl_symbol_name(definition->name)[0] != '@') // don't bother with typeinf on macros - jl_type_infer(newmeth, 0); - } JL_UNLOCK(&codegen_lock); // Might GC if (definition->traced && jl_method_tracer) jl_call_tracer(jl_method_tracer, (jl_value_t*)newmeth); @@ -1037,6 +1051,38 @@ jl_lambda_info_t *jl_method_lookup(jl_methtable_t *mt, jl_value_t **args, size_t JL_DLLEXPORT jl_value_t *jl_matching_methods(jl_tupletype_t *types, int lim, int include_ambiguous); +jl_lambda_info_t *jl_compile_for_dispatch(jl_lambda_info_t *li) +{ + if (li->functionObjectsDecls.functionObject == NULL) { + if (li->inInference || li->inCompile) { + // if inference is running on this function, get a copy + // of the function to be compiled without inference and run. + assert(li->def != NULL); + li = jl_get_unspecialized(li); + } + else { + if (li->code == jl_nothing || + (!li->inferred && li->def != NULL && jl_symbol_name(li->def->name)[0] != '@')) { + // don't bother with typeinf on macros or toplevel thunks + jl_type_infer(li, 0); + } + if (li->functionObjectsDecls.functionObject == NULL) { + if (li->inInference || li->inCompile || li->code == jl_nothing) { + // if inference is running on this function, get a copy + // of the function to be compiled without inference and run. + assert(li->def != NULL); + li = jl_get_unspecialized(li); + } + } + } + if (li->functionObjectsDecls.functionObject == NULL) { // check again, because jl_type_infer may have compiled it + jl_compile_linfo(li); + } + } + jl_generate_fptr(li); + return li; +} + // compile-time method lookup jl_lambda_info_t *jl_get_specialization1(jl_tupletype_t *types) { @@ -1047,7 +1093,7 @@ jl_lambda_info_t *jl_get_specialization1(jl_tupletype_t *types) // make sure exactly 1 method matches (issue #7302). int i; - for(i=0; i < jl_nparams(types); i++) { + for (i = 0; i < jl_nparams(types); i++) { jl_value_t *ti = jl_tparam(types, i); // if one argument type is DataType, multiple Type{} definitions // might match. also be conservative with tuples rather than trying @@ -1068,36 +1114,38 @@ jl_lambda_info_t *jl_get_specialization1(jl_tupletype_t *types) JL_TRY { sf = jl_method_lookup_by_type(mt, types, 1, 1); } JL_CATCH { - goto not_found; - } - if (sf != NULL) { - jl_method_t *m = sf->def; - if (jl_has_call_ambiguities(types, m)) { - goto not_found; - } + sf = NULL; } - if (sf == NULL || sf->code == NULL || sf->inInference) - goto not_found; - if (sf->functionObjectsDecls.functionObject == NULL) { - if (sf->fptr != NULL) - goto not_found; - if (sf->code == jl_nothing) { - jl_type_infer(sf, 0); - if (sf->code == jl_nothing) - goto not_found; - } - jl_compile_linfo(sf); + if (sf == NULL || sf->code == NULL || + jl_has_call_ambiguities(types, sf->def)) { + sf = NULL; } JL_GC_POP(); return sf; - not_found: - JL_GC_POP(); - return NULL; } JL_DLLEXPORT int jl_compile_hint(jl_tupletype_t *types) { - return jl_get_specialization1(types) != NULL; + jl_lambda_info_t *li = jl_get_specialization1(types); + if (li == NULL) + return 0; + if (li->functionObjectsDecls.functionObject == NULL) { + assert(!li->inCompile); + if (li->inInference) + return 0; + if (li->code == jl_nothing || !li->inferred) + jl_type_infer(li, 0); + if (li->inInference || li->code == jl_nothing) + return 0; + jl_compile_linfo(li); + } + return 1; +} + +JL_DLLEXPORT jl_value_t *jl_get_spec_lambda(jl_tupletype_t *types) +{ + jl_value_t *li = (jl_value_t*)jl_get_specialization1(types); + return li ? li : jl_nothing; } int jl_has_call_ambiguities(jl_tupletype_t *types, jl_method_t *m) @@ -1159,9 +1207,9 @@ static int _compile_all_tvar_union(jl_tupletype_t *methsig, jl_svec_t *tvars) if (jl_is_leaf_type((jl_value_t*)methsig)) { // usually can create a specialized version of the function, // if the signature is already a leaftype - jl_lambda_info_t *spec = jl_get_specialization1(methsig); - if (spec) + if (jl_compile_hint(methsig)) { return 1; + } } return 0; } @@ -1192,7 +1240,7 @@ static int _compile_all_tvar_union(jl_tupletype_t *methsig, jl_svec_t *tvars) goto getnext; // signature wouldn't be callable / is invalid -- skip it } if (jl_is_leaf_type(sig)) { - if (jl_get_specialization1((jl_tupletype_t*)sig)) { + if (jl_compile_hint((jl_tupletype_t*)sig)) { if (!jl_has_typevars((jl_value_t*)sig)) goto getnext; // success } } @@ -1300,7 +1348,7 @@ static void _compile_all_deq(jl_array_t *found) jl_lambda_info_t *linfo = NULL; JL_GC_PUSH1(&linfo); for (found_i = 0; found_i < found_l; found_i++) { - if (found_i % (found_l / 300) == 0 || found_i == found_l - 1) // show 300 progress steps, to show progress without overwhelming log files + if (found_i % (1 + found_l / 300) == 0 || found_i == found_l - 1) // show 300 progress steps, to show progress without overwhelming log files jl_printf(JL_STDERR, " %d / %d\r", found_i + 1, found_l); jl_typemap_entry_t *ml = (jl_typemap_entry_t*)jl_array_ptr_ref(found, found_i); if (ml->func.value == NULL) @@ -1310,10 +1358,7 @@ static void _compile_all_deq(jl_array_t *found) if (jl_is_method(ml->func.value)) { // type infer a copy of the template, to avoid modifying the template itself templ = ml->func.method->lambda_template; - if (templ->unspecialized_ducttape) - linfo = templ->unspecialized_ducttape; // TODO: switch to using the ->tfunc field to store/retrieve this - else - linfo = jl_get_specialized(ml->func.method, ml->sig, jl_emptysvec); + linfo = jl_specializations_get_linfo(ml->func.method, ml->sig, jl_emptysvec); } else if (jl_is_lambda_info(ml->func.value)) { templ = ml->func.linfo; @@ -1323,14 +1368,9 @@ static void _compile_all_deq(jl_array_t *found) continue; // this should be unreachable } - if (!linfo->inferred) { + if (!linfo->inferred || linfo->code == jl_nothing) { // force this function to be recompiled jl_type_infer(linfo, 1); - linfo->functionObjectsDecls.functionObject = NULL; - linfo->functionObjectsDecls.specFunctionObject = NULL; - linfo->functionID = 0; - linfo->specFunctionID = 0; - linfo->jlcall_api = 0; } // keep track of whether all possible signatures have been cached (and thus whether it can skip trying to compile the template function) @@ -1345,13 +1385,12 @@ static void _compile_all_deq(jl_array_t *found) jl_compile_linfo(linfo); assert(linfo->functionID > 0); if (linfo != templ) { - // copy the function pointer back to the template + // copy the function pointer back to the lambda_template templ->functionObjectsDecls.functionObject = linfo->functionObjectsDecls.functionObject; templ->functionObjectsDecls.specFunctionObject = linfo->functionObjectsDecls.specFunctionObject; templ->functionID = linfo->functionID; templ->specFunctionID = linfo->specFunctionID; templ->jlcall_api = linfo->jlcall_api; - templ->unspecialized_ducttape = linfo; jl_gc_wb(templ, linfo); } } diff --git a/src/interpreter.c b/src/interpreter.c index 2c775124207ff..dc7f4f9d30679 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -67,6 +67,20 @@ static jl_value_t *do_call(jl_value_t **args, size_t nargs, interpreter_state *s return result; } +static jl_value_t *do_invoke(jl_value_t **args, size_t nargs, interpreter_state *s) +{ + jl_value_t **argv; + JL_GC_PUSHARGS(argv, nargs - 1); + size_t i; + for (i = 1; i < nargs; i++) + argv[i - 1] = eval(args[i], s); + jl_lambda_info_t *meth = (jl_lambda_info_t*)args[0]; + assert(jl_is_lambda_info(meth) && !meth->inInference); + jl_value_t *result = jl_call_method_internal(meth, argv, nargs - 1); + JL_GC_POP(); + return result; +} + jl_value_t *jl_eval_global_var(jl_module_t *m, jl_sym_t *e) { jl_value_t *v = jl_get_global(m, e); @@ -173,6 +187,9 @@ static jl_value_t *eval(jl_value_t *e, interpreter_state *s) if (ex->head == call_sym) { return do_call(args, nargs, s); } + else if (ex->head == invoke_sym) { + return do_invoke(args, nargs, s); + } else if (ex->head == new_sym) { jl_value_t *thetype = eval(args[0], s); jl_value_t *v=NULL; diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 2ac9c4c91dd6e..a16a774c2373f 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -505,7 +505,12 @@ static jl_cgval_t generic_box(jl_value_t *targ, jl_value_t *x, jl_codectx_t *ctx vx = builder.CreateBitCast(vx, llvmt); } - return mark_julia_type(vx, false, bt, ctx); + if (jl_is_leaf_type(bt)) + return mark_julia_type(vx, false, bt, ctx); + else + return mark_julia_type( + init_bits_value(emit_allocobj(nb), boxed(bt_value, ctx), vx, tbaa_immut), + true, bt, ctx); } // put a bits type tag on some value diff --git a/src/jltypes.c b/src/jltypes.c index 21990589d43da..3b889ee6665ac 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3809,6 +3809,7 @@ void jl_init_types(void) empty_sym = jl_symbol(""); call_sym = jl_symbol("call"); + invoke_sym = jl_symbol("invoke"); quote_sym = jl_symbol("quote"); inert_sym = jl_symbol("inert"); top_sym = jl_symbol("top"); diff --git a/src/julia.h b/src/julia.h index 1c5155bf38836..4604fbff2c0cc 100644 --- a/src/julia.h +++ b/src/julia.h @@ -545,7 +545,8 @@ extern JL_DLLEXPORT jl_value_t *jl_false; extern JL_DLLEXPORT jl_value_t *jl_nothing; // some important symbols -extern jl_sym_t *call_sym; extern jl_sym_t *empty_sym; +extern jl_sym_t *call_sym; extern jl_sym_t *invoke_sym; +extern jl_sym_t *empty_sym; extern jl_sym_t *dots_sym; extern jl_sym_t *vararg_sym; extern jl_sym_t *quote_sym; extern jl_sym_t *newvar_sym; extern jl_sym_t *top_sym; extern jl_sym_t *dot_sym; @@ -1379,6 +1380,7 @@ STATIC_INLINE int jl_vinfo_usedundef(uint8_t vi) // calling into julia --------------------------------------------------------- JL_DLLEXPORT jl_value_t *jl_apply_generic(jl_value_t **args, uint32_t nargs); +JL_DLLEXPORT jl_value_t *jl_invoke(jl_lambda_info_t *meth, jl_value_t **args, uint32_t nargs); STATIC_INLINE jl_value_t *jl_apply(jl_value_t **args, uint32_t nargs) diff --git a/src/julia_internal.h b/src/julia_internal.h index 712256f1b03cf..91093cca95902 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -72,6 +72,7 @@ STATIC_INLINE jl_value_t *newstruct(jl_datatype_t *type) jl_lambda_info_t *jl_type_infer(jl_lambda_info_t *li, int force); void jl_generate_fptr(jl_lambda_info_t *li); void jl_compile_linfo(jl_lambda_info_t *li); +JL_DLLEXPORT int jl_compile_hint(jl_tupletype_t *types); jl_lambda_info_t *jl_compile_for_dispatch(jl_lambda_info_t *li); // invoke (compiling if necessary) the jlcall function pointer for a method diff --git a/src/threading.c b/src/threading.c index c2dcba0f468c7..8acf03b726047 100644 --- a/src/threading.c +++ b/src/threading.c @@ -395,8 +395,7 @@ JL_DLLEXPORT jl_value_t *jl_threading_run(jl_svec_t *args) int8_t gc_state = jl_gc_unsafe_enter(); JL_GC_PUSH1(&argtypes); argtypes = arg_type_tuple(jl_svec_data(args), jl_svec_len(args)); - jl_lambda_info_t *li = jl_get_specialization1(argtypes); - jl_generate_fptr(li); + jl_compile_hint(argtypes); threadwork.command = TI_THREADWORK_RUN; // TODO jb/functions: lookup and store jlcall fptr here diff --git a/src/toplevel.c b/src/toplevel.c index 06ce0ecc99cb2..69a0f55f41d3e 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -79,7 +79,7 @@ static void jl_module_load_time_initialize(jl_module_t *m) jl_value_t *tt = jl_is_type(f) ? (jl_value_t*)jl_wrap_Type(f) : jl_typeof(f); JL_GC_PUSH1(&tt); tt = (jl_value_t*)jl_apply_tuple_type_v(&tt, 1); - jl_get_specialization1((jl_tupletype_t*)tt); + jl_compile_hint((jl_tupletype_t*)tt); JL_GC_POP(); } }