From 0f0d8e26ebf90246dd6c0e0917c324de4f336ac2 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 31 May 2016 22:36:35 -0400 Subject: [PATCH] add a new Expr head for directly calling a LambdaInfo Expr(:invoke, LambdaInfo, call-args...) is a more primitive form of Expr(:call) for which the dispatch logic has been pre-determined this is not used by lowering, but is used by inference to allowing moving of this logic out of codegen also fix the tfunc for kwfunc, since it was all munged up and making the invoke inlining unhappy also fixes a number of bugs caused by deleting code not preserving various invariants or not testing correctly for them also fixes a number of bugs that have accumulated in --compile=all operation since that configuration stopped being tested on CI --- base/inference.jl | 77 +++++++++++++--------- base/show.jl | 45 ++++++++++++- base/stacktraces.jl | 28 +------- base/strings/string.jl | 5 ++ base/sysimg.jl | 2 +- base/unicode/checkstring.jl | 5 -- src/alloc.c | 3 +- src/cgutils.cpp | 6 +- src/codegen.cpp | 112 +++++++++++++++++++++---------- src/dump.c | 6 +- src/gf.c | 127 +++++++++++++++++++++++------------- src/interpreter.c | 17 +++++ src/intrinsics.cpp | 7 +- src/jltypes.c | 1 + src/julia.h | 4 +- src/julia_internal.h | 1 + src/threading.c | 3 +- src/toplevel.c | 2 +- 18 files changed, 296 insertions(+), 155 deletions(-) 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(); } }