From aea91558d3b60bf6d2bcdbd608b369e28ffffc9f Mon Sep 17 00:00:00 2001 From: Jarrett Revels Date: Tue, 16 Jan 2018 12:34:05 -0500 Subject: [PATCH] refactor inference.jl into multiple files for readability/maintainability (#25517) - the contents of `coreimg.jl`, `inference.jl` and `codevalidation.jl` now live in `base/compiler/` - `Core.Inference` --> `Core.Compiler`, and `Core.InferenceParams` --> `Core.Compiler.Params`, since these refer to more than just type inference at this point - Constants/structs now mostly appear in the file in which they are first used. Non-business logic methods acting on freshly-defined structs are now defined alongside the structs themselves. Non-business logic methods which don't depend on new structs are now in `utilities.jl`. - All non-alias `const` names are now all-caps. Type alias `const`s are camelcase, and function alias `const`s are lowercase. - Inference logic is split into several files (`abstractinterpretation.jl`, `typelattice.jl`, `typeinf.jl`, etc.) - All the tfunc definitions now reside in their own file. - Optimization logic (inlining, DCE, etc.) hasn't been broken up at all yet; it is now in `optimize.jl`. A future PR will be required to break it up. - No semantic changes, at least not on purpose. --- HISTORY.md | 2 +- Makefile | 20 +- base/array.jl | 6 +- base/compiler/abstractinterpretation.jl | 879 ++++ base/compiler/bootstrap.jl | 35 + base/{coreimg.jl => compiler/compiler.jl} | 54 +- base/compiler/inferenceresult.jl | 107 + base/compiler/inferencestate.jl | 233 + base/{inference.jl => compiler/optimize.jl} | 4083 +---------------- base/compiler/params.jl | 54 + base/compiler/tfuncs.jl | 905 ++++ base/compiler/typeinfer.jl | 467 ++ base/compiler/typelattice.jl | 266 ++ base/compiler/typelimits.jl | 369 ++ base/compiler/typeutils.jl | 171 + base/compiler/utilities.jl | 269 ++ .../validation.jl} | 23 +- base/deprecated.jl | 4 +- base/intfuncs.jl | 2 +- base/precompile.jl | 448 +- base/promotion.jl | 4 +- base/reducedim.jl | 2 +- base/reflection.jl | 16 +- base/repl/REPLCompletions.jl | 4 +- base/replutil.jl | 2 +- base/show.jl | 2 +- base/sysimg.jl | 10 +- base/tuple.jl | 2 +- contrib/build_sysimg.jl | 14 +- doc/src/devdocs/debuggingtips.md | 2 +- doc/src/devdocs/eval.md | 2 +- doc/src/devdocs/inference.md | 22 +- doc/src/manual/metaprogramming.md | 2 +- src/common_symbols2.inc | 2 +- stdlib/Distributed/src/precompile.jl | 110 +- test/ambiguous.jl | 6 +- test/choosetests.jl | 14 +- test/{inference.jl => compiler/compiler.jl} | 84 +- .../validation.jl} | 82 +- test/keywordargs.jl | 4 +- test/misc.jl | 2 +- test/reflection.jl | 8 +- test/staged.jl | 2 +- test/worlds.jl | 30 +- 44 files changed, 4469 insertions(+), 4356 deletions(-) create mode 100644 base/compiler/abstractinterpretation.jl create mode 100644 base/compiler/bootstrap.jl rename base/{coreimg.jl => compiler/compiler.jl} (51%) create mode 100644 base/compiler/inferenceresult.jl create mode 100644 base/compiler/inferencestate.jl rename base/{inference.jl => compiler/optimize.jl} (51%) create mode 100644 base/compiler/params.jl create mode 100644 base/compiler/tfuncs.jl create mode 100644 base/compiler/typeinfer.jl create mode 100644 base/compiler/typelattice.jl create mode 100644 base/compiler/typelimits.jl create mode 100644 base/compiler/typeutils.jl create mode 100644 base/compiler/utilities.jl rename base/{codevalidation.jl => compiler/validation.jl} (90%) rename test/{inference.jl => compiler/compiler.jl} (94%) rename test/{codevalidation.jl => compiler/validation.jl} (53%) diff --git a/HISTORY.md b/HISTORY.md index 366e88f403b1c..9edaa38af09a2 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -584,7 +584,7 @@ Deprecated or removed * `Base.promote_type(op::Type, Ts::Type...)` has been removed as part of an overhaul of `broadcast`'s promotion mechanism. If you need the functionality of that `Base.promote_type` method, consider defining it locally via - `Core.Inference.return_type(op, Tuple{Ts...})` ([#18642]). + `Core.Compiler.return_type(op, Tuple{Ts...})` ([#18642]). * `bitbroadcast` has been deprecated in favor of `broadcast`, which now produces a `BitArray` instead of `Array{Bool}` for functions yielding a boolean result ([#19771]). diff --git a/Makefile b/Makefile index e0f1a54bd3040..3fddd6af7d40e 100644 --- a/Makefile +++ b/Makefile @@ -94,13 +94,13 @@ julia-src-release julia-src-debug : julia-src-% : julia-deps julia_flisp.boot.in julia-ui-release julia-ui-debug : julia-ui-% : julia-src-% @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/ui julia-$* -julia-inference : julia-base julia-ui-$(JULIA_BUILD_MODE) $(build_prefix)/.examples - @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT) $(build_private_libdir)/inference.ji JULIA_BUILD_MODE=$(JULIA_BUILD_MODE) +julia-base-compiler : julia-base julia-ui-$(JULIA_BUILD_MODE) $(build_prefix)/.examples + @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT) $(build_private_libdir)/basecompiler.ji JULIA_BUILD_MODE=$(JULIA_BUILD_MODE) -julia-sysimg-release : julia-inference julia-ui-release +julia-sysimg-release : julia-base-compiler julia-ui-release @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT) $(build_private_libdir)/sys.$(SHLIB_EXT) JULIA_BUILD_MODE=release -julia-sysimg-debug : julia-inference julia-ui-debug +julia-sysimg-debug : julia-base-compiler julia-ui-debug @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT) $(build_private_libdir)/sys-debug.$(SHLIB_EXT) JULIA_BUILD_MODE=debug julia-debug julia-release : julia-% : julia-ui-% julia-sysimg-% julia-symlink julia-libccalltest julia-base-cache @@ -174,19 +174,17 @@ $(build_private_libdir)/%.$(SHLIB_EXT): $(build_private_libdir)/%.o @$(DSYMUTIL) $@ CORE_SRCS := $(addprefix $(JULIAHOME)/, \ - base/boot.jl base/coreimg.jl \ + base/boot.jl base/compiler/compiler.jl \ base/docs/core.jl \ base/abstractarray.jl \ base/array.jl \ base/bool.jl \ base/abstractdict.jl \ - base/codevalidation.jl \ base/error.jl \ base/essentials.jl \ base/generator.jl \ base/expr.jl \ base/hashing.jl \ - base/inference.jl \ base/int.jl \ base/bitset.jl \ base/number.jl \ @@ -201,15 +199,15 @@ CORE_SRCS := $(addprefix $(JULIAHOME)/, \ BASE_SRCS := $(sort $(shell find $(JULIAHOME)/base -name \*.jl) $(shell find $(BUILDROOT)/base -name \*.jl)) STDLIB_SRCS := $(sort $(shell find $(JULIAHOME)/stdlib/*/src -name \*.jl)) -$(build_private_libdir)/inference.ji: $(CORE_SRCS) | $(build_private_libdir) +$(build_private_libdir)/basecompiler.ji: $(CORE_SRCS) | $(build_private_libdir) @$(call PRINT_JULIA, cd $(JULIAHOME)/base && \ $(call spawn,$(JULIA_EXECUTABLE)) -C "$(JULIA_CPU_TARGET)" --output-ji $(call cygpath_w,$@) \ - --startup-file=no -g0 -O0 coreimg.jl) + --startup-file=no -g0 -O0 compiler/compiler.jl) RELBUILDROOT := $(shell $(JULIAHOME)/contrib/relative_path.sh "$(JULIAHOME)/base" "$(BUILDROOT)/base/") COMMA:=, define sysimg_builder -$$(build_private_libdir)/sys$1.o: $$(build_private_libdir)/inference.ji $$(JULIAHOME)/VERSION $$(BASE_SRCS) $$(STDLIB_SRCS) +$$(build_private_libdir)/sys$1.o: $$(build_private_libdir)/basecompiler.ji $$(JULIAHOME)/VERSION $$(BASE_SRCS) $$(STDLIB_SRCS) @$$(call PRINT_JULIA, cd $$(JULIAHOME)/base && \ if $$(call spawn,$3) $2 -C "$$(JULIA_CPU_TARGET)" --output-o $$(call cygpath_w,$$@).tmp $$(JULIA_SYSIMG_BUILD_FLAGS) \ --startup-file=no --warn-overwrite=yes --sysimage $$(call cygpath_w,$$<) sysimg.jl $$(RELBUILDROOT); then \ @@ -522,7 +520,7 @@ distcleanall: cleanall .PHONY: default debug release check-whitespace release-candidate \ julia-debug julia-release julia-deps \ julia-ui-release julia-ui-debug julia-src-release julia-src-debug \ - julia-symlink julia-base julia-inference julia-sysimg-release julia-sysimg-debug \ + julia-symlink julia-base julia-base-compiler julia-sysimg-release julia-sysimg-debug \ test testall testall1 test clean distcleanall cleanall clean-* \ run-julia run-julia-debug run-julia-release run \ install binary-dist light-source-dist.tmp light-source-dist \ diff --git a/base/array.jl b/base/array.jl index f5f779311a247..8d2a6a5443eb9 100644 --- a/base/array.jl +++ b/base/array.jl @@ -492,18 +492,18 @@ function _collect_indices(indsA, A) copyto!(B, CartesianIndices(axes(B)), A, CartesianIndices(indsA)) end -# define this as a macro so that the call to Inference +# define this as a macro so that the call to Core.Compiler # gets inlined into the caller before recursion detection # gets a chance to see it, so that recursive calls to the caller # don't trigger the inference limiter -if isdefined(Core, :Inference) +if isdefined(Core, :Compiler) macro default_eltype(itr) I = esc(itr) return quote if $I isa Generator && ($I).f isa Type ($I).f else - Core.Inference.return_type(first, Tuple{typeof($I)}) + Core.Compiler.return_type(first, Tuple{typeof($I)}) end end end diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl new file mode 100644 index 0000000000000..00a8509ac0034 --- /dev/null +++ b/base/compiler/abstractinterpretation.jl @@ -0,0 +1,879 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +############# +# constants # +############# + +const CoreNumType = Union{Int32, Int64, Float32, Float64} + +const DEPRECATED_SYM = Symbol("deprecated.jl") + +const _REF_NAME = Ref.body.name + +######### +# logic # +######### + +function abstract_call_gf_by_type(@nospecialize(f), argtypes::Vector{Any}, @nospecialize(atype), sv::InferenceState) + atype = limit_tuple_type(atype, sv.params) + atype_params = unwrap_unionall(atype).parameters + ft = unwrap_unionall(atype_params[1]) # TODO: ccall jl_first_argument_datatype here + isa(ft, DataType) || return Any # the function being called is unknown. can't properly handle this backedge right now + ftname = ft.name + isdefined(ftname, :mt) || return Any # not callable. should be Bottom, but can't track this backedge right now + if ftname === _TYPE_NAME + tname = ft.parameters[1] + if isa(tname, TypeVar) + tname = tname.ub + end + tname = unwrap_unionall(tname) + if !isa(tname, DataType) + # can't track the backedge to the ctor right now + # for things like Union + return Any + end + end + min_valid = UInt[typemin(UInt)] + max_valid = UInt[typemax(UInt)] + splitunions = 1 < countunionsplit(atype_params) <= sv.params.MAX_UNION_SPLITTING + if splitunions + splitsigs = switchtupleunion(atype) + applicable = Any[] + for sig_n in splitsigs + xapplicable = _methods_by_ftype(sig_n, sv.params.MAX_METHODS, sv.params.world, min_valid, max_valid) + xapplicable === false && return Any + append!(applicable, xapplicable) + end + else + applicable = _methods_by_ftype(atype, sv.params.MAX_METHODS, sv.params.world, min_valid, max_valid) + if applicable === false + # this means too many methods matched + # (assume this will always be true, so we don't compute / update valid age in this case) + return Any + end + end + update_valid_age!(min_valid[1], max_valid[1], sv) + applicable = applicable::Array{Any,1} + napplicable = length(applicable) + rettype = Bottom + edgecycle = false + for i in 1:napplicable + match = applicable[i]::SimpleVector + method = match[3]::Method + sig = match[1] + sigtuple = unwrap_unionall(sig)::DataType + splitunions = false + # TODO: splitunions = 1 < countunionsplit(sigtuple.parameters) * napplicable <= sv.params.MAX_UNION_SPLITTING + # currently this triggers a bug in inference recursion detection + if splitunions + splitsigs = switchtupleunion(sig) + for sig_n in splitsigs + rt, edgecycle1 = abstract_call_method(method, sig_n, svec(), sv) + edgecycle |= edgecycle1::Bool + rettype = tmerge(rettype, rt) + rettype === Any && break + end + rettype === Any && break + else + rt, edgecycle = abstract_call_method(method, sig, match[2]::SimpleVector, sv) + rettype = tmerge(rettype, rt) + rettype === Any && break + end + end + if napplicable == 1 && !edgecycle && isa(rettype, Type) && sv.params.ipo_constant_propagation + # if there's a possibility we could constant-propagate a better result + # (hopefully without doing too much work), try to do that now + # TODO: it feels like this could be better integrated into abstract_call_method / typeinf_edge + const_rettype = abstract_call_method_with_const_args(f, argtypes, applicable[1]::SimpleVector, sv) + if const_rettype ⊑ rettype + # use the better result, if it's a refinement of rettype + rettype = const_rettype + end + end + if !(rettype === Any) # adding a new method couldn't refine (widen) this type + fullmatch = false + for i in napplicable:-1:1 + match = applicable[i]::SimpleVector + method = match[3]::Method + if atype <: method.sig + fullmatch = true + break + end + end + if !fullmatch + # also need an edge to the method table in case something gets + # added that did not intersect with any existing method + add_mt_backedge!(ftname.mt, atype, sv) + end + end + #print("=> ", rettype, "\n") + return rettype +end + +function abstract_call_method_with_const_args(@nospecialize(f), argtypes::Vector{Any}, match::SimpleVector, sv::InferenceState) + method = match[3]::Method + nargs::Int = method.nargs + method.isva && (nargs -= 1) + length(argtypes) >= nargs || return Any # probably limit_tuple_type made this non-matching method apparently match + haveconst = false + for i in 1:nargs + a = argtypes[i] + if isa(a, Const) && !isdefined(typeof(a.val), :instance) + if !isleaftype(a.val) # alternately: !isa(a.val, DataType) || !isconstType(Type{a.val}) + # have new information from argtypes that wasn't available from the signature + haveconst = true + break + end + end + end + haveconst || return Any + sig = match[1] + sparams = match[2]::SimpleVector + code = code_for_method(method, sig, sparams, sv.params.world) + code === nothing && return Any + code = code::MethodInstance + # decide if it's likely to be worthwhile + cache_inlineable = false + if isdefined(code, :inferred) + cache_inf = code.inferred + if !(cache_inf === nothing) + cache_src_inferred = ccall(:jl_ast_flag_inferred, Bool, (Any,), cache_inf) + cache_src_inlineable = ccall(:jl_ast_flag_inlineable, Bool, (Any,), cache_inf) + cache_inlineable = cache_src_inferred && cache_src_inlineable + end + end + if !cache_inlineable && !sv.params.aggressive_constant_propagation + tm = _topmod(sv) + if !istopfunction(tm, f, :getproperty) && !istopfunction(tm, f, :setproperty!) + # in this case, see if all of the arguments are constants + for i in 1:nargs + a = argtypes[i] + if !isa(a, Const) && !isconstType(a) + return Any + end + end + end + end + inf_result = cache_lookup(code, argtypes, sv.params.cache) + if inf_result === nothing + inf_result = InferenceResult(code) + atypes = get_argtypes(inf_result) + for i in 1:nargs + a = argtypes[i] + if a isa Const + atypes[i] = a # inject Const argtypes into inference + end + end + frame = InferenceState(inf_result, #=optimize=#true, #=cache=#false, sv.params) + frame.limited = true + frame.parent = sv + push!(sv.params.cache, inf_result) + typeinf(frame) + end + result = inf_result.result + isa(result, InferenceState) && return Any # TODO: is this recursive constant inference? + add_backedge!(inf_result.linfo, sv) + return result +end + +function abstract_call_method(method::Method, @nospecialize(sig), sparams::SimpleVector, sv::InferenceState) + # TODO: remove with 0.7 deprecations + if method.file === DEPRECATED_SYM && method.sig == (Tuple{Type{T},Any} where T) + return Any, false + end + topmost = nothing + # Limit argument type tuple growth of functions: + # look through the parents list to see if there's a call to the same method + # and from the same method. + # Returns the topmost occurrence of that repeated edge. + cyclei = 0 + infstate = sv + edgecycle = false + while !(infstate === nothing) + infstate = infstate::InferenceState + if method === infstate.linfo.def + if infstate.linfo.specTypes == sig + # avoid widening when detecting self-recursion + # TODO: merge call cycle and return right away + topmost = nothing + edgecycle = true + break + end + if topmost === nothing + # inspect the parent of this edge, + # to see if they are the same Method as sv + # in which case we'll need to ensure it is convergent + # otherwise, we don't + for parent in infstate.callers_in_cycle + # check in the cycle list first + # all items in here are mutual parents of all others + if parent.linfo.def === sv.linfo.def + topmost = infstate + edgecycle = true + break + end + end + let parent = infstate.parent + # then check the parent link + if topmost === nothing && parent !== nothing + parent = parent::InferenceState + if parent.cached && parent.linfo.def === sv.linfo.def + topmost = infstate + edgecycle = true + end + end + end + end + end + # iterate through the cycle before walking to the parent + if cyclei < length(infstate.callers_in_cycle) + cyclei += 1 + infstate = infstate.callers_in_cycle[cyclei] + else + cyclei = 0 + infstate = infstate.parent + end + end + + if !(topmost === nothing) + topmost = topmost::InferenceState + sigtuple = unwrap_unionall(sig)::DataType + msig = unwrap_unionall(method.sig)::DataType + spec_len = length(msig.parameters) + 1 + ls = length(sigtuple.parameters) + if method === sv.linfo.def + # Under direct self-recursion, permit much greater use of reducers. + # here we assume that complexity(specTypes) :>= complexity(sig) + comparison = sv.linfo.specTypes + l_comparison = length(unwrap_unionall(comparison).parameters) + spec_len = max(spec_len, l_comparison) + else + comparison = method.sig + end + # see if the type is actually too big (relative to the caller), and limit it if required + newsig = limit_type_size(sig, comparison, sv.linfo.specTypes, spec_len) + + if newsig !== sig + # continue inference, but note that we've limited parameter complexity + # on this call (to ensure convergence), so that we don't cache this result + infstate = sv + topmost = topmost::InferenceState + while !(infstate.parent === topmost.parent) + infstate.limited = true + for infstate_cycle in infstate.callers_in_cycle + infstate_cycle.limited = true + end + infstate = infstate.parent + end + sig = newsig + sparams = svec() + end + end + + # if sig changed, may need to recompute the sparams environment + if isa(method.sig, UnionAll) && isempty(sparams) + recomputed = ccall(:jl_type_intersection_with_env, Any, (Any, Any), sig, method.sig)::SimpleVector + sig = recomputed[1] + if !isa(unwrap_unionall(sig), DataType) # probably Union{} + return Any, false + end + sparams = recomputed[2]::SimpleVector + end + + rt, edge = typeinf_edge(method, sig, sparams, sv) + if edge === nothing + edgecycle = true + else + add_backedge!(edge::MethodInstance, sv) + end + return rt, edgecycle +end + +# `typ` is the inferred type for expression `arg`. +# if the expression constructs a container (e.g. `svec(x,y,z)`), +# refine its type to an array of element types. +# Union of Tuples of the same length is converted to Tuple of Unions. +# returns an array of types +function precise_container_type(@nospecialize(arg), @nospecialize(typ), vtypes::VarTable, sv::InferenceState) + if isa(typ, Const) + val = typ.val + if isa(val, SimpleVector) || isa(val, Tuple) + return Any[ Const(val[i]) for i in 1:length(val) ] # avoid making a tuple Generator here! + end + end + + while isa(arg, SSAValue) + def = sv.ssavalue_defs[arg.id + 1] + stmt = sv.src.code[def]::Expr + arg = stmt.args[2] + end + + if is_specializable_vararg_slot(arg, sv) + return Any[rewrap_unionall(p, sv.linfo.specTypes) for p in sv.vararg_type_container.parameters] + end + + tti0 = widenconst(typ) + tti = unwrap_unionall(tti0) + if isa(arg, Expr) && arg.head === :call && (abstract_evals_to_constant(arg.args[1], svec, vtypes, sv) || + abstract_evals_to_constant(arg.args[1], tuple, vtypes, sv)) + aa = arg.args + result = Any[ (isa(aa[j],Expr) ? aa[j].typ : abstract_eval(aa[j],vtypes,sv)) for j=2:length(aa) ] + if _any(isvarargtype, result) + return Any[Vararg{Any}] + end + return result + elseif isa(tti, Union) + utis = uniontypes(tti) + if _any(t -> !isa(t,DataType) || !(t <: Tuple) || !isknownlength(t), utis) + return Any[Vararg{Any}] + end + result = Any[rewrap_unionall(p, tti0) for p in utis[1].parameters] + for t in utis[2:end] + if length(t.parameters) != length(result) + return Any[Vararg{Any}] + end + for j in 1:length(t.parameters) + result[j] = tmerge(result[j], rewrap_unionall(t.parameters[j], tti0)) + end + end + return result + elseif isa(tti0,DataType) && tti0 <: Tuple + if isvatuple(tti0) && length(tti0.parameters) == 1 + return Any[Vararg{unwrapva(tti0.parameters[1])}] + else + return Any[ p for p in tti0.parameters ] + end + elseif tti0 <: Array + return Any[Vararg{eltype(tti0)}] + else + return Any[abstract_iteration(typ, vtypes, sv)] + end +end + +# simulate iteration protocol on container type up to fixpoint +function abstract_iteration(@nospecialize(itertype), vtypes::VarTable, sv::InferenceState) + tm = _topmod(sv) + if !isdefined(tm, :start) || !isdefined(tm, :next) || !isconst(tm, :start) || !isconst(tm, :next) + return Vararg{Any} + end + startf = getfield(tm, :start) + nextf = getfield(tm, :next) + statetype = abstract_call(startf, (), Any[Const(startf), itertype], vtypes, sv) + statetype === Bottom && return Bottom + valtype = Bottom + while valtype !== Any + nt = abstract_call(nextf, (), Any[Const(nextf), itertype, statetype], vtypes, sv) + nt = widenconst(nt) + if !isa(nt, DataType) || !(nt <: Tuple) || isvatuple(nt) || length(nt.parameters) != 2 + return Vararg{Any} + end + if nt.parameters[1] <: valtype && nt.parameters[2] <: statetype + break + end + valtype = tmerge(valtype, nt.parameters[1]) + statetype = tmerge(statetype, nt.parameters[2]) + end + return Vararg{valtype} +end + +# do apply(af, fargs...), where af is a function value +function abstract_apply(@nospecialize(aft), fargs::Vector{Any}, aargtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState) + if !isa(aft, Const) && !isconstType(aft) + if !(isleaftype(aft) || aft <: Type) || (aft <: Builtin) || (aft <: IntrinsicFunction) + return Any + end + # non-constant function, but type is known + end + res = Union{} + nargs = length(fargs) + assert(nargs == length(aargtypes)) + splitunions = 1 < countunionsplit(aargtypes) <= sv.params.MAX_APPLY_UNION_ENUM + ctypes = Any[Any[aft]] + for i = 1:nargs + ctypes´ = [] + for ti in (splitunions ? uniontypes(aargtypes[i]) : Any[aargtypes[i]]) + cti = precise_container_type(fargs[i], ti, vtypes, sv) + for ct in ctypes + if !isempty(ct) && isvarargtype(ct[end]) + tail = tuple_tail_elem(unwrapva(ct[end]), cti) + push!(ctypes´, push!(ct[1:(end - 1)], tail)) + else + push!(ctypes´, append_any(ct, cti)) + end + end + end + ctypes = ctypes´ + end + for ct in ctypes + if length(ct) > sv.params.MAX_TUPLETYPE_LEN + tail = tuple_tail_elem(Bottom, ct[sv.params.MAX_TUPLETYPE_LEN:end]) + resize!(ct, sv.params.MAX_TUPLETYPE_LEN) + ct[end] = tail + end + if isa(aft, Const) + rt = abstract_call(aft.val, (), ct, vtypes, sv) + elseif isconstType(aft) + rt = abstract_call(aft.parameters[1], (), ct, vtypes, sv) + else + astype = argtypes_to_type(ct) + rt = abstract_call_gf_by_type(nothing, ct, astype, sv) + end + res = tmerge(res, rt) + if res === Any + break + end + end + return res +end + +function pure_eval_call(@nospecialize(f), argtypes::Vector{Any}, @nospecialize(atype), sv::InferenceState) + for i = 2:length(argtypes) + a = argtypes[i] + if !(isa(a,Const) || isconstType(a)) + return false + end + end + + min_valid = UInt[typemin(UInt)] + max_valid = UInt[typemax(UInt)] + meth = _methods_by_ftype(atype, 1, sv.params.world, min_valid, max_valid) + if meth === false || length(meth) != 1 + return false + end + meth = meth[1]::SimpleVector + method = meth[3]::Method + # TODO: check pure on the inferred thunk + if isdefined(method, :generator) || !method.pure + return false + end + + args = Any[ (a=argtypes[i]; isa(a,Const) ? a.val : a.parameters[1]) for i in 2:length(argtypes) ] + try + value = Core._apply_pure(f, args) + # TODO: add some sort of edge(s) + return Const(value, true) + catch + return false + end +end + +function abstract_call(@nospecialize(f), fargs::Union{Tuple{},Vector{Any}}, argtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState) + if f === _apply + length(fargs) > 1 || return Any + return abstract_apply(argtypes[2], fargs[3:end], argtypes[3:end], vtypes, sv) + end + + la = length(argtypes) + for i = 2:(la - 1) + if isvarargtype(argtypes[i]) + return Any + end + end + + tm = _topmod(sv) + if isa(f, Builtin) || isa(f, IntrinsicFunction) + rt = builtin_tfunction(f, argtypes[2:end], sv) + if rt === Bool && isa(fargs, Vector{Any}) + # perform very limited back-propagation of type information for `is` and `isa` + if f === isa + a = fargs[2] + if isa(a, fieldtype(Conditional, :var)) + aty = widenconst(argtypes[2]) + tty_ub, isexact_tty = instanceof_tfunc(argtypes[3]) + if isexact_tty && !isa(tty_ub, TypeVar) + tty_lb = tty_ub # TODO: this would be wrong if !isexact_tty, but instanceof_tfunc doesn't preserve this info + if !has_free_typevars(tty_lb) && !has_free_typevars(tty_ub) + ifty = typeintersect(aty, tty_ub) + elsety = typesubtract(aty, tty_lb) + if ifty != elsety + return Conditional(a, ifty, elsety) + end + end + end + return Bool + end + elseif f === (===) + a = fargs[2] + b = fargs[3] + aty = argtypes[2] + bty = argtypes[3] + # if doing a comparison to a singleton, consider returning a `Conditional` instead + if isa(aty, Const) && isa(b, fieldtype(Conditional, :var)) + if isdefined(typeof(aty.val), :instance) # can only widen a if it is a singleton + return Conditional(b, aty, typesubtract(widenconst(bty), typeof(aty.val))) + end + return Conditional(b, aty, bty) + end + if isa(bty, Const) && isa(a, fieldtype(Conditional, :var)) + if isdefined(typeof(bty.val), :instance) # same for b + return Conditional(a, bty, typesubtract(widenconst(aty), typeof(bty.val))) + end + return Conditional(a, bty, aty) + end + elseif f === Core.Compiler.not_int + aty = argtypes[2] + if isa(aty, Conditional) + return Conditional(aty.var, aty.elsetype, aty.vtype) + end + end + end + return isa(rt, TypeVar) ? rt.ub : rt + elseif f === Core.kwfunc + if length(argtypes) == 2 + ft = widenconst(argtypes[2]) + if isa(ft, DataType) && isdefined(ft.name, :mt) && isdefined(ft.name.mt, :kwsorter) + return Const(ft.name.mt.kwsorter) + end + end + return Any + elseif f === TypeVar + lb = Union{} + ub = Any + ub_certain = lb_certain = true + if length(argtypes) >= 2 && isa(argtypes[2], Const) + nv = argtypes[2].val + ubidx = 3 + if length(argtypes) >= 4 + ubidx = 4 + if isa(argtypes[3], Const) + lb = argtypes[3].val + elseif isType(argtypes[3]) + lb = argtypes[3].parameters[1] + lb_certain = false + else + return TypeVar + end + end + if length(argtypes) >= ubidx + if isa(argtypes[ubidx], Const) + ub = argtypes[ubidx].val + elseif isType(argtypes[ubidx]) + ub = argtypes[ubidx].parameters[1] + ub_certain = false + else + return TypeVar + end + end + tv = TypeVar(nv, lb, ub) + return PartialTypeVar(tv, lb_certain, ub_certain) + end + return TypeVar + elseif f === UnionAll + if length(argtypes) == 3 + canconst = true + if isa(argtypes[3], Const) + body = argtypes[3].val + elseif isType(argtypes[3]) + body = argtypes[3].parameters[1] + canconst = false + else + return Any + end + if !isa(body, Type) && !isa(body, TypeVar) + return Any + end + has_free_typevars(body) || return body + if isa(argtypes[2], Const) + tv = argtypes[2].val + elseif isa(argtypes[2], PartialTypeVar) + ptv = argtypes[2] + tv = ptv.tv + canconst = false + else + return Any + end + !isa(tv, TypeVar) && return Any + theunion = UnionAll(tv, body) + ret = canconst ? AbstractEvalConstant(theunion) : Type{theunion} + return ret + end + return Any + elseif f === return_type + rt_rt = return_type_tfunc(argtypes, vtypes, sv) + if rt_rt !== NOT_FOUND + return rt_rt + end + elseif length(argtypes) == 2 && istopfunction(tm, f, :!) + # handle Conditional propagation through !Bool + aty = argtypes[2] + if isa(aty, Conditional) + abstract_call_gf_by_type(f, Any[Const(f), Bool], Tuple{typeof(f), Bool}, sv) # make sure we've inferred `!(::Bool)` + return Conditional(aty.var, aty.elsetype, aty.vtype) + end + elseif length(argtypes) == 3 && istopfunction(tm, f, :!==) + # mark !== as exactly a negated call to === + rty = abstract_call((===), fargs, argtypes, vtypes, sv) + if isa(rty, Conditional) + return Conditional(rty.var, rty.elsetype, rty.vtype) # swap if-else + elseif isa(rty, Const) + return Const(rty.val === false) + end + return rty + elseif length(argtypes) == 3 && istopfunction(tm, f, :(>:)) + # mark issupertype as a exact alias for issubtype + # swap T1 and T2 arguments and call <: + if length(fargs) == 3 + fargs = Any[<:, fargs[3], fargs[2]] + else + fargs = () + end + argtypes = Any[typeof(<:), argtypes[3], argtypes[2]] + rty = abstract_call(<:, fargs, argtypes, vtypes, sv) + return rty + elseif length(argtypes) == 2 && isa(argtypes[2], Const) && isa(argtypes[2].val, SimpleVector) && istopfunction(tm, f, :length) + # mark length(::SimpleVector) as @pure + return Const(length(argtypes[2].val)) + elseif length(argtypes) == 3 && isa(argtypes[2], Const) && isa(argtypes[3], Const) && + isa(argtypes[2].val, SimpleVector) && isa(argtypes[3].val, Int) && istopfunction(tm, f, :getindex) + # mark getindex(::SimpleVector, i::Int) as @pure + svecval = argtypes[2].val::SimpleVector + idx = argtypes[3].val::Int + if 1 <= idx <= length(svecval) && isassigned(svecval, idx) + return Const(getindex(svecval, idx)) + end + elseif length(argtypes) == 2 && istopfunction(tm, f, :typename) + return typename_static(argtypes[2]) + end + + atype = argtypes_to_type(argtypes) + t = pure_eval_call(f, argtypes, atype, sv) + t !== false && return t + + if istopfunction(tm, f, :typejoin) || f === return_type + return Type # don't try to infer these function edges directly -- it won't actually come up with anything useful + end + + if sv.params.inlining + # need to model the special inliner for ^ + # to ensure we have added the same edge + if isdefined(Main, :Base) && + ((isdefined(Main.Base, :^) && f === Main.Base.:^) || + (isdefined(Main.Base, :.^) && f === Main.Base.:.^)) && + length(argtypes) == 3 && (argtypes[3] ⊑ Int32 || argtypes[3] ⊑ Int64) + + a1 = argtypes[2] + basenumtype = Union{CoreNumType, Main.Base.ComplexF32, Main.Base.ComplexF64, Main.Base.Rational} + if a1 ⊑ basenumtype + ftimes = Main.Base.:* + ta1 = widenconst(a1) + abstract_call_gf_by_type(ftimes, Any[ftimes, a1, a1], Tuple{typeof(ftimes), ta1, ta1}, sv) + end + end + end + return abstract_call_gf_by_type(f, argtypes, atype, sv) +end + +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 + x === Bottom && return Bottom + end + ft = argtypes[1] + if isa(ft, Const) + f = ft.val + else + if isType(ft) && isleaftype(ft.parameters[1]) + f = ft.parameters[1] + elseif isleaftype(ft) && isdefined(ft, :instance) + f = ft.instance + else + for i = 2:(length(argtypes)-1) + if isvarargtype(argtypes[i]) + return Any + end + end + # non-constant function, but type is known + if (isleaftype(ft) || ft <: Type) && !(ft <: Builtin) && !(ft <: IntrinsicFunction) + return abstract_call_gf_by_type(nothing, argtypes, argtypes_to_type(argtypes), sv) + end + return Any + end + end + return abstract_call(f, e.args, argtypes, vtypes, sv) +end + +function abstract_eval(@nospecialize(e), vtypes::VarTable, sv::InferenceState) + if isa(e, QuoteNode) + return AbstractEvalConstant((e::QuoteNode).value) + elseif isa(e, SSAValue) + return abstract_eval_ssavalue(e::SSAValue, sv.src) + elseif isa(e, Slot) + return vtypes[slot_id(e)].typ + elseif isa(e, Symbol) + return abstract_eval_global(sv.mod, e) + elseif isa(e,GlobalRef) + return abstract_eval_global(e.mod, e.name) + end + + if !isa(e, Expr) + return AbstractEvalConstant(e) + end + e = e::Expr + if e.head === :call + t = abstract_eval_call(e, vtypes, sv) + elseif e.head === :new + t = instanceof_tfunc(abstract_eval(e.args[1], vtypes, sv))[1] + for i = 2:length(e.args) + if abstract_eval(e.args[i], vtypes, sv) === Bottom + rt = Bottom + end + end + elseif e.head === :& + abstract_eval(e.args[1], vtypes, sv) + t = Any + elseif e.head === :foreigncall + rt = e.args[2] + if isa(sv.linfo.def, Method) + spsig = sv.linfo.def.sig + if isa(spsig, UnionAll) + if !isempty(sv.linfo.sparam_vals) + env = pointer_from_objref(sv.linfo.sparam_vals) + sizeof(Ptr{Cvoid}) + rt = ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), e.args[2], spsig, env) + else + rt = rewrap_unionall(e.args[2], spsig) + end + end + end + abstract_eval(e.args[1], vtypes, sv) + for i = 3:length(e.args) + if abstract_eval(e.args[i], vtypes, sv) === Bottom + t = Bottom + end + end + if rt === Bottom + t = Bottom + elseif isa(rt, Type) + t = rt + if isa(t, DataType) && (t::DataType).name === _REF_NAME + t = t.parameters[1] + if t === Any + t = Bottom # a return type of Box{Any} is invalid + end + end + for v in sv.linfo.sparam_vals + if isa(v,TypeVar) + t = UnionAll(v, t) + end + end + else + t = Any + end + elseif e.head === :static_parameter + n = e.args[1] + t = Any + if 1 <= n <= length(sv.sp) + val = sv.sp[n] + if isa(val, TypeVar) + if Any <: val.ub + # static param bound to typevar + # if the tvar is not known to refer to anything more specific than Any, + # the static param might actually be an integer, symbol, etc. + else + t = UnionAll(val, Type{val}) + end + else + t = AbstractEvalConstant(val) + end + end + elseif e.head === :method + t = (length(e.args) == 1) ? Any : Nothing + elseif e.head === :copyast + t = abstract_eval(e.args[1], vtypes, sv) + if t isa Const && t.val isa Expr + # `copyast` makes copies of Exprs + t = Expr + end + elseif e.head === :invoke + error("type inference data-flow error: tried to double infer a function") + elseif e.head === :boundscheck + return Bool + elseif e.head === :isdefined + sym = e.args[1] + t = Bool + if isa(sym, Slot) + vtyp = vtypes[slot_id(sym)] + if vtyp.typ === Bottom + t = Const(false) # never assigned previously + elseif !vtyp.undef + t = Const(true) # definitely assigned previously + end + elseif isa(sym, Symbol) + if isdefined(sv.mod, sym.name) + t = Const(true) + end + elseif isa(sym, GlobalRef) + if isdefined(sym.mod, sym.name) + t = Const(true) + end + elseif isa(sym, Expr) && sym.head === :static_parameter + n = sym.args[1] + if 1 <= n <= length(sv.sp) + val = sv.sp[n] + if !isa(val, TypeVar) + t = Const(true) + end + end + end + else + t = Any + end + if isa(t, TypeVar) + # no need to use a typevar as the type of an expression + t = t.ub + end + if isa(t, DataType) && isdefined(t, :instance) + # replace singleton types with their equivalent Const object + t = Const(t.instance) + end + e.typ = t + return t +end + +function abstract_eval_global(M::Module, s::Symbol) + if isdefined(M,s) && isconst(M,s) + return AbstractEvalConstant(getfield(M,s)) + end + return Any +end + +function abstract_eval_ssavalue(s::SSAValue, src::CodeInfo) + typ = src.ssavaluetypes[s.id + 1] + if typ === NOT_FOUND + return Bottom + end + return typ +end + +# determine whether `ex` abstractly evals to constant `c` +function abstract_evals_to_constant(@nospecialize(ex), @nospecialize(c), vtypes::VarTable, sv::InferenceState) + av = abstract_eval(ex, vtypes, sv) + return isa(av,Const) && av.val === c +end + +# handling for statement-position expressions +function abstract_interpret(@nospecialize(e), vtypes::VarTable, sv::InferenceState) + !isa(e, Expr) && return vtypes + # handle assignment + if e.head === :(=) + t = abstract_eval(e.args[2], vtypes, sv) + t === Bottom && return () + lhs = e.args[1] + if isa(lhs, Slot) || isa(lhs, SSAValue) + # don't bother for GlobalRef + return StateUpdate(lhs, VarState(t, false), vtypes) + end + elseif e.head === :call || e.head === :foreigncall + t = abstract_eval(e, vtypes, sv) + t === Bottom && return () + elseif e.head === :gotoifnot + t = abstract_eval(e.args[1], vtypes, sv) + t === Bottom && return () + elseif e.head === :method + fname = e.args[1] + if isa(fname, Slot) + return StateUpdate(fname, VarState(Any, false), vtypes) + end + end + return vtypes +end diff --git a/base/compiler/bootstrap.jl b/base/compiler/bootstrap.jl new file mode 100644 index 0000000000000..04796b39c58cf --- /dev/null +++ b/base/compiler/bootstrap.jl @@ -0,0 +1,35 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# make sure that typeinf is executed before turning on typeinf_ext +# this ensures that typeinf_ext doesn't recurse before it can add the item to the workq +# especially try to make sure any recursive and leaf functions have concrete signatures, +# since we won't be able to specialize & infer them at runtime + +let fs = Any[typeinf_ext, typeinf, typeinf_edge, pure_eval_call], + world = ccall(:jl_get_world_counter, UInt, ()) + for x in T_FFUNC_VAL + push!(fs, x[3]) + end + for i = 1:length(T_IFUNC) + if isassigned(T_IFUNC, i) + x = T_IFUNC[i] + push!(fs, x[3]) + else + println(STDERR, "WARNING: tfunc missing for ", reinterpret(IntrinsicFunction, Int32(i))) + end + end + for f in fs + for m in _methods_by_ftype(Tuple{typeof(f), Vararg{Any}}, 10, typemax(UInt)) + # remove any TypeVars from the intersection + typ = Any[m[1].parameters...] + for i = 1:length(typ) + if isa(typ[i], TypeVar) + typ[i] = typ[i].ub + end + end + typeinf_type(m[3], Tuple{typ...}, m[2], true, Params(world)) + end + end +end + +ccall(:jl_set_typeinf_func, Cvoid, (Any,), typeinf_ext) diff --git a/base/coreimg.jl b/base/compiler/compiler.jl similarity index 51% rename from base/coreimg.jl rename to base/compiler/compiler.jl index 583f3b8ab96d1..87e5bc8644cca 100644 --- a/base/coreimg.jl +++ b/base/compiler/compiler.jl @@ -1,23 +1,28 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -getfield(getfield(Main, :Core), :eval)(getfield(Main, :Core), :(baremodule Inference +getfield(getfield(Main, :Core), :eval)(getfield(Main, :Core), :(baremodule Compiler + using Core.Intrinsics -import Core: print, println, show, write, unsafe_write, STDOUT, STDERR + +import Core: print, println, show, write, unsafe_write, STDOUT, STDERR, + _apply, svec, apply_type, Builtin, IntrinsicFunction, MethodInstance const getproperty = getfield const setproperty! = setfield! -ccall(:jl_set_istopmod, Cvoid, (Any, Bool), Inference, false) +ccall(:jl_set_istopmod, Cvoid, (Any, Bool), Compiler, false) -eval(x) = Core.eval(Inference, x) +eval(x) = Core.eval(Compiler, x) eval(m, x) = Core.eval(m, x) -include(x) = Core.include(Inference, x) +include(x) = Core.include(Compiler, x) include(mod, x) = Core.include(mod, x) -function return_type end +############# +# from Base # +############# -## Load essential files and libraries +# essential files and libraries include("essentials.jl") include("ctypes.jl") include("generator.jl") @@ -25,6 +30,7 @@ include("reflection.jl") include("options.jl") # core operations & types +function return_type end # promotion.jl expects this to exist include("promotion.jl") include("tuple.jl") include("pair.jl") @@ -53,7 +59,7 @@ macro simd(forloop) end include("reduce.jl") -## core structures +# core structures include("bitarray.jl") include("bitset.jl") include("abstractdict.jl") @@ -63,10 +69,32 @@ include("namedtuple.jl") # core docsystem include("docs/core.jl") -# compiler -include("codevalidation.jl") -include("inference.jl") -ccall(:jl_set_typeinf_func, Cvoid, (Any,), typeinf_ext) +############ +# compiler # +############ + +inlining_enabled() = (JLOptions().can_inline == 1) +coverage_enabled() = (JLOptions().code_coverage != 0) + +const isleaftype = _isleaftype + +include("compiler/utilities.jl") +include("compiler/validation.jl") + +include("compiler/inferenceresult.jl") +include("compiler/params.jl") +include("compiler/inferencestate.jl") + +include("compiler/typeutils.jl") +include("compiler/typelimits.jl") +include("compiler/typelattice.jl") +include("compiler/tfuncs.jl") + +include("compiler/abstractinterpretation.jl") +include("compiler/typeinfer.jl") +include("compiler/optimize.jl") # TODO: break this up further + extract utilities + +include("compiler/bootstrap.jl") -end # baremodule Inference +end # baremodule Compiler )) diff --git a/base/compiler/inferenceresult.jl b/base/compiler/inferenceresult.jl new file mode 100644 index 0000000000000..c7674c7948061 --- /dev/null +++ b/base/compiler/inferenceresult.jl @@ -0,0 +1,107 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +const EMPTY_VECTOR = Vector{Any}() + +mutable struct InferenceResult + linfo::MethodInstance + args::Vector{Any} + result # ::Type, or InferenceState if WIP + src::Union{CodeInfo, Nothing} # if inferred copy is available + function InferenceResult(linfo::MethodInstance) + if isdefined(linfo, :inferred_const) + result = Const(linfo.inferred_const) + else + result = linfo.rettype + end + return new(linfo, EMPTY_VECTOR, result, nothing) + end +end + +function get_argtypes(result::InferenceResult) + result.args === EMPTY_VECTOR || return result.args # already cached + linfo = result.linfo + toplevel = !isa(linfo.def, Method) + atypes::SimpleVector = unwrap_unionall(linfo.specTypes).parameters + nargs::Int = toplevel ? 0 : linfo.def.nargs + args = Vector{Any}(uninitialized, nargs) + if !toplevel && linfo.def.isva + if linfo.specTypes == Tuple + if nargs > 1 + atypes = svec(Any[ Any for i = 1:(nargs - 1) ]..., Tuple.parameters[1]) + end + vararg_type = Tuple + else + vararg_type = rewrap(tupleparam_tail(atypes, nargs), linfo.specTypes) + end + args[nargs] = vararg_type + nargs -= 1 + end + laty = length(atypes) + if laty > 0 + if laty > nargs + laty = nargs + end + local lastatype + atail = laty + for i = 1:laty + atyp = atypes[i] + if i == laty && isvarargtype(atyp) + atyp = unwrap_unionall(atyp).parameters[1] + atail -= 1 + end + if isa(atyp, TypeVar) + atyp = atyp.ub + end + if isa(atyp, DataType) && isdefined(atyp, :instance) + # replace singleton types with their equivalent Const object + atyp = Const(atyp.instance) + elseif isconstType(atyp) + atyp = Const(atyp.parameters[1]) + else + atyp = rewrap_unionall(atyp, linfo.specTypes) + end + i == laty && (lastatype = atyp) + args[i] = atyp + end + for i = (atail + 1):nargs + args[i] = lastatype + end + else + @assert nargs == 0 "invalid specialization of method" # wrong number of arguments + end + result.args = args + return args +end + +function cache_lookup(code::MethodInstance, argtypes::Vector{Any}, cache::Vector{InferenceResult}) + method = code.def::Method + nargs::Int = method.nargs + method.isva && (nargs -= 1) + for cache_code in cache + # try to search cache first + cache_args = cache_code.args + if cache_code.linfo === code && length(cache_args) >= nargs + cache_match = true + # verify that the trailing args (va) aren't Const + for i in (nargs + 1):length(cache_args) + if isa(cache_args[i], Const) + cache_match = false + break + end + end + cache_match || continue + for i in 1:nargs + a = argtypes[i] + ca = cache_args[i] + # verify that all Const argument types match between the call and cache + if (isa(a, Const) || isa(ca, Const)) && !(a === ca) + cache_match = false + break + end + end + cache_match || continue + return cache_code + end + end + return nothing +end diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl new file mode 100644 index 0000000000000..29b8e708339b2 --- /dev/null +++ b/base/compiler/inferencestate.jl @@ -0,0 +1,233 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +const LineNum = Int + +mutable struct InferenceState + params::Params # describes how to compute the result + result::InferenceResult # remember where to put the result + linfo::MethodInstance # used here for the tuple (specTypes, env, Method) and world-age validity + sp::SimpleVector # static parameters + mod::Module + currpc::LineNum + + # info on the state of inference and the linfo + src::CodeInfo + min_valid::UInt + max_valid::UInt + nargs::Int + stmt_types::Vector{Any} + stmt_edges::Vector{Any} + # return type + bestguess #::Type + # current active instruction pointers + ip::BitSet + pc´´::LineNum + nstmts::Int + # current exception handler info + cur_hand #::Tuple{LineNum, Tuple{LineNum, ...}} + handler_at::Vector{Any} + n_handlers::Int + # ssavalue sparsity and restart info + ssavalue_uses::Vector{BitSet} + ssavalue_defs::Vector{LineNum} + vararg_type_container #::Type + + backedges::Vector{Tuple{InferenceState, LineNum}} # call-graph backedges connecting from callee to caller + callers_in_cycle::Vector{InferenceState} + parent::Union{Nothing, InferenceState} + + const_api::Bool + const_ret::Bool + + # TODO: move these to InferenceResult / Params? + optimize::Bool + cached::Bool + limited::Bool + inferred::Bool + dont_work_on_me::Bool + + # src is assumed to be a newly-allocated CodeInfo, that can be modified in-place to contain intermediate results + function InferenceState(result::InferenceResult, src::CodeInfo, + optimize::Bool, cached::Bool, params::Params) + linfo = result.linfo + code = src.code::Array{Any,1} + toplevel = !isa(linfo.def, Method) + + if !toplevel && isempty(linfo.sparam_vals) && !isempty(linfo.def.sparam_syms) + # linfo is unspecialized + sp = Any[] + sig = linfo.def.sig + while isa(sig, UnionAll) + push!(sp, sig.var) + sig = sig.body + end + sp = svec(sp...) + else + sp = linfo.sparam_vals + end + + nssavalues = src.ssavaluetypes::Int + src.ssavaluetypes = Any[ NOT_FOUND for i = 1:nssavalues ] + + n = length(code) + s_edges = Any[ () for i = 1:n ] + s_types = Any[ () for i = 1:n ] + + # initial types + nslots = length(src.slotnames) + argtypes = get_argtypes(result) + vararg_type_container = nothing + nargs = length(argtypes) + s_argtypes = VarTable(uninitialized, nslots) + src.slottypes = Vector{Any}(uninitialized, nslots) + for i in 1:nslots + at = (i > nargs) ? Bottom : argtypes[i] + if !toplevel && linfo.def.isva && i == nargs + if !(at == Tuple) # would just be a no-op + vararg_type_container = limit_tuple_depth(params, unwrap_unionall(at)) # TODO: should be limiting tuple depth much earlier than here + vararg_type = tuple_tfunc(vararg_type_container) # returns a Const object, if applicable + at = rewrap(vararg_type, linfo.specTypes) + end + end + s_argtypes[i] = VarState(at, i > nargs) + src.slottypes[i] = at + end + s_types[1] = s_argtypes + + ssavalue_uses = find_ssavalue_uses(code, nssavalues) + ssavalue_defs = find_ssavalue_defs(code, nssavalues) + + # exception handlers + cur_hand = () + handler_at = Any[ () for i=1:n ] + n_handlers = 0 + + W = BitSet() + push!(W, 1) #initial pc to visit + + if !toplevel + meth = linfo.def + inmodule = meth.module + else + inmodule = linfo.def::Module + end + + if cached && !toplevel + min_valid = min_world(linfo.def) + max_valid = max_world(linfo.def) + else + min_valid = typemax(UInt) + max_valid = typemin(UInt) + end + frame = new( + params, result, linfo, + sp, inmodule, 0, + src, min_valid, max_valid, + nargs, s_types, s_edges, + Union{}, W, 1, n, + cur_hand, handler_at, n_handlers, + ssavalue_uses, ssavalue_defs, vararg_type_container, + Vector{Tuple{InferenceState,LineNum}}(), # backedges + Vector{InferenceState}(), # callers_in_cycle + #=parent=#nothing, + false, false, optimize, cached, false, false, false) + result.result = frame + cached && push!(params.cache, result) + return frame + end +end + +function InferenceState(linfo::MethodInstance, optimize::Bool, cached::Bool, params::Params) + return InferenceState(InferenceResult(linfo), optimize, cached, params) +end + +function InferenceState(result::InferenceResult, optimize::Bool, cached::Bool, params::Params) + # prepare an InferenceState object for inferring lambda + src = retrieve_code_info(result.linfo) + src === nothing && return nothing + validate_code_in_debug_mode(result.linfo, src, "lowered") + return InferenceState(result, src, optimize, cached, params) +end + +_topmod(sv::InferenceState) = _topmod(sv.mod) + +# work towards converging the valid age range for sv +function update_valid_age!(min_valid::UInt, max_valid::UInt, sv::InferenceState) + sv.min_valid = max(sv.min_valid, min_valid) + sv.max_valid = min(sv.max_valid, max_valid) + @assert(!isa(sv.linfo.def, Method) || + !sv.cached || + sv.min_valid <= sv.params.world <= sv.max_valid, + "invalid age range update") + nothing +end + +update_valid_age!(edge::InferenceState, sv::InferenceState) = update_valid_age!(edge.min_valid, edge.max_valid, sv) +update_valid_age!(li::MethodInstance, sv::InferenceState) = update_valid_age!(min_world(li), max_world(li), sv) + +function record_ssa_assign(ssa_id::Int, @nospecialize(new), frame::InferenceState) + old = frame.src.ssavaluetypes[ssa_id] + if old === NOT_FOUND || !(new ⊑ old) + frame.src.ssavaluetypes[ssa_id] = tmerge(old, new) + W = frame.ip + s = frame.stmt_types + for r in frame.ssavalue_uses[ssa_id] + if s[r] !== () # s[r] === () => unreached statement + if r < frame.pc´´ + frame.pc´´ = r + end + push!(W, r) + end + end + end + nothing +end + +function add_backedge!(frame::InferenceState, caller::InferenceState, currpc::Int) + update_valid_age!(frame, caller) + backedge = (caller, currpc) + contains_is(frame.backedges, backedge) || push!(frame.backedges, backedge) + return frame +end + +# temporarily accumulate our edges to later add as backedges in the callee +function add_backedge!(li::MethodInstance, caller::InferenceState) + isa(caller.linfo.def, Method) || return # don't add backedges to toplevel exprs + if caller.stmt_edges[caller.currpc] === () + caller.stmt_edges[caller.currpc] = [] + end + push!(caller.stmt_edges[caller.currpc], li) + update_valid_age!(li, caller) + nothing +end + +# used to temporarily accumulate our no method errors to later add as backedges in the callee method table +function add_mt_backedge!(mt::MethodTable, @nospecialize(typ), caller::InferenceState) + isa(caller.linfo.def, Method) || return # don't add backedges to toplevel exprs + if caller.stmt_edges[caller.currpc] === () + caller.stmt_edges[caller.currpc] = [] + end + push!(caller.stmt_edges[caller.currpc], mt) + push!(caller.stmt_edges[caller.currpc], typ) + nothing +end + +function is_specializable_vararg_slot(@nospecialize(arg), sv::InferenceState) + return (isa(arg, Slot) && slot_id(arg) == sv.nargs && + isa(sv.vararg_type_container, DataType)) +end + +function print_callstack(sv::InferenceState) + while sv !== nothing + print(sv.linfo) + sv.limited && print(" [limited]") + !sv.cached && print(" [uncached]") + println() + for cycle in sv.callers_in_cycle + print(' ', cycle.linfo) + cycle.limited && print(" [limited]") + println() + end + sv = sv.parent + end +end diff --git a/base/inference.jl b/base/compiler/optimize.jl similarity index 51% rename from base/inference.jl rename to base/compiler/optimize.jl index a1f868ceae42a..45473166a99ca 100644 --- a/base/inference.jl +++ b/base/compiler/optimize.jl @@ -1,392 +1,8 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -import Core: _apply, svec, apply_type, Builtin, IntrinsicFunction, MethodInstance - -#### parameters limiting potentially-infinite types #### -const MAX_TYPEUNION_LEN = 3 -const MAX_TYPE_DEPTH = 8 -const TUPLE_COMPLEXITY_LIMIT_DEPTH = 3 - -const MAX_INLINE_CONST_SIZE = 256 - -const empty_vector = Vector{Any}() - -mutable struct InferenceResult - linfo::MethodInstance - args::Vector{Any} - result # ::Type, or InferenceState if WIP - src::Union{CodeInfo, Nothing} # if inferred copy is available - function InferenceResult(linfo::MethodInstance) - if isdefined(linfo, :inferred_const) - result = Const(linfo.inferred_const) - else - result = linfo.rettype - end - return new(linfo, empty_vector, result, nothing) - end -end - -function get_argtypes(result::InferenceResult) - result.args === empty_vector || return result.args # already cached - linfo = result.linfo - toplevel = !isa(linfo.def, Method) - atypes::SimpleVector = unwrap_unionall(linfo.specTypes).parameters - nargs::Int = toplevel ? 0 : linfo.def.nargs - args = Vector{Any}(uninitialized, nargs) - if !toplevel && linfo.def.isva - if linfo.specTypes == Tuple - if nargs > 1 - atypes = svec(Any[ Any for i = 1:(nargs - 1) ]..., Tuple.parameters[1]) - end - vararg_type = Tuple - else - vararg_type = rewrap(tupleparam_tail(atypes, nargs), linfo.specTypes) - end - args[nargs] = vararg_type - nargs -= 1 - end - laty = length(atypes) - if laty > 0 - if laty > nargs - laty = nargs - end - local lastatype - atail = laty - for i = 1:laty - atyp = atypes[i] - if i == laty && isvarargtype(atyp) - atyp = unwrap_unionall(atyp).parameters[1] - atail -= 1 - end - if isa(atyp, TypeVar) - atyp = atyp.ub - end - if isa(atyp, DataType) && isdefined(atyp, :instance) - # replace singleton types with their equivalent Const object - atyp = Const(atyp.instance) - elseif isconstType(atyp) - atyp = Const(atyp.parameters[1]) - else - atyp = rewrap_unionall(atyp, linfo.specTypes) - end - i == laty && (lastatype = atyp) - args[i] = atyp - end - for i = (atail + 1):nargs - args[i] = lastatype - end - else - @assert nargs == 0 "invalid specialization of method" # wrong number of arguments - end - result.args = args - return args -end - -struct InferenceParams - cache::Vector{InferenceResult} - world::UInt - - # optimization - inlining::Bool - ipo_constant_propagation::Bool - aggressive_constant_propagation::Bool - inline_cost_threshold::Int # number of CPU cycles beyond which it's not worth inlining - inline_nonleaf_penalty::Int # penalty for dynamic dispatch - inline_tupleret_bonus::Int # extra willingness for non-isbits tuple return types - - # don't consider more than N methods. this trades off between - # compiler performance and generated code performance. - # typically, considering many methods means spending lots of time - # obtaining poor type information. - # It is important for N to be >= the number of methods in the error() - # function, so we can still know that error() is always Bottom. - MAX_METHODS::Int - # the maximum number of union-tuples to swap / expand - # before computing the set of matching methods - MAX_UNION_SPLITTING::Int - # the maximum number of union-tuples to swap / expand - # when inferring a call to _apply - MAX_APPLY_UNION_ENUM::Int - - # parameters limiting large types - MAX_TUPLETYPE_LEN::Int - MAX_TUPLE_DEPTH::Int - - # when attempting to inlining _apply, abort the optimization if the tuple - # contains more than this many elements - MAX_TUPLE_SPLAT::Int - - # reasonable defaults - function InferenceParams(world::UInt; - inlining::Bool = inlining_enabled(), - inline_cost_threshold::Int = 100, - inline_nonleaf_penalty::Int = 1000, - inline_tupleret_bonus::Int = 400, - max_methods::Int = 4, - tupletype_len::Int = 15, - tuple_depth::Int = 4, - tuple_splat::Int = 16, - union_splitting::Int = 4, - apply_union_enum::Int = 8) - return new(Vector{InferenceResult}(), - world, inlining, true, false, inline_cost_threshold, inline_nonleaf_penalty, - inline_tupleret_bonus, max_methods, union_splitting, apply_union_enum, - tupletype_len, tuple_depth, tuple_splat) - end -end - -# slot property bit flags -# The slot is has uses that are not statically dominated by any assignment -# This is implied by `Slot_UsedUndef`. -# If this is not set, all the uses are (statically) dominated by the defs. -# In particular, if a slot has `AssignedOnce && !StaticUndef`, it is an SSA. -const Slot_StaticUndef = 1 -# The slot is assigned to only once -const Slot_AssignedOnce = 16 -# The slot has uses that might raise UndefVarError -const Slot_UsedUndef = 32 -# const Slot_Called = 64 - -#### inference state types #### - -struct NotFound end -const NF = NotFound() -const LineNum = Int -const VarTable = Array{Any,1} - -const isleaftype = _isleaftype - -# The type of a variable load is either a value or an UndefVarError -mutable struct VarState - typ - undef::Bool - VarState(@nospecialize(typ), undef::Bool) = new(typ, undef) -end - -# The type of a value might be constant -struct Const - val - actual::Bool # if true, we obtained `val` by actually calling a @pure function - Const(@nospecialize(v)) = new(v, false) - Const(@nospecialize(v), a::Bool) = new(v, a) -end - -# The type of this value might be Bool. -# However, to enable a limited amount of back-propagagation, -# we also keep some information about how this Bool value was created. -# In particular, if you branch on this value, then may assume that in -# the true branch, the type of `var` will be limited by `vtype` and in -# the false branch, it will be limited by `elsetype`. Example: -# ``` -# cond = isa(x::Union{Int, Float}, Int)::Conditional(x, Int, Float) -# if cond -# # May assume x is `Int` now -# else -# # May assume x is `Float` now -# end -# ``` -mutable struct Conditional - var::Union{Slot,SSAValue} - vtype - elsetype - function Conditional( - @nospecialize(var), - @nospecialize(vtype), - @nospecialize(nottype)) - return new(var, vtype, nottype) - end -end - -struct PartialTypeVar - tv::TypeVar - # N.B.: Currently unused, but would allow turning something back - # into Const, if the bounds are pulled out of this TypeVar - lb_certain::Bool - ub_certain::Bool - PartialTypeVar(tv::TypeVar, lb_certain::Bool, ub_certain::Bool) = new(tv, lb_certain, ub_certain) -end - -function rewrap(@nospecialize(t), @nospecialize(u)) - isa(t, Const) && return t - isa(t, Conditional) && return t - return rewrap_unionall(t, u) -end - -mutable struct InferenceState - params::InferenceParams # describes how to compute the result - result::InferenceResult # remember where to put the result - linfo::MethodInstance # used here for the tuple (specTypes, env, Method) and world-age validity - sp::SimpleVector # static parameters - mod::Module - currpc::LineNum - - # info on the state of inference and the linfo - src::CodeInfo - min_valid::UInt - max_valid::UInt - nargs::Int - stmt_types::Vector{Any} - stmt_edges::Vector{Any} - # return type - bestguess #::Type - # current active instruction pointers - ip::BitSet - pc´´::LineNum - nstmts::Int - # current exception handler info - cur_hand #::Tuple{LineNum, Tuple{LineNum, ...}} - handler_at::Vector{Any} - n_handlers::Int - # ssavalue sparsity and restart info - ssavalue_uses::Vector{BitSet} - ssavalue_defs::Vector{LineNum} - vararg_type_container #::Type - - backedges::Vector{Tuple{InferenceState, LineNum}} # call-graph backedges connecting from callee to caller - callers_in_cycle::Vector{InferenceState} - parent::Union{Nothing, InferenceState} - - const_api::Bool - const_ret::Bool - - # TODO: move these to InferenceResult / InferenceParams? - optimize::Bool - cached::Bool - limited::Bool - inferred::Bool - dont_work_on_me::Bool - - # src is assumed to be a newly-allocated CodeInfo, that can be modified in-place to contain intermediate results - function InferenceState(result::InferenceResult, src::CodeInfo, - optimize::Bool, cached::Bool, params::InferenceParams) - linfo = result.linfo - code = src.code::Array{Any,1} - toplevel = !isa(linfo.def, Method) - - if !toplevel && isempty(linfo.sparam_vals) && !isempty(linfo.def.sparam_syms) - # linfo is unspecialized - sp = Any[] - sig = linfo.def.sig - while isa(sig, UnionAll) - push!(sp, sig.var) - sig = sig.body - end - sp = svec(sp...) - else - sp = linfo.sparam_vals - end - - nssavalues = src.ssavaluetypes::Int - src.ssavaluetypes = Any[ NF for i = 1:nssavalues ] - - n = length(code) - s_edges = Any[ () for i = 1:n ] - s_types = Any[ () for i = 1:n ] - - # initial types - nslots = length(src.slotnames) - argtypes = get_argtypes(result) - vararg_type_container = nothing - nargs = length(argtypes) - s_argtypes = VarTable(uninitialized, nslots) - src.slottypes = Vector{Any}(uninitialized, nslots) - for i in 1:nslots - at = (i > nargs) ? Bottom : argtypes[i] - if !toplevel && linfo.def.isva && i == nargs - if !(at == Tuple) # would just be a no-op - vararg_type_container = limit_tuple_depth(params, unwrap_unionall(at)) # TODO: should be limiting tuple depth much earlier than here - vararg_type = tuple_tfunc(vararg_type_container) # returns a Const object, if applicable - at = rewrap(vararg_type, linfo.specTypes) - end - end - s_argtypes[i] = VarState(at, i > nargs) - src.slottypes[i] = at - end - s_types[1] = s_argtypes - - ssavalue_uses = find_ssavalue_uses(code, nssavalues) - ssavalue_defs = find_ssavalue_defs(code, nssavalues) - - # exception handlers - cur_hand = () - handler_at = Any[ () for i=1:n ] - n_handlers = 0 - - W = BitSet() - push!(W, 1) #initial pc to visit - - if !toplevel - meth = linfo.def - inmodule = meth.module - else - inmodule = linfo.def::Module - end - - if cached && !toplevel - min_valid = min_world(linfo.def) - max_valid = max_world(linfo.def) - else - min_valid = typemax(UInt) - max_valid = typemin(UInt) - end - frame = new( - params, result, linfo, - sp, inmodule, 0, - src, min_valid, max_valid, - nargs, s_types, s_edges, - Union{}, W, 1, n, - cur_hand, handler_at, n_handlers, - ssavalue_uses, ssavalue_defs, vararg_type_container, - Vector{Tuple{InferenceState,LineNum}}(), # backedges - Vector{InferenceState}(), # callers_in_cycle - #=parent=#nothing, - false, false, optimize, cached, false, false, false) - result.result = frame - cached && push!(params.cache, result) - return frame - end -end - - -function InferenceState(linfo::MethodInstance, - optimize::Bool, cached::Bool, params::InferenceParams) - return InferenceState(InferenceResult(linfo), optimize, cached, params) -end -function InferenceState(result::InferenceResult, - optimize::Bool, cached::Bool, params::InferenceParams) - # prepare an InferenceState object for inferring lambda - src = retrieve_code_info(result.linfo) - src === nothing && return nothing - _validate(result.linfo, src, "lowered") - return InferenceState(result, src, optimize, cached, params) -end - -function _validate(linfo::MethodInstance, src::CodeInfo, kind::String) - if JLOptions().debug_level == 2 - # this is a debug build of julia, so let's validate linfo - errors = validate_code(linfo, src) - if !isempty(errors) - for e in errors - if linfo.def isa Method - println(STDERR, "WARNING: Encountered invalid ", kind, " code for method ", - linfo.def, ": ", e) - else - println(STDERR, "WARNING: Encountered invalid ", kind, " code for top level expression in ", - linfo.def, ": ", e) - end - end - end - end -end - -function get_staged(li::MethodInstance) - try - # user code might throw errors – ignore them - return ccall(:jl_code_for_staged, Any, (Any,), li)::CodeInfo - catch - return nothing - end -end +##################### +# OptimizationState # +##################### mutable struct OptimizationState linfo::MethodInstance @@ -398,7 +14,7 @@ mutable struct OptimizationState next_label::Int # index of the current highest label for this function min_valid::UInt max_valid::UInt - params::InferenceParams + params::Params function OptimizationState(frame::InferenceState) s_edges = frame.stmt_edges[1] if s_edges === () @@ -413,7 +29,7 @@ mutable struct OptimizationState frame.params) end function OptimizationState(linfo::MethodInstance, src::CodeInfo, - params::InferenceParams) + params::Params) # prepare src for running optimization passes # if it isn't already nssavalues = src.ssavaluetypes @@ -446,3314 +62,183 @@ mutable struct OptimizationState end end -function OptimizationState(linfo::MethodInstance, params::InferenceParams) +function OptimizationState(linfo::MethodInstance, params::Params) src = retrieve_code_info(linfo) src === nothing && return nothing return OptimizationState(linfo, src, params) end +_topmod(sv::OptimizationState) = _topmod(sv.mod) -#### debugging utilities #### - -function print_callstack(sv::InferenceState) - while sv !== nothing - print(sv.linfo) - sv.limited && print(" [limited]") - !sv.cached && print(" [uncached]") - println() - for cycle in sv.callers_in_cycle - print(' ', cycle.linfo) - cycle.limited && print(" [limited]") - println() - end - sv = sv.parent - end -end - - -#### helper functions #### - -# create copies of the CodeInfo definition, and any fields that type-inference might modify -function copy_code_info(c::CodeInfo) - cnew = ccall(:jl_copy_code_info, Ref{CodeInfo}, (Any,), c) - cnew.code = copy_exprargs(cnew.code) - cnew.slotnames = copy(cnew.slotnames) - cnew.slotflags = copy(cnew.slotflags) - return cnew -end - -function retrieve_code_info(linfo::MethodInstance) - m = linfo.def::Method - if isdefined(m, :generator) - return get_staged(linfo) - else - # TODO: post-inference see if we can swap back to the original arrays? - if isa(m.source, Array{UInt8,1}) - c = ccall(:jl_uncompress_ast, Any, (Any, Any), m, m.source) - else - c = copy_code_info(m.source) - end - end - return c -end - -# TODO: Use these functions instead of directly manipulating -# the "actual" method for appropriate places in inference (see #24676) -function method_for_inference_heuristics(cinfo, default) - if isa(cinfo, CodeInfo) - # appropriate format for `sig` is svec(ftype, argtypes, world) - sig = cinfo.signature_for_inference_heuristics - if isa(sig, SimpleVector) && length(sig) == 3 - methods = _methods(sig[1], sig[2], -1, sig[3]) - if length(methods) == 1 - _, _, m = methods[] - if isa(m, Method) - return m - end - end - end - end - return default -end - -function method_for_inference_heuristics(method::Method, @nospecialize(sig), sparams, world) - if isdefined(method, :generator) && method.generator.expand_early - method_instance = code_for_method(method, sig, sparams, world, false) - if isa(method_instance, MethodInstance) - return method_for_inference_heuristics(get_staged(method_instance), method) - end - end - return method -end - -@inline slot_id(s) = isa(s, SlotNumber) ? (s::SlotNumber).id : (s::TypedSlot).id # using a function to ensure we can infer this - -# avoid cycle due to over-specializing `any` when used by inference -function _any(@nospecialize(f), a) - for x in a - f(x) && return true - end - return false -end +genlabel(sv::OptimizationState) = LabelNode(sv.next_label += 1) -function contains_is(itr, @nospecialize(x)) - for y in itr - if y === x - return true - end - end - return false +function newvar!(sv::OptimizationState, @nospecialize(typ)) + id = length(sv.src.ssavaluetypes) + push!(sv.src.ssavaluetypes, typ) + return SSAValue(id) end -anymap(f::Function, a::Array{Any,1}) = Any[ f(a[i]) for i in 1:length(a) ] - -_topmod(sv::OptimizationState) = _topmod(sv.mod) -_topmod(sv::InferenceState) = _topmod(sv.mod) -_topmod(m::Module) = ccall(:jl_base_relative_to, Any, (Any,), m)::Module - -function istopfunction(topmod, @nospecialize(f), sym) - if isdefined(Main, :Base) && isdefined(Main.Base, sym) && isconst(Main.Base, sym) && f === getfield(Main.Base, sym) - return true - elseif isdefined(topmod, sym) && isconst(topmod, sym) && f === getfield(topmod, sym) - return true - end - return false +function update_valid_age!(min_valid::UInt, max_valid::UInt, sv::OptimizationState) + sv.min_valid = max(sv.min_valid, min_valid) + sv.max_valid = min(sv.max_valid, max_valid) + @assert(!isa(sv.linfo.def, Method) || + (sv.min_valid == typemax(UInt) && sv.max_valid == typemin(UInt)) || + sv.min_valid <= sv.params.world <= sv.max_valid, + "invalid age range update") + nothing end -isknownlength(t::DataType) = !isvatuple(t) || - (length(t.parameters) > 0 && isa(unwrap_unionall(t.parameters[end]).parameters[2],Int)) +update_valid_age!(li::MethodInstance, sv::OptimizationState) = update_valid_age!(min_world(li), max_world(li), sv) -# t[n:end] -function tupleparam_tail(t::SimpleVector, n) - lt = length(t) - if n > lt - va = t[lt] - if isvarargtype(va) - # assumes that we should never see Vararg{T, x}, where x is a constant (should be guaranteed by construction) - return Tuple{va} - end - return Tuple{} - end - return Tuple{t[n:lt]...} +function add_backedge!(li::MethodInstance, caller::OptimizationState) + isa(caller.linfo.def, Method) || return # don't add backedges to toplevel exprs + push!(caller.backedges, li) + update_valid_age!(li, caller) + nothing end -function is_specializable_vararg_slot(@nospecialize(arg), sv::Union{InferenceState, OptimizationState}) +function is_specializable_vararg_slot(@nospecialize(arg), sv::OptimizationState) return (isa(arg, Slot) && slot_id(arg) == sv.nargs && isa(sv.vararg_type_container, DataType)) end -function is_self_quoting(@nospecialize(x)) - return isa(x,Number) || isa(x,AbstractString) || isa(x,Tuple) || isa(x,Type) || - isa(x,Char) || x === nothing || isa(x,Function) -end - -function quoted(@nospecialize(x)) - return is_self_quoting(x) ? x : QuoteNode(x) -end - - -#### type-functions for builtins / intrinsics #### - -const _Type_name = Type.body.name -isType(@nospecialize t) = isa(t, DataType) && (t::DataType).name === _Type_name - -const _NamedTuple_name = NamedTuple.body.body.name - -# true if Type is inlineable as constant (is a singleton) -function isconstType(@nospecialize t) - isType(t) || return false - p1 = t.parameters[1] - # typeof(Bottom) is special since even though it is as leaftype, - # at runtime, it might be Type{Union{}} instead, so don't attempt inference of it - p1 === typeof(Union{}) && return false - p1 === Union{} && return true - isleaftype(p1) && return true - return false -end - -iskindtype(@nospecialize t) = (t === DataType || t === UnionAll || t === Union || t === typeof(Bottom)) - -const IInf = typemax(Int) # integer infinity -const n_ifunc = reinterpret(Int32, arraylen) + 1 -const t_ifunc = Vector{Tuple{Int, Int, Any}}(uninitialized, n_ifunc) -const t_ifunc_cost = Vector{Int}(uninitialized, n_ifunc) -const t_ffunc_key = Vector{Any}() -const t_ffunc_val = Vector{Tuple{Int, Int, Any}}() -const t_ffunc_cost = Vector{Int}() -function add_tfunc(f::IntrinsicFunction, minarg::Int, maxarg::Int, @nospecialize(tfunc), cost::Int) - idx = reinterpret(Int32, f) + 1 - t_ifunc[idx] = (minarg, maxarg, tfunc) - t_ifunc_cost[idx] = cost -end -# TODO: add @nospecialize on `f` and declare its type as `Builtin` when that's supported -function add_tfunc(f::Function, minarg::Int, maxarg::Int, @nospecialize(tfunc), cost::Int) - push!(t_ffunc_key, f) - push!(t_ffunc_val, (minarg, maxarg, tfunc)) - push!(t_ffunc_cost, cost) -end - -add_tfunc(throw, 1, 1, (@nospecialize(x)) -> Bottom, 0) - -# the inverse of typeof_tfunc -# returns (type, isexact) -# if isexact is false, the actual runtime type may (will) be a subtype of t -function instanceof_tfunc(@nospecialize(t)) - if t === Bottom || t === typeof(Bottom) - return Bottom, true - elseif isa(t, Const) - if isa(t.val, Type) - return t.val, true - end - elseif isType(t) - tp = t.parameters[1] - return tp, !has_free_typevars(tp) - elseif isa(t, UnionAll) - t′ = unwrap_unionall(t) - t′′, isexact = instanceof_tfunc(t′) - return rewrap_unionall(t′′, t), isexact - elseif isa(t, Union) - ta, isexact_a = instanceof_tfunc(t.a) - tb, isexact_b = instanceof_tfunc(t.b) - return Union{ta, tb}, false # at runtime, will be exactly one of these - end - return Any, false -end -bitcast_tfunc(@nospecialize(t), @nospecialize(x)) = instanceof_tfunc(t)[1] -math_tfunc(@nospecialize(x)) = widenconst(x) -math_tfunc(@nospecialize(x), @nospecialize(y)) = widenconst(x) -math_tfunc(@nospecialize(x), @nospecialize(y), @nospecialize(z)) = widenconst(x) -fptoui_tfunc(@nospecialize(t), @nospecialize(x)) = bitcast_tfunc(t, x) -fptosi_tfunc(@nospecialize(t), @nospecialize(x)) = bitcast_tfunc(t, x) -function fptoui_tfunc(@nospecialize(x)) - T = widenconst(x) - T === Float64 && return UInt64 - T === Float32 && return UInt32 - T === Float16 && return UInt16 - return Any -end -function fptosi_tfunc(@nospecialize(x)) - T = widenconst(x) - T === Float64 && return Int64 - T === Float32 && return Int32 - T === Float16 && return Int16 - return Any -end - - ## conversion ## -add_tfunc(bitcast, 2, 2, bitcast_tfunc, 1) -add_tfunc(sext_int, 2, 2, bitcast_tfunc, 1) -add_tfunc(zext_int, 2, 2, bitcast_tfunc, 1) -add_tfunc(trunc_int, 2, 2, bitcast_tfunc, 1) -add_tfunc(fptoui, 1, 2, fptoui_tfunc, 1) -add_tfunc(fptosi, 1, 2, fptosi_tfunc, 1) -add_tfunc(uitofp, 2, 2, bitcast_tfunc, 1) -add_tfunc(sitofp, 2, 2, bitcast_tfunc, 1) -add_tfunc(fptrunc, 2, 2, bitcast_tfunc, 1) -add_tfunc(fpext, 2, 2, bitcast_tfunc, 1) - ## arithmetic ## -add_tfunc(neg_int, 1, 1, math_tfunc, 1) -add_tfunc(add_int, 2, 2, math_tfunc, 1) -add_tfunc(sub_int, 2, 2, math_tfunc, 1) -add_tfunc(mul_int, 2, 2, math_tfunc, 4) -add_tfunc(sdiv_int, 2, 2, math_tfunc, 30) -add_tfunc(udiv_int, 2, 2, math_tfunc, 30) -add_tfunc(srem_int, 2, 2, math_tfunc, 30) -add_tfunc(urem_int, 2, 2, math_tfunc, 30) -add_tfunc(add_ptr, 2, 2, math_tfunc, 1) -add_tfunc(sub_ptr, 2, 2, math_tfunc, 1) -add_tfunc(neg_float, 1, 1, math_tfunc, 1) -add_tfunc(add_float, 2, 2, math_tfunc, 1) -add_tfunc(sub_float, 2, 2, math_tfunc, 1) -add_tfunc(mul_float, 2, 2, math_tfunc, 4) -add_tfunc(div_float, 2, 2, math_tfunc, 20) -add_tfunc(rem_float, 2, 2, math_tfunc, 20) -add_tfunc(fma_float, 3, 3, math_tfunc, 5) -add_tfunc(muladd_float, 3, 3, math_tfunc, 5) - ## fast arithmetic ## -add_tfunc(neg_float_fast, 1, 1, math_tfunc, 1) -add_tfunc(add_float_fast, 2, 2, math_tfunc, 1) -add_tfunc(sub_float_fast, 2, 2, math_tfunc, 1) -add_tfunc(mul_float_fast, 2, 2, math_tfunc, 2) -add_tfunc(div_float_fast, 2, 2, math_tfunc, 10) -add_tfunc(rem_float_fast, 2, 2, math_tfunc, 10) - ## bitwise operators ## -add_tfunc(and_int, 2, 2, math_tfunc, 1) -add_tfunc(or_int, 2, 2, math_tfunc, 1) -add_tfunc(xor_int, 2, 2, math_tfunc, 1) -add_tfunc(not_int, 1, 1, math_tfunc, 1) -add_tfunc(shl_int, 2, 2, math_tfunc, 1) -add_tfunc(lshr_int, 2, 2, math_tfunc, 1) -add_tfunc(ashr_int, 2, 2, math_tfunc, 1) -add_tfunc(bswap_int, 1, 1, math_tfunc, 1) -add_tfunc(ctpop_int, 1, 1, math_tfunc, 1) -add_tfunc(ctlz_int, 1, 1, math_tfunc, 1) -add_tfunc(cttz_int, 1, 1, math_tfunc, 1) -add_tfunc(checked_sdiv_int, 2, 2, math_tfunc, 40) -add_tfunc(checked_udiv_int, 2, 2, math_tfunc, 40) -add_tfunc(checked_srem_int, 2, 2, math_tfunc, 40) -add_tfunc(checked_urem_int, 2, 2, math_tfunc, 40) - ## functions ## -add_tfunc(abs_float, 1, 1, math_tfunc, 2) -add_tfunc(copysign_float, 2, 2, math_tfunc, 2) -add_tfunc(flipsign_int, 2, 2, math_tfunc, 1) -add_tfunc(ceil_llvm, 1, 1, math_tfunc, 10) -add_tfunc(floor_llvm, 1, 1, math_tfunc, 10) -add_tfunc(trunc_llvm, 1, 1, math_tfunc, 10) -add_tfunc(rint_llvm, 1, 1, math_tfunc, 10) -add_tfunc(sqrt_llvm, 1, 1, math_tfunc, 20) - ## same-type comparisons ## -cmp_tfunc(@nospecialize(x), @nospecialize(y)) = Bool -add_tfunc(eq_int, 2, 2, cmp_tfunc, 1) -add_tfunc(ne_int, 2, 2, cmp_tfunc, 1) -add_tfunc(slt_int, 2, 2, cmp_tfunc, 1) -add_tfunc(ult_int, 2, 2, cmp_tfunc, 1) -add_tfunc(sle_int, 2, 2, cmp_tfunc, 1) -add_tfunc(ule_int, 2, 2, cmp_tfunc, 1) -add_tfunc(eq_float, 2, 2, cmp_tfunc, 2) -add_tfunc(ne_float, 2, 2, cmp_tfunc, 2) -add_tfunc(lt_float, 2, 2, cmp_tfunc, 2) -add_tfunc(le_float, 2, 2, cmp_tfunc, 2) -add_tfunc(fpiseq, 2, 2, cmp_tfunc, 1) -add_tfunc(fpislt, 2, 2, cmp_tfunc, 1) -add_tfunc(eq_float_fast, 2, 2, cmp_tfunc, 1) -add_tfunc(ne_float_fast, 2, 2, cmp_tfunc, 1) -add_tfunc(lt_float_fast, 2, 2, cmp_tfunc, 1) -add_tfunc(le_float_fast, 2, 2, cmp_tfunc, 1) - - ## checked arithmetic ## -chk_tfunc(@nospecialize(x), @nospecialize(y)) = Tuple{widenconst(x), Bool} -add_tfunc(checked_sadd_int, 2, 2, chk_tfunc, 10) -add_tfunc(checked_uadd_int, 2, 2, chk_tfunc, 10) -add_tfunc(checked_ssub_int, 2, 2, chk_tfunc, 10) -add_tfunc(checked_usub_int, 2, 2, chk_tfunc, 10) -add_tfunc(checked_smul_int, 2, 2, chk_tfunc, 10) -add_tfunc(checked_umul_int, 2, 2, chk_tfunc, 10) - ## other, misc intrinsics ## -add_tfunc(Core.Intrinsics.llvmcall, 3, IInf, - (@nospecialize(fptr), @nospecialize(rt), @nospecialize(at), a...) -> instanceof_tfunc(rt)[1], 10) -cglobal_tfunc(@nospecialize(fptr)) = Ptr{Cvoid} -cglobal_tfunc(@nospecialize(fptr), @nospecialize(t)) = (isType(t) ? Ptr{t.parameters[1]} : Ptr) -cglobal_tfunc(@nospecialize(fptr), t::Const) = (isa(t.val, Type) ? Ptr{t.val} : Ptr) -add_tfunc(Core.Intrinsics.cglobal, 1, 2, cglobal_tfunc, 5) -add_tfunc(Core.Intrinsics.select_value, 3, 3, - function (@nospecialize(cnd), @nospecialize(x), @nospecialize(y)) - if isa(cnd, Const) - if cnd.val === true - return x - elseif cnd.val === false - return y - else - return Bottom - end - end - (Bool ⊑ cnd) || return Bottom - return tmerge(x, y) - end, 1) -add_tfunc(===, 2, 2, - function (@nospecialize(x), @nospecialize(y)) - if isa(x, Const) && isa(y, Const) - return Const(x.val === y.val) - elseif typeintersect(widenconst(x), widenconst(y)) === Bottom - return Const(false) - elseif (isa(x, Const) && y === typeof(x.val) && isdefined(y, :instance)) || - (isa(y, Const) && x === typeof(y.val) && isdefined(x, :instance)) - return Const(true) - elseif isa(x, Conditional) && isa(y, Const) - y.val === false && return Conditional(x.var, x.elsetype, x.vtype) - y.val === true && return x - return x - elseif isa(y, Conditional) && isa(x, Const) - x.val === false && return Conditional(y.var, y.elsetype, y.vtype) - x.val === true && return y - end - return Bool - end, 1) -function isdefined_tfunc(args...) - arg1 = args[1] - if isa(arg1, Const) - a1 = typeof(arg1.val) - else - a1 = widenconst(arg1) - end - if isType(a1) - return Bool - end - a1 = unwrap_unionall(a1) - if isa(a1, DataType) && !a1.abstract - if a1 <: Array # TODO update when deprecation is removed - elseif a1 === Module - length(args) == 2 || return Bottom - sym = args[2] - Symbol <: widenconst(sym) || return Bottom - if isa(sym, Const) && isa(sym.val, Symbol) && isa(arg1, Const) && isdefined(arg1.val, sym.val) - return Const(true) - end - elseif length(args) == 2 && isa(args[2], Const) - val = args[2].val - idx::Int = 0 - if isa(val, Symbol) - idx = fieldindex(a1, val, false) - elseif isa(val, Int) - idx = val - else - return Bottom - end - if 1 <= idx <= a1.ninitialized - return Const(true) - elseif a1.name === _NamedTuple_name - if isleaftype(a1) - return Const(false) - end - elseif idx <= 0 || (!isvatuple(a1) && idx > fieldcount(a1)) - return Const(false) - elseif !isvatuple(a1) && isbits(fieldtype(a1, idx)) - return Const(true) - elseif isa(arg1, Const) && isimmutable((arg1::Const).val) - return Const(isdefined((arg1::Const).val, idx)) - end - end - end - Bool -end -# TODO change IInf to 2 when deprecation is removed -add_tfunc(isdefined, 1, IInf, isdefined_tfunc, 1) -_const_sizeof(@nospecialize(x)) = try - # Constant Vector does not have constant size - isa(x, Vector) && return Int - return Const(Core.sizeof(x)) -catch - return Int -end -add_tfunc(Core.sizeof, 1, 1, - function (@nospecialize(x),) - isa(x, Const) && return _const_sizeof(x.val) - isa(x, Conditional) && return _const_sizeof(Bool) - isconstType(x) && return _const_sizeof(x.parameters[1]) - x !== DataType && isleaftype(x) && return _const_sizeof(x) - return Int - end, 0) -old_nfields(@nospecialize x) = length((isa(x,DataType) ? x : typeof(x)).types) -add_tfunc(nfields, 1, 1, - function (@nospecialize(x),) - isa(x,Const) && return Const(old_nfields(x.val)) - isa(x,Conditional) && return Const(old_nfields(Bool)) - if isType(x) - # TODO: remove with deprecation in builtins.c for nfields(::Type) - isleaftype(x.parameters[1]) && return Const(old_nfields(x.parameters[1])) - elseif isa(x,DataType) && !x.abstract && !(x.name === Tuple.name && isvatuple(x)) && x !== DataType - if !(x.name === _NamedTuple_name && !isleaftype(x)) - return Const(length(x.types)) - end - end - return Int - end, 0) -add_tfunc(Core._expr, 1, IInf, (args...)->Expr, 100) -add_tfunc(applicable, 1, IInf, (@nospecialize(f), args...)->Bool, 100) -add_tfunc(Core.Intrinsics.arraylen, 1, 1, x->Int, 4) -add_tfunc(arraysize, 2, 2, (@nospecialize(a), @nospecialize(d))->Int, 4) -add_tfunc(pointerref, 3, 3, - function (@nospecialize(a), @nospecialize(i), @nospecialize(align)) - a = widenconst(a) - if a <: Ptr - if isa(a,DataType) && isa(a.parameters[1],Type) - return a.parameters[1] - elseif isa(a,UnionAll) && !has_free_typevars(a) - unw = unwrap_unionall(a) - if isa(unw,DataType) - return rewrap_unionall(unw.parameters[1], a) - end - end - end - return Any - end, 4) -add_tfunc(pointerset, 4, 4, (@nospecialize(a), @nospecialize(v), @nospecialize(i), @nospecialize(align)) -> a, 5) - -function typeof_tfunc(@nospecialize(t)) - if isa(t, Const) - return Const(typeof(t.val)) - elseif isa(t, Conditional) - return Const(Bool) - elseif isType(t) - tp = t.parameters[1] - if !isleaftype(tp) - return DataType # typeof(Kind::Type)::DataType - else - return Const(typeof(tp)) # XXX: this is not necessarily true - end - elseif isa(t, DataType) - if isleaftype(t) || isvarargtype(t) - return Const(t) - elseif t === Any - return DataType - else - return Type{<:t} - end - elseif isa(t, Union) - a = widenconst(typeof_tfunc(t.a)) - b = widenconst(typeof_tfunc(t.b)) - return Union{a, b} - elseif isa(t, TypeVar) && !(Any <: t.ub) - return typeof_tfunc(t.ub) - elseif isa(t, UnionAll) - return rewrap_unionall(widenconst(typeof_tfunc(unwrap_unionall(t))), t) - else - return DataType # typeof(anything)::DataType - end -end -add_tfunc(typeof, 1, 1, typeof_tfunc, 0) -add_tfunc(typeassert, 2, 2, - function (@nospecialize(v), @nospecialize(t)) - t, isexact = instanceof_tfunc(t) - t === Any && return v - if isa(v, Const) - if !has_free_typevars(t) && !isa(v.val, t) - return Bottom - end - return v - elseif isa(v, Conditional) - if !(Bool <: t) - return Bottom - end - return v - end - return typeintersect(v, t) - end, 4) -add_tfunc(isa, 2, 2, - function (@nospecialize(v), @nospecialize(t)) - t, isexact = instanceof_tfunc(t) - if !has_free_typevars(t) - if t === Bottom - return Const(false) - elseif v ⊑ t - if isexact - return Const(true) - end - elseif isa(v, Const) || isa(v, Conditional) || (isleaftype(v) && !iskindtype(v)) - return Const(false) - elseif isexact && typeintersect(v, t) === Bottom - if !iskindtype(v) #= subtyping currently intentionally answers this query incorrectly for kinds =# - return Const(false) - end - end - end - # TODO: handle non-leaftype(t) by testing against lower and upper bounds - return Bool - end, 0) -add_tfunc(<:, 2, 2, - function (@nospecialize(a), @nospecialize(b)) - a, isexact_a = instanceof_tfunc(a) - b, isexact_b = instanceof_tfunc(b) - if !has_free_typevars(a) && !has_free_typevars(b) - if a <: b - if isexact_b || a === Bottom - return Const(true) - end - else - if isexact_a || (b !== Bottom && typeintersect(a, b) === Union{}) - return Const(false) - end - end - end - return Bool - end, 0) - -function type_depth(@nospecialize(t)) - if t === Bottom - return 0 - elseif isa(t, Union) - return max(type_depth(t.a), type_depth(t.b)) + 1 - elseif isa(t, DataType) - return (t::DataType).depth - elseif isa(t, UnionAll) - if t.var.ub === Any && t.var.lb === Bottom - return type_depth(t.body) - end - return max(type_depth(t.var.ub) + 1, type_depth(t.var.lb) + 1, type_depth(t.body)) - end - return 0 -end - -function limit_type_depth(@nospecialize(t), d::Int) - r = limit_type_depth(t, d, true, TypeVar[]) - @assert !isa(t, Type) || t <: r - return r -end - -function limit_type_depth(@nospecialize(t), d::Int, cov::Bool, vars::Vector{TypeVar}=TypeVar[]) - if isa(t, Union) - if d < 0 - if cov - return Any - else - var = TypeVar(:_) - push!(vars, var) - return var - end - end - return Union{limit_type_depth(t.a, d - 1, cov, vars), - limit_type_depth(t.b, d - 1, cov, vars)} - elseif isa(t, UnionAll) - v = t.var - if v.ub === Any - if v.lb === Bottom - return UnionAll(t.var, limit_type_depth(t.body, d, cov, vars)) - end - ub = Any - else - ub = limit_type_depth(v.ub, d - 1, cov, vars) - end - if v.lb === Bottom || type_depth(v.lb) > d - # note: lower bounds need to be widened by making them lower - lb = Bottom - else - lb = v.lb - end - v2 = TypeVar(v.name, lb, ub) - return UnionAll(v2, limit_type_depth(t{v2}, d, cov, vars)) - elseif !isa(t,DataType) - return t - end - P = t.parameters - isempty(P) && return t - if d < 0 - if isvarargtype(t) - # never replace Vararg with non-Vararg - # passing depth=0 avoids putting a bare typevar here, for the diagonal rule - return Vararg{limit_type_depth(P[1], 0, cov, vars), P[2]} - end - widert = t.name.wrapper - if !(t <: widert) - # This can happen when a typevar has bounds too wide for its context, e.g. - # `Complex{T} where T` is not a subtype of `Complex`. In that case widen even - # faster to something safe to ensure the result is a supertype of the input. - widert = Any - end - cov && return widert - var = TypeVar(:_, widert) - push!(vars, var) - return var - end - stillcov = cov && (t.name === Tuple.name) - newdepth = d - 1 - if isvarargtype(t) - newdepth = max(newdepth, 0) - end - Q = map(x -> limit_type_depth(x, newdepth, stillcov, vars), P) - R = t.name.wrapper{Q...} - if cov && !stillcov - for var in vars - R = UnionAll(var, R) - end - end - return R -end - -# limit the complexity of type `t` to be simpler than the comparison type `compare` -# no new values may be introduced, so the parameter `source` encodes the set of all values already present -# the outermost tuple type is permitted to have up to `allowed_tuplelen` parameters -function limit_type_size(@nospecialize(t), @nospecialize(compare), @nospecialize(source), allowed_tuplelen::Int) - source = svec(unwrap_unionall(compare), unwrap_unionall(source)) - source[1] === source[2] && (source = svec(source[1])) - type_more_complex(t, compare, source, 1, TUPLE_COMPLEXITY_LIMIT_DEPTH, allowed_tuplelen) || return t - r = _limit_type_size(t, compare, source, 1, allowed_tuplelen) - @assert t <: r - #@assert r === _limit_type_size(r, t, source) # this monotonicity constraint is slightly stronger than actually required, - # since we only actually need to demonstrate that repeated application would reaches a fixed point, - #not that it is already at the fixed point - return r -end - -sym_isless(a::Symbol, b::Symbol) = ccall(:strcmp, Int32, (Ptr{UInt8}, Ptr{UInt8}), a, b) < 0 - -function type_more_complex(@nospecialize(t), @nospecialize(c), sources::SimpleVector, depth::Int, tupledepth::Int, allowed_tuplelen::Int) - # detect cases where the comparison is trivial - if t === c - return false - elseif t === Union{} - return false # Bottom is as simple as they come - elseif isa(t, DataType) && isempty(t.parameters) - return false # fastpath: unparameterized types are always finite - elseif tupledepth > 0 && isa(unwrap_unionall(t), DataType) && isa(c, Type) && c !== Union{} && c <: t - return false # t is already wider than the comparison in the type lattice - elseif tupledepth > 0 && is_derived_type_from_any(unwrap_unionall(t), sources, depth) - return false # t isn't something new - end - # peel off wrappers - if isa(c, UnionAll) - # allow wrapping type with fewer UnionAlls than comparison if in a covariant context - if !isa(t, UnionAll) && tupledepth == 0 - return true - end - t = unwrap_unionall(t) - c = unwrap_unionall(c) - end - # rules for various comparison types - if isa(c, TypeVar) - tupledepth = 1 # allow replacing a TypeVar with a concrete value (since we know the UnionAll must be in covariant position) - if isa(t, TypeVar) - return !(t.lb === Union{} || t.lb === c.lb) || # simplify lb towards Union{} - type_more_complex(t.ub, c.ub, sources, depth + 1, tupledepth, 0) - end - c.lb === Union{} || return true - return type_more_complex(t, c.ub, sources, depth, tupledepth, 0) - elseif isa(c, Union) - if isa(t, Union) - return type_more_complex(t.a, c.a, sources, depth, tupledepth, allowed_tuplelen) || - type_more_complex(t.b, c.b, sources, depth, tupledepth, allowed_tuplelen) - end - return type_more_complex(t, c.a, sources, depth, tupledepth, allowed_tuplelen) && - type_more_complex(t, c.b, sources, depth, tupledepth, allowed_tuplelen) - elseif isa(t, Int) && isa(c, Int) - return t !== 1 # alternatively, could use !(0 <= t < c) - end - # base case for data types - if isa(t, DataType) - tP = t.parameters - if isa(c, DataType) && t.name === c.name - cP = c.parameters - length(cP) < length(tP) && return true - ntail = length(cP) - length(tP) # assume parameters were dropped from the tuple head - # allow creating variation within a nested tuple, but only so deep - if t.name === Tuple.name && tupledepth > 0 - tupledepth -= 1 - elseif !isvarargtype(t) - tupledepth = 0 - end - isgenerator = (t.name.name === :Generator && t.name.module === _topmod(t.name.module)) - for i = 1:length(tP) - tPi = tP[i] - cPi = cP[i + ntail] - if isgenerator - let tPi = unwrap_unionall(tPi), - cPi = unwrap_unionall(cPi) - if isa(tPi, DataType) && isa(cPi, DataType) && - !tPi.abstract && !cPi.abstract && - sym_isless(cPi.name.name, tPi.name.name) - # allow collect on (anonymous) Generators to nest, provided that their functions are appropriately ordered - # TODO: is there a better way? - continue - end - end - end - type_more_complex(tPi, cPi, sources, depth + 1, tupledepth, 0) && return true - end - return false - elseif isvarargtype(c) - return type_more_complex(t, unwrapva(c), sources, depth, tupledepth, 0) - end - if isType(t) # allow taking typeof any source type anywhere as Type{...}, as long as it isn't nesting Type{Type{...}} - tt = unwrap_unionall(t.parameters[1]) - if isa(tt, DataType) && !isType(tt) - is_derived_type_from_any(tt, sources, depth) || return true - return false - end - end - end - return true -end +########### +# structs # +########### -# try to find `type` somewhere in `comparison` type -# at a minimum nesting depth of `mindepth` -function is_derived_type(@nospecialize(t), @nospecialize(c), mindepth::Int) - if mindepth > 0 - mindepth -= 1 - end - if t === c - return mindepth == 0 - end - if isa(c, TypeVar) - # see if it is replacing a TypeVar upper bound with something simpler - return is_derived_type(t, c.ub, mindepth) - elseif isa(c, Union) - # see if it is one of the elements of the union - return is_derived_type(t, c.a, mindepth + 1) || is_derived_type(t, c.b, mindepth + 1) - elseif isa(c, UnionAll) - # see if it is derived from the body - return is_derived_type(t, c.body, mindepth) - elseif isa(c, DataType) - if isa(t, DataType) - # see if it is one of the supertypes of a parameter - super = supertype(c) - while super !== Any - t === super && return true - super = supertype(super) - end - end - # see if it was extracted from a type parameter - cP = c.parameters - for p in cP - is_derived_type(t, p, mindepth) && return true - end - if isleaftype(c) && isbits(c) - # see if it was extracted from a fieldtype - # however, only look through types that can be inlined - # to ensure monotonicity of derivation - # since we know that for immutable types, - # the field types must have been constructed prior to the type, - # it cannot have a reference cycle in the type graph - cF = c.types - for f in cF - is_derived_type(t, f, mindepth) && return true - end - end - end - return false -end +# This struct contains information about a use of certain value (`SSAValue` or `Slot`) +# This might be a toplevel use for `Slot` in which case the `expr` field is `#undef`, +# and it only happens if the slot might be used before it's defined. +struct ValueUse + # The statement array where `expr` (or its parent assignment) appears in + stmts::Vector{Any} + # The position of the `expr` in the `stmts` array + stmtidx::Int -function is_derived_type_from_any(@nospecialize(t), sources::SimpleVector, mindepth::Int) - for s in sources - is_derived_type(t, s, mindepth) && return true - end - return false -end + # The position the value appears in `expr` + exidx::Int + # The expression the value is used in. + # If `expr` is undef, the use is a statement level use. + # This must be one of the following: + # 1. A statement level `Expr(:(=), ...)`. + # 2. The RHS of a statement level `Expr(:(=), ...)`. + # 3. A `&` ccall argument. + expr::Expr -# type vs. comparison or which was derived from source -function _limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVector, depth::Int, allowed_tuplelen::Int) - if t === c - return t # quick egal test - elseif t === Union{} - return t # easy case - elseif isa(t, DataType) && isempty(t.parameters) - return t # fast path: unparameterized are always simple - elseif isa(unwrap_unionall(t), DataType) && isa(c, Type) && c !== Union{} && c <: t - return t # t is already wider than the comparison in the type lattice - elseif is_derived_type_from_any(unwrap_unionall(t), sources, depth) - return t # t isn't something new - end - if isa(t, TypeVar) - if isa(c, TypeVar) - if t.ub === c.ub && t.lb === c.lb - return t - end - end - elseif isa(t, Union) - if isa(c, Union) - a = _limit_type_size(t.a, c.a, sources, depth, allowed_tuplelen) - b = _limit_type_size(t.b, c.b, sources, depth, allowed_tuplelen) - return Union{a, b} - end - elseif isa(t, UnionAll) - if isa(c, UnionAll) - tv = t.var - cv = c.var - if tv.ub === cv.ub - if tv.lb === cv.lb - return UnionAll(tv, _limit_type_size(t.body, c.body, sources, depth + 1, allowed_tuplelen)) - end - ub = tv.ub - else - ub = _limit_type_size(tv.ub, cv.ub, sources, depth + 1, 0) - end - if tv.lb === cv.lb - lb = tv.lb - else - # note: lower bounds need to be widened by making them lower - lb = Bottom - end - v2 = TypeVar(tv.name, lb, ub) - return UnionAll(v2, _limit_type_size(t{v2}, c{v2}, sources, depth + 1, allowed_tuplelen)) - end - tbody = _limit_type_size(t.body, c, sources, depth + 1, allowed_tuplelen) - tbody === t.body && return t - return UnionAll(t.var, tbody) - elseif isa(c, UnionAll) - # peel off non-matching wrapper of comparison - return _limit_type_size(t, c.body, sources, depth, allowed_tuplelen) - elseif isa(t, DataType) - if isa(c, DataType) - tP = t.parameters - cP = c.parameters - if t.name === c.name && !isempty(cP) - if isvarargtype(t) - VaT = _limit_type_size(tP[1], cP[1], sources, depth + 1, 0) - N = tP[2] - if isa(N, TypeVar) || N === cP[2] - return Vararg{VaT, N} - end - return Vararg{VaT} - elseif t.name === Tuple.name - # for covariant datatypes (Tuple), - # apply type-size limit element-wise - ltP = length(tP) - lcP = length(cP) - np = min(ltP, max(lcP, allowed_tuplelen)) - Q = Any[ tP[i] for i in 1:np ] - if ltP > np - # combine tp[np:end] into tP[np] using Vararg - Q[np] = tuple_tail_elem(Bottom, Any[ tP[i] for i in np:ltP ]) - end - for i = 1:np - # now apply limit element-wise to Q - # padding out the comparison as needed to allowed_tuplelen elements - if i <= lcP - cPi = cP[i] - elseif isvarargtype(cP[lcP]) - cPi = cP[lcP] - else - cPi = Any - end - Q[i] = _limit_type_size(Q[i], cPi, sources, depth + 1, 0) - end - return Tuple{Q...} - end - elseif isvarargtype(c) - # Tuple{Vararg{T}} --> Tuple{T} is OK - return _limit_type_size(t, cP[1], sources, depth, 0) - end - end - if isType(t) # allow taking typeof as Type{...}, but ensure it doesn't start nesting - tt = unwrap_unionall(t.parameters[1]) - if isa(tt, DataType) && !isType(tt) - is_derived_type_from_any(tt, sources, depth) && return t - end - end - if isvarargtype(t) - # never replace Vararg with non-Vararg - return Vararg - end - widert = t.name.wrapper - if !(t <: widert) - # This can happen when a typevar has bounds too wide for its context, e.g. - # `Complex{T} where T` is not a subtype of `Complex`. In that case widen even - # faster to something safe to ensure the result is a supertype of the input. - return Any - end - return widert - end - return Any -end - - -const DataType_name_fieldindex = fieldindex(DataType, :name) -const DataType_parameters_fieldindex = fieldindex(DataType, :parameters) -const DataType_types_fieldindex = fieldindex(DataType, :types) -const DataType_super_fieldindex = fieldindex(DataType, :super) -const DataType_mutable_fieldindex = fieldindex(DataType, :mutable) - -const TypeName_name_fieldindex = fieldindex(TypeName, :name) -const TypeName_module_fieldindex = fieldindex(TypeName, :module) -const TypeName_wrapper_fieldindex = fieldindex(TypeName, :wrapper) - -function const_datatype_getfield_tfunc(sv, fld) - if (fld == DataType_name_fieldindex || - fld == DataType_parameters_fieldindex || - fld == DataType_types_fieldindex || - fld == DataType_super_fieldindex || - fld == DataType_mutable_fieldindex) - return abstract_eval_constant(getfield(sv, fld)) - end - return nothing -end - -getfield_tfunc(@nospecialize(s00), @nospecialize(name), @nospecialize(inbounds)) = - getfield_tfunc(s00, name) -function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) - if isa(s00, TypeVar) - s00 = s00.ub - end - s = unwrap_unionall(s00) - if isa(s, Union) - return tmerge(rewrap(getfield_tfunc(s.a, name),s00), - rewrap(getfield_tfunc(s.b, name),s00)) - elseif isa(s, Conditional) - return Bottom # Bool has no fields - elseif isa(s, Const) || isconstType(s) - if !isa(s, Const) - sv = s.parameters[1] - else - sv = s.val - end - if isa(name, Const) - nv = name.val - if isa(sv, UnionAll) - if nv === :var || nv === 1 - return Const(sv.var) - elseif nv === :body || nv === 2 - return Const(sv.body) - end - elseif isa(sv, DataType) - t = const_datatype_getfield_tfunc(sv, isa(nv, Symbol) ? - fieldindex(DataType, nv, false) : nv) - t !== nothing && return t - elseif isa(sv, TypeName) - fld = isa(nv, Symbol) ? fieldindex(TypeName, nv, false) : nv - if (fld == TypeName_name_fieldindex || - fld == TypeName_module_fieldindex || - fld == TypeName_wrapper_fieldindex) - return abstract_eval_constant(getfield(sv, fld)) - end - end - if isa(sv, Module) && isa(nv, Symbol) - return abstract_eval_global(sv, nv) - end - if !(isa(nv,Symbol) || isa(nv,Int)) - return Bottom - end - if (isa(sv, SimpleVector) || isimmutable(sv)) && isdefined(sv, nv) - return abstract_eval_constant(getfield(sv, nv)) - end - end - s = typeof(sv) - end - if isType(s) || !isa(s, DataType) || s.abstract - return Any - end - if s <: Tuple && name ⊑ Symbol - return Bottom - end - if s <: Module - if name ⊑ Int - return Bottom - end - return Any - end - if s.name === _NamedTuple_name && !isleaftype(s) - # TODO: better approximate inference - return Any - end - if isempty(s.types) - return Bottom - end - if isa(name, Conditional) - return Bottom # can't index fields with Bool - end - if !isa(name, Const) - if !(Int <: name || Symbol <: name) - return Bottom - end - if length(s.types) == 1 - return rewrap_unionall(unwrapva(s.types[1]), s00) - end - # union together types of all fields - R = reduce(tmerge, Bottom, map(t -> rewrap_unionall(unwrapva(t), s00), s.types)) - # do the same limiting as the known-symbol case to preserve type-monotonicity - if isempty(s.parameters) - return R - end - return limit_type_depth(R, MAX_TYPE_DEPTH) - end - fld = name.val - if isa(fld,Symbol) - fld = fieldindex(s, fld, false) - end - if !isa(fld,Int) - return Bottom - end - nf = length(s.types) - if s <: Tuple && fld >= nf && isvarargtype(s.types[nf]) - return rewrap_unionall(unwrapva(s.types[nf]), s00) - end - if fld < 1 || fld > nf - return Bottom - end - if isType(s00) && isleaftype(s00.parameters[1]) - sp = s00.parameters[1] - elseif isa(s00, Const) && isa(s00.val, DataType) - sp = s00.val - else - sp = nothing - end - if sp !== nothing - t = const_datatype_getfield_tfunc(sp, fld) - t !== nothing && return t - end - R = s.types[fld] - if isempty(s.parameters) - return R - end - # TODO jb/subtype is this still necessary? - # conservatively limit the type depth here, - # since the UnionAll type bound is otherwise incorrect - # in the current type system - return rewrap_unionall(limit_type_depth(R, MAX_TYPE_DEPTH), s00) -end -add_tfunc(getfield, 2, 3, getfield_tfunc, 1) -add_tfunc(setfield!, 3, 3, (@nospecialize(o), @nospecialize(f), @nospecialize(v)) -> v, 3) -fieldtype_tfunc(@nospecialize(s0), @nospecialize(name), @nospecialize(inbounds)) = - fieldtype_tfunc(s0, name) -function fieldtype_tfunc(@nospecialize(s0), @nospecialize(name)) - if s0 === Any || s0 === Type || DataType ⊑ s0 || UnionAll ⊑ s0 - return Type - end - # fieldtype only accepts DataType and UnionAll, errors on `Module` - if isa(s0,Const) && (!(isa(s0.val,DataType) || isa(s0.val,UnionAll)) || s0.val === Module) - return Bottom - end - if s0 == Type{Module} || s0 == Type{Union{}} || isa(s0, Conditional) - return Bottom - end - - s = instanceof_tfunc(s0)[1] - u = unwrap_unionall(s) - - if isa(u,Union) - return tmerge(rewrap(fieldtype_tfunc(u.a, name),s), - rewrap(fieldtype_tfunc(u.b, name),s)) - end - - if !isa(u,DataType) || u.abstract - return Type - end - if u.name === _NamedTuple_name && !isleaftype(u) - return Type - end - ftypes = u.types - if isempty(ftypes) - return Bottom - end - - if !isa(name, Const) - if !(Int <: name || Symbol <: name) - return Bottom - end - return reduce(tmerge, Bottom, - Any[ fieldtype_tfunc(s0, Const(i)) for i = 1:length(ftypes) ]) - end - - fld = name.val - if isa(fld,Symbol) - fld = fieldindex(u, fld, false) - end - if !isa(fld, Int) - return Bottom - end - nf = length(ftypes) - if u.name === Tuple.name && fld >= nf && isvarargtype(ftypes[nf]) - ft = unwrapva(ftypes[nf]) - elseif fld < 1 || fld > nf - return Bottom - else - ft = ftypes[fld] - end - - exact = (isa(s0, Const) || isType(s0)) && !has_free_typevars(s) - ft = rewrap_unionall(ft,s) - if exact - return Const(ft) - end - return Type{<:ft} -end -add_tfunc(fieldtype, 2, 3, fieldtype_tfunc, 0) - -function valid_tparam(@nospecialize(x)) - if isa(x,Tuple) - for t in x - !valid_tparam(t) && return false - end - return true - end - return isa(x,Int) || isa(x,Symbol) || isa(x,Bool) || (!isa(x,Type) && isbits(x)) -end - -has_free_typevars(@nospecialize(t)) = ccall(:jl_has_free_typevars, Cint, (Any,), t)!=0 - -# TODO: handle e.g. apply_type(T, R::Union{Type{Int32},Type{Float64}}) -function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...) - if isa(headtypetype, Const) - headtype = headtypetype.val - elseif isType(headtypetype) && isleaftype(headtypetype.parameters[1]) - headtype = headtypetype.parameters[1] - else - return Any - end - largs = length(args) - if headtype === Union - largs == 0 && return Const(Bottom) - largs == 1 && return args[1] - for i = 1:largs - ai = args[i] - if !isa(ai, Const) || !isa(ai.val, Type) - if !isType(ai) - return Any - end - end - end - ty = Union{} - allconst = true - for i = 1:largs - ai = args[i] - if isType(ai) - aty = ai.parameters[1] - isleaftype(aty) || (allconst = false) - else - aty = (ai::Const).val - end - ty = Union{ty, aty} - end - return allconst ? Const(ty) : Type{ty} - end - istuple = (headtype == Tuple) - if !istuple && !isa(headtype, UnionAll) - # TODO: return `Bottom` for trying to apply a non-UnionAll - return Any - end - uncertain = false - canconst = true - tparams = Any[] - outervars = Any[] - for i = 1:largs - ai = args[i] - if isType(ai) - aip1 = ai.parameters[1] - canconst &= !has_free_typevars(aip1) - push!(tparams, aip1) - elseif isa(ai, Const) && (isa(ai.val, Type) || isa(ai.val, TypeVar) || valid_tparam(ai.val)) - push!(tparams, ai.val) - elseif isa(ai, PartialTypeVar) - canconst = false - push!(tparams, ai.tv) - else - # TODO: return `Bottom` for trying to apply a non-UnionAll - uncertain = true - # These blocks improve type info but make compilation a bit slower. - # XXX - #unw = unwrap_unionall(ai) - #isT = isType(unw) - #if isT && isa(ai,UnionAll) && contains_is(outervars, ai.var) - # ai = rename_unionall(ai) - # unw = unwrap_unionall(ai) - #end - if istuple - if i == largs - push!(tparams, Vararg) - # XXX - #elseif isT - # push!(tparams, rewrap_unionall(unw.parameters[1], ai)) - else - push!(tparams, Any) - end - # XXX - #elseif isT - # push!(tparams, unw.parameters[1]) - # while isa(ai, UnionAll) - # push!(outervars, ai.var) - # ai = ai.body - # end - else - v = TypeVar(:_) - push!(tparams, v) - push!(outervars, v) - end - end - end - local appl - try - appl = apply_type(headtype, tparams...) - catch ex - # type instantiation might fail if one of the type parameters - # doesn't match, which could happen if a type estimate is too coarse - return Type{<:headtype} - end - !uncertain && canconst && return Const(appl) - if isvarargtype(headtype) - return Type - end - if uncertain && type_too_complex(appl, MAX_TYPE_DEPTH) - return Type{<:headtype} - end - if istuple - return Type{<:appl} - end - ans = Type{appl} - for i = length(outervars):-1:1 - ans = UnionAll(outervars[i], ans) - end - return ans -end -add_tfunc(apply_type, 1, IInf, apply_type_tfunc, 10) - -@pure function type_typeof(@nospecialize(v)) - if isa(v, Type) - return Type{v} - end - return typeof(v) -end - -function invoke_tfunc(@nospecialize(f), @nospecialize(types), @nospecialize(argtype), sv::InferenceState) - if !isleaftype(Type{types}) - return Any - end - argtype = typeintersect(types,limit_tuple_type(argtype, sv.params)) - if argtype === Bottom - return Bottom - end - ft = type_typeof(f) - types = rewrap_unionall(Tuple{ft, unwrap_unionall(types).parameters...}, types) - argtype = Tuple{ft, argtype.parameters...} - entry = ccall(:jl_gf_invoke_lookup, Any, (Any, UInt), types, sv.params.world) - if entry === nothing - return Any - end - meth = entry.func - (ti, env) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), argtype, meth.sig)::SimpleVector - rt, edge = typeinf_edge(meth::Method, ti, env, sv) - edge !== nothing && add_backedge!(edge::MethodInstance, sv) - return rt -end - -function tuple_tfunc(@nospecialize(argtype)) - if isa(argtype, DataType) && argtype.name === Tuple.name - p = Vector{Any}() - for x in argtype.parameters - if isType(x) && !isa(x.parameters[1], TypeVar) - xparam = x.parameters[1] - if isleaftype(xparam) || xparam === Bottom - push!(p, typeof(xparam)) - else - push!(p, Type) - end - else - push!(p, x) - end - end - t = Tuple{p...} - # replace a singleton type with its equivalent Const object - isdefined(t, :instance) && return Const(t.instance) - return t - end - return argtype -end - -function builtin_tfunction(@nospecialize(f), argtypes::Array{Any,1}, - sv::Union{InferenceState,Nothing}, params::InferenceParams = sv.params) - isva = !isempty(argtypes) && isvarargtype(argtypes[end]) - if f === tuple - for a in argtypes - if !isa(a, Const) - return tuple_tfunc(limit_tuple_depth(params, argtypes_to_type(argtypes))) - end - end - return Const(tuple(anymap(a->a.val, argtypes)...)) - elseif f === svec - return SimpleVector - elseif f === arrayset - if length(argtypes) < 4 - isva && return Any - return Bottom - end - return argtypes[2] - elseif f === arrayref - if length(argtypes) < 3 - isva && return Any - return Bottom - end - a = widenconst(argtypes[2]) - if a <: Array - if isa(a, DataType) && (isa(a.parameters[1], Type) || isa(a.parameters[1], TypeVar)) - # TODO: the TypeVar case should not be needed here - a = a.parameters[1] - return isa(a, TypeVar) ? a.ub : a - elseif isa(a, UnionAll) && !has_free_typevars(a) - unw = unwrap_unionall(a) - if isa(unw, DataType) - return rewrap_unionall(unw.parameters[1], a) - end - end - end - return Any - elseif f === Expr - if length(argtypes) < 1 && !isva - return Bottom - end - return Expr - elseif f === invoke - if length(argtypes)>1 && isa(argtypes[1], Const) - af = argtypes[1].val - sig = argtypes[2] - if isa(sig, Const) - sigty = sig.val - elseif isType(sig) - sigty = sig.parameters[1] - else - sigty = nothing - end - if isa(sigty, Type) && sigty <: Tuple && sv !== nothing - return invoke_tfunc(af, sigty, argtypes_to_type(argtypes[3:end]), sv) - end - end - return Any - end - if isva - return Any - end - if isa(f, IntrinsicFunction) - if is_pure_intrinsic_infer(f) && all(a -> isa(a, Const), argtypes) - argvals = anymap(a -> a.val, argtypes) - try - return Const(f(argvals...)) - end - end - iidx = Int(reinterpret(Int32, f::IntrinsicFunction)) + 1 - if iidx < 0 || iidx > length(t_ifunc) - # invalid intrinsic - return Any - end - tf = t_ifunc[iidx] - else - fidx = findfirst(x->x===f, t_ffunc_key) - if fidx === nothing - # unknown/unhandled builtin function - return Any - end - tf = t_ffunc_val[fidx] - end - tf = tf::Tuple{Int, Int, Any} - if !(tf[1] <= length(argtypes) <= tf[2]) - # wrong # of args - return Bottom - end - return tf[3](argtypes...) -end - -limit_tuple_depth(params::InferenceParams, @nospecialize(t)) = limit_tuple_depth_(params,t,0) - -function limit_tuple_depth_(params::InferenceParams, @nospecialize(t), d::Int) - if isa(t,Union) - # also limit within Union types. - # may have to recur into other stuff in the future too. - return Union{limit_tuple_depth_(params, t.a, d+1), - limit_tuple_depth_(params, t.b, d+1)} - elseif isa(t,UnionAll) - ub = limit_tuple_depth_(params, t.var.ub, d) - if ub !== t.var.ub - var = TypeVar(t.var.name, t.var.lb, ub) - body = t{var} - else - var = t.var - body = t.body - end - body = limit_tuple_depth_(params, body, d) - return UnionAll(var, body) - elseif !(isa(t,DataType) && t.name === Tuple.name) - return t - elseif d > params.MAX_TUPLE_DEPTH - return Tuple - end - p = map(x->limit_tuple_depth_(params,x,d+1), t.parameters) - Tuple{p...} -end - -limit_tuple_type = (@nospecialize(t), params::InferenceParams) -> limit_tuple_type_n(t, params.MAX_TUPLETYPE_LEN) - -function limit_tuple_type_n(@nospecialize(t), lim::Int) - if isa(t,UnionAll) - return UnionAll(t.var, limit_tuple_type_n(t.body, lim)) - end - p = t.parameters - n = length(p) - if n > lim - tail = reduce(typejoin, Bottom, Any[p[lim:(n-1)]..., unwrapva(p[n])]) - return Tuple{p[1:(lim-1)]..., Vararg{tail}} - end - return t -end - -# return an upper-bound on type `a` with type `b` removed -# such that `return <: a` && `Union{return, b} == Union{a, b}` -function typesubtract(@nospecialize(a), @nospecialize(b)) - if a <: b - return Bottom - end - if isa(a, Union) - return Union{typesubtract(a.a, b), - typesubtract(a.b, b)} - end - return a # TODO: improve this bound? -end - -#### recursing into expression #### - -# take a Tuple where one or more parameters are Unions -# and return an array such that those Unions are removed -# and `Union{return...} == ty` -function switchtupleunion(@nospecialize(ty)) - tparams = (unwrap_unionall(ty)::DataType).parameters - return _switchtupleunion(Any[tparams...], length(tparams), [], ty) -end - -function _switchtupleunion(t::Vector{Any}, i::Int, tunion::Vector{Any}, @nospecialize(origt)) - if i == 0 - tpl = rewrap_unionall(Tuple{t...}, origt) - push!(tunion, tpl) - else - ti = t[i] - if isa(ti, Union) - for ty in uniontypes(ti::Union) - t[i] = ty - _switchtupleunion(t, i - 1, tunion, origt) - end - t[i] = ti - else - _switchtupleunion(t, i - 1, tunion, origt) - end - end - return tunion -end - -function abstract_call_gf_by_type(@nospecialize(f), argtypes::Vector{Any}, @nospecialize(atype), sv::InferenceState) - atype = limit_tuple_type(atype, sv.params) - atype_params = unwrap_unionall(atype).parameters - ft = unwrap_unionall(atype_params[1]) # TODO: ccall jl_first_argument_datatype here - isa(ft, DataType) || return Any # the function being called is unknown. can't properly handle this backedge right now - ftname = ft.name - isdefined(ftname, :mt) || return Any # not callable. should be Bottom, but can't track this backedge right now - if ftname === _Type_name - tname = ft.parameters[1] - if isa(tname, TypeVar) - tname = tname.ub - end - tname = unwrap_unionall(tname) - if !isa(tname, DataType) - # can't track the backedge to the ctor right now - # for things like Union - return Any - end - end - min_valid = UInt[typemin(UInt)] - max_valid = UInt[typemax(UInt)] - splitunions = 1 < countunionsplit(atype_params) <= sv.params.MAX_UNION_SPLITTING - if splitunions - splitsigs = switchtupleunion(atype) - applicable = Any[] - for sig_n in splitsigs - xapplicable = _methods_by_ftype(sig_n, sv.params.MAX_METHODS, sv.params.world, min_valid, max_valid) - xapplicable === false && return Any - append!(applicable, xapplicable) - end - else - applicable = _methods_by_ftype(atype, sv.params.MAX_METHODS, sv.params.world, min_valid, max_valid) - if applicable === false - # this means too many methods matched - # (assume this will always be true, so we don't compute / update valid age in this case) - return Any - end - end - update_valid_age!(min_valid[1], max_valid[1], sv) - applicable = applicable::Array{Any,1} - napplicable = length(applicable) - rettype = Bottom - edgecycle = false - for i in 1:napplicable - match = applicable[i]::SimpleVector - method = match[3]::Method - sig = match[1] - sigtuple = unwrap_unionall(sig)::DataType - splitunions = false - # TODO: splitunions = 1 < countunionsplit(sigtuple.parameters) * napplicable <= sv.params.MAX_UNION_SPLITTING - # currently this triggers a bug in inference recursion detection - if splitunions - splitsigs = switchtupleunion(sig) - for sig_n in splitsigs - rt, edgecycle1 = abstract_call_method(method, sig_n, svec(), sv) - edgecycle |= edgecycle1::Bool - rettype = tmerge(rettype, rt) - rettype === Any && break - end - rettype === Any && break - else - rt, edgecycle = abstract_call_method(method, sig, match[2]::SimpleVector, sv) - rettype = tmerge(rettype, rt) - rettype === Any && break - end - end - if napplicable == 1 && !edgecycle && isa(rettype, Type) && sv.params.ipo_constant_propagation - # if there's a possibility we could constant-propagate a better result - # (hopefully without doing too much work), try to do that now - # TODO: it feels like this could be better integrated into abstract_call_method / typeinf_edge - const_rettype = abstract_call_method_with_const_args(f, argtypes, applicable[1]::SimpleVector, sv) - if const_rettype ⊑ rettype - # use the better result, if it's a refinement of rettype - rettype = const_rettype - end - end - if !(rettype === Any) # adding a new method couldn't refine (widen) this type - fullmatch = false - for i in napplicable:-1:1 - match = applicable[i]::SimpleVector - method = match[3]::Method - if atype <: method.sig - fullmatch = true - break - end - end - if !fullmatch - # also need an edge to the method table in case something gets - # added that did not intersect with any existing method - add_mt_backedge(ftname.mt, atype, sv) - end - end - #print("=> ", rettype, "\n") - return rettype -end - -function cache_lookup(code::MethodInstance, argtypes::Vector{Any}, cache::Vector{InferenceResult}) - method = code.def::Method - nargs::Int = method.nargs - method.isva && (nargs -= 1) - for cache_code in cache - # try to search cache first - cache_args = cache_code.args - if cache_code.linfo === code && length(cache_args) >= nargs - cache_match = true - # verify that the trailing args (va) aren't Const - for i in (nargs + 1):length(cache_args) - if isa(cache_args[i], Const) - cache_match = false - break - end - end - cache_match || continue - for i in 1:nargs - a = argtypes[i] - ca = cache_args[i] - # verify that all Const argument types match between the call and cache - if (isa(a, Const) || isa(ca, Const)) && !(a === ca) - cache_match = false - break - end - end - cache_match || continue - return cache_code - end - end - return nothing -end - -function abstract_call_method_with_const_args(@nospecialize(f), argtypes::Vector{Any}, match::SimpleVector, sv::InferenceState) - method = match[3]::Method - nargs::Int = method.nargs - method.isva && (nargs -= 1) - length(argtypes) >= nargs || return Any # probably limit_tuple_type made this non-matching method apparently match - haveconst = false - for i in 1:nargs - a = argtypes[i] - if isa(a, Const) && !isdefined(typeof(a.val), :instance) - if !isleaftype(a.val) # alternately: !isa(a.val, DataType) || !isconstType(Type{a.val}) - # have new information from argtypes that wasn't available from the signature - haveconst = true - break - end - end - end - haveconst || return Any - sig = match[1] - sparams = match[2]::SimpleVector - code = code_for_method(method, sig, sparams, sv.params.world) - code === nothing && return Any - code = code::MethodInstance - # decide if it's likely to be worthwhile - cache_inlineable = false - if isdefined(code, :inferred) - cache_inf = code.inferred - if !(cache_inf === nothing) - cache_src_inferred = ccall(:jl_ast_flag_inferred, Bool, (Any,), cache_inf) - cache_src_inlineable = ccall(:jl_ast_flag_inlineable, Bool, (Any,), cache_inf) - cache_inlineable = cache_src_inferred && cache_src_inlineable - end - end - if !cache_inlineable && !sv.params.aggressive_constant_propagation - tm = _topmod(sv) - if !istopfunction(tm, f, :getproperty) && !istopfunction(tm, f, :setproperty!) - # in this case, see if all of the arguments are constants - for i in 1:nargs - a = argtypes[i] - if !isa(a, Const) && !isconstType(a) - return Any - end - end - end - end - inf_result = cache_lookup(code, argtypes, sv.params.cache) - if inf_result === nothing - inf_result = InferenceResult(code) - atypes = get_argtypes(inf_result) - for i in 1:nargs - a = argtypes[i] - if a isa Const - atypes[i] = a # inject Const argtypes into inference - end - end - frame = InferenceState(inf_result, #=optimize=#true, #=cache=#false, sv.params) - frame.limited = true - frame.parent = sv - push!(sv.params.cache, inf_result) - typeinf(frame) - end - result = inf_result.result - isa(result, InferenceState) && return Any # TODO: is this recursive constant inference? - add_backedge!(inf_result.linfo, sv) - return result -end - -const deprecated_sym = Symbol("deprecated.jl") - -function abstract_call_method(method::Method, @nospecialize(sig), sparams::SimpleVector, sv::InferenceState) - # TODO: remove with 0.7 deprecations - if method.file === deprecated_sym && method.sig == (Tuple{Type{T},Any} where T) - return Any, false - end - topmost = nothing - # Limit argument type tuple growth of functions: - # look through the parents list to see if there's a call to the same method - # and from the same method. - # Returns the topmost occurrence of that repeated edge. - cyclei = 0 - infstate = sv - edgecycle = false - while !(infstate === nothing) - infstate = infstate::InferenceState - if method === infstate.linfo.def - if infstate.linfo.specTypes == sig - # avoid widening when detecting self-recursion - # TODO: merge call cycle and return right away - topmost = nothing - edgecycle = true - break - end - if topmost === nothing - # inspect the parent of this edge, - # to see if they are the same Method as sv - # in which case we'll need to ensure it is convergent - # otherwise, we don't - for parent in infstate.callers_in_cycle - # check in the cycle list first - # all items in here are mutual parents of all others - if parent.linfo.def === sv.linfo.def - topmost = infstate - edgecycle = true - break - end - end - let parent = infstate.parent - # then check the parent link - if topmost === nothing && parent !== nothing - parent = parent::InferenceState - if parent.cached && parent.linfo.def === sv.linfo.def - topmost = infstate - edgecycle = true - end - end - end - end - end - # iterate through the cycle before walking to the parent - if cyclei < length(infstate.callers_in_cycle) - cyclei += 1 - infstate = infstate.callers_in_cycle[cyclei] - else - cyclei = 0 - infstate = infstate.parent - end - end - - if !(topmost === nothing) - topmost = topmost::InferenceState - sigtuple = unwrap_unionall(sig)::DataType - msig = unwrap_unionall(method.sig)::DataType - spec_len = length(msig.parameters) + 1 - ls = length(sigtuple.parameters) - if method === sv.linfo.def - # Under direct self-recursion, permit much greater use of reducers. - # here we assume that complexity(specTypes) :>= complexity(sig) - comparison = sv.linfo.specTypes - l_comparison = length(unwrap_unionall(comparison).parameters) - spec_len = max(spec_len, l_comparison) - else - comparison = method.sig - end - # see if the type is actually too big (relative to the caller), and limit it if required - newsig = limit_type_size(sig, comparison, sv.linfo.specTypes, spec_len) - - if newsig !== sig - # continue inference, but note that we've limited parameter complexity - # on this call (to ensure convergence), so that we don't cache this result - infstate = sv - topmost = topmost::InferenceState - while !(infstate.parent === topmost.parent) - infstate.limited = true - for infstate_cycle in infstate.callers_in_cycle - infstate_cycle.limited = true - end - infstate = infstate.parent - end - sig = newsig - sparams = svec() - end - end - - # if sig changed, may need to recompute the sparams environment - if isa(method.sig, UnionAll) && isempty(sparams) - recomputed = ccall(:jl_type_intersection_with_env, Any, (Any, Any), sig, method.sig)::SimpleVector - sig = recomputed[1] - if !isa(unwrap_unionall(sig), DataType) # probably Union{} - return Any, false - end - sparams = recomputed[2]::SimpleVector - end - - rt, edge = typeinf_edge(method, sig, sparams, sv) - if edge === nothing - edgecycle = true - else - add_backedge!(edge::MethodInstance, sv) - end - return rt, edgecycle -end - -# determine whether `ex` abstractly evals to constant `c` -function abstract_evals_to_constant(@nospecialize(ex), @nospecialize(c), vtypes::VarTable, sv::InferenceState) - av = abstract_eval(ex, vtypes, sv) - return isa(av,Const) && av.val === c -end - -# `typ` is the inferred type for expression `arg`. -# if the expression constructs a container (e.g. `svec(x,y,z)`), -# refine its type to an array of element types. -# Union of Tuples of the same length is converted to Tuple of Unions. -# returns an array of types -function precise_container_type(@nospecialize(arg), @nospecialize(typ), vtypes::VarTable, sv::InferenceState) - if isa(typ, Const) - val = typ.val - if isa(val, SimpleVector) || isa(val, Tuple) - return Any[ Const(val[i]) for i in 1:length(val) ] # avoid making a tuple Generator here! - end - end - - while isa(arg, SSAValue) - def = sv.ssavalue_defs[arg.id + 1] - stmt = sv.src.code[def]::Expr - arg = stmt.args[2] - end - - if is_specializable_vararg_slot(arg, sv) - return Any[rewrap_unionall(p, sv.linfo.specTypes) for p in sv.vararg_type_container.parameters] - end - - tti0 = widenconst(typ) - tti = unwrap_unionall(tti0) - if isa(arg, Expr) && arg.head === :call && (abstract_evals_to_constant(arg.args[1], svec, vtypes, sv) || - abstract_evals_to_constant(arg.args[1], tuple, vtypes, sv)) - aa = arg.args - result = Any[ (isa(aa[j],Expr) ? aa[j].typ : abstract_eval(aa[j],vtypes,sv)) for j=2:length(aa) ] - if _any(isvarargtype, result) - return Any[Vararg{Any}] - end - return result - elseif isa(tti, Union) - utis = uniontypes(tti) - if _any(t -> !isa(t,DataType) || !(t <: Tuple) || !isknownlength(t), utis) - return Any[Vararg{Any}] - end - result = Any[rewrap_unionall(p, tti0) for p in utis[1].parameters] - for t in utis[2:end] - if length(t.parameters) != length(result) - return Any[Vararg{Any}] - end - for j in 1:length(t.parameters) - result[j] = tmerge(result[j], rewrap_unionall(t.parameters[j], tti0)) - end - end - return result - elseif isa(tti0,DataType) && tti0 <: Tuple - if isvatuple(tti0) && length(tti0.parameters) == 1 - return Any[Vararg{unwrapva(tti0.parameters[1])}] - else - return Any[ p for p in tti0.parameters ] - end - elseif tti0 <: Array - return Any[Vararg{eltype(tti0)}] - else - return Any[abstract_iteration(typ, vtypes, sv)] - end -end - -# simulate iteration protocol on container type up to fixpoint -function abstract_iteration(@nospecialize(itertype), vtypes::VarTable, sv::InferenceState) - tm = _topmod(sv) - if !isdefined(tm, :start) || !isdefined(tm, :next) || !isconst(tm, :start) || !isconst(tm, :next) - return Vararg{Any} - end - startf = getfield(tm, :start) - nextf = getfield(tm, :next) - statetype = abstract_call(startf, (), Any[Const(startf), itertype], vtypes, sv) - statetype === Bottom && return Bottom - valtype = Bottom - while valtype !== Any - nt = abstract_call(nextf, (), Any[Const(nextf), itertype, statetype], vtypes, sv) - nt = widenconst(nt) - if !isa(nt, DataType) || !(nt <: Tuple) || isvatuple(nt) || length(nt.parameters) != 2 - return Vararg{Any} - end - if nt.parameters[1] <: valtype && nt.parameters[2] <: statetype - break - end - valtype = tmerge(valtype, nt.parameters[1]) - statetype = tmerge(statetype, nt.parameters[2]) - end - return Vararg{valtype} -end - -function tuple_tail_elem(@nospecialize(init), ct) - return Vararg{widenconst(foldl((a, b) -> tmerge(a, unwrapva(b)), init, ct))} -end - -# do apply(af, fargs...), where af is a function value -function abstract_apply(@nospecialize(aft), fargs::Vector{Any}, aargtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState) - if !isa(aft, Const) && !isconstType(aft) - if !(isleaftype(aft) || aft <: Type) || (aft <: Builtin) || (aft <: IntrinsicFunction) - return Any - end - # non-constant function, but type is known - end - res = Union{} - nargs = length(fargs) - assert(nargs == length(aargtypes)) - splitunions = 1 < countunionsplit(aargtypes) <= sv.params.MAX_APPLY_UNION_ENUM - ctypes = Any[Any[aft]] - for i = 1:nargs - ctypes´ = [] - for ti in (splitunions ? uniontypes(aargtypes[i]) : Any[aargtypes[i]]) - cti = precise_container_type(fargs[i], ti, vtypes, sv) - for ct in ctypes - if !isempty(ct) && isvarargtype(ct[end]) - tail = tuple_tail_elem(unwrapva(ct[end]), cti) - push!(ctypes´, push!(ct[1:(end - 1)], tail)) - else - push!(ctypes´, append_any(ct, cti)) - end - end - end - ctypes = ctypes´ - end - for ct in ctypes - if length(ct) > sv.params.MAX_TUPLETYPE_LEN - tail = tuple_tail_elem(Bottom, ct[sv.params.MAX_TUPLETYPE_LEN:end]) - resize!(ct, sv.params.MAX_TUPLETYPE_LEN) - ct[end] = tail - end - if isa(aft, Const) - rt = abstract_call(aft.val, (), ct, vtypes, sv) - elseif isconstType(aft) - rt = abstract_call(aft.parameters[1], (), ct, vtypes, sv) - else - astype = argtypes_to_type(ct) - rt = abstract_call_gf_by_type(nothing, ct, astype, sv) - end - res = tmerge(res, rt) - if res === Any - break - end - end - return res -end - -# TODO: this function is a very buggy and poor model of the return_type function -# since abstract_call_gf_by_type is a very inaccurate model of _method and of typeinf_type, -# while this assumes that it is a precisely accurate and exact model of both -function return_type_tfunc(argtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState) - if length(argtypes) == 3 - tt = argtypes[3] - if isa(tt, Const) || (isType(tt) && !has_free_typevars(tt)) - aft = argtypes[2] - if isa(aft, Const) || (isType(aft) && !has_free_typevars(aft)) || - (isleaftype(aft) && !(aft <: Builtin)) - af_argtype = isa(tt, Const) ? tt.val : tt.parameters[1] - if isa(af_argtype, DataType) && af_argtype <: Tuple - argtypes_vec = Any[aft, af_argtype.parameters...] - if contains_is(argtypes_vec, Union{}) - return Const(Union{}) - end - astype = argtypes_to_type(argtypes_vec) - if isa(aft, Const) - rt = abstract_call(aft.val, (), argtypes_vec, vtypes, sv) - elseif isconstType(aft) - rt = abstract_call(aft.parameters[1], (), argtypes_vec, vtypes, sv) - else - rt = abstract_call_gf_by_type(nothing, argtypes_vec, astype, sv) - end - if isa(rt, Const) - # output was computed to be constant - return Const(typeof(rt.val)) - elseif isleaftype(rt) || rt === Bottom - # output type was known for certain - return Const(rt) - elseif (isa(tt, Const) || isconstType(tt)) && - (isa(aft, Const) || isconstType(aft)) - # input arguments were known for certain - return Const(rt) - else - return Type{<:rt} - end - end - end - end - end - return NF -end - -function pure_eval_call(@nospecialize(f), argtypes::Vector{Any}, @nospecialize(atype), sv::InferenceState) - for i = 2:length(argtypes) - a = argtypes[i] - if !(isa(a,Const) || isconstType(a)) - return false - end - end - - min_valid = UInt[typemin(UInt)] - max_valid = UInt[typemax(UInt)] - meth = _methods_by_ftype(atype, 1, sv.params.world, min_valid, max_valid) - if meth === false || length(meth) != 1 - return false - end - meth = meth[1]::SimpleVector - method = meth[3]::Method - # TODO: check pure on the inferred thunk - if isdefined(method, :generator) || !method.pure - return false - end - - args = Any[ (a=argtypes[i]; isa(a,Const) ? a.val : a.parameters[1]) for i in 2:length(argtypes) ] - try - value = Core._apply_pure(f, args) - # TODO: add some sort of edge(s) - return Const(value, true) - catch - return false - end -end - -argtypes_to_type(argtypes::Array{Any,1}) = Tuple{anymap(widenconst, argtypes)...} - -_typename(a) = Union{} -_typename(a::Vararg) = Any -_typename(a::TypeVar) = Any -_typename(a::DataType) = Const(a.name) -function _typename(a::Union) - ta = _typename(a.a) - tb = _typename(a.b) - ta === tb ? tb : (ta === Any || tb === Any) ? Any : Union{} -end -_typename(union::UnionAll) = _typename(union.body) - -# N.B.: typename maps type equivalence classes to a single value -typename_static(t::Const) = _typename(t.val) -typename_static(@nospecialize(t)) = isType(t) ? _typename(t.parameters[1]) : Any - -function abstract_call(@nospecialize(f), fargs::Union{Tuple{},Vector{Any}}, argtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState) - if f === _apply - length(fargs) > 1 || return Any - return abstract_apply(argtypes[2], fargs[3:end], argtypes[3:end], vtypes, sv) - end - - la = length(argtypes) - for i = 2:(la - 1) - if isvarargtype(argtypes[i]) - return Any - end - end - - tm = _topmod(sv) - if isa(f, Builtin) || isa(f, IntrinsicFunction) - rt = builtin_tfunction(f, argtypes[2:end], sv) - if rt === Bool && isa(fargs, Vector{Any}) - # perform very limited back-propagation of type information for `is` and `isa` - if f === isa - a = fargs[2] - if isa(a, fieldtype(Conditional, :var)) - aty = widenconst(argtypes[2]) - tty_ub, isexact_tty = instanceof_tfunc(argtypes[3]) - if isexact_tty && !isa(tty_ub, TypeVar) - tty_lb = tty_ub # TODO: this would be wrong if !isexact_tty, but instanceof_tfunc doesn't preserve this info - if !has_free_typevars(tty_lb) && !has_free_typevars(tty_ub) - ifty = typeintersect(aty, tty_ub) - elsety = typesubtract(aty, tty_lb) - if ifty != elsety - return Conditional(a, ifty, elsety) - end - end - end - return Bool - end - elseif f === (===) - a = fargs[2] - b = fargs[3] - aty = argtypes[2] - bty = argtypes[3] - # if doing a comparison to a singleton, consider returning a `Conditional` instead - if isa(aty, Const) && isa(b, fieldtype(Conditional, :var)) - if isdefined(typeof(aty.val), :instance) # can only widen a if it is a singleton - return Conditional(b, aty, typesubtract(widenconst(bty), typeof(aty.val))) - end - return Conditional(b, aty, bty) - end - if isa(bty, Const) && isa(a, fieldtype(Conditional, :var)) - if isdefined(typeof(bty.val), :instance) # same for b - return Conditional(a, bty, typesubtract(widenconst(aty), typeof(bty.val))) - end - return Conditional(a, bty, aty) - end - elseif f === Core.Inference.not_int - aty = argtypes[2] - if isa(aty, Conditional) - return Conditional(aty.var, aty.elsetype, aty.vtype) - end - end - end - return isa(rt, TypeVar) ? rt.ub : rt - elseif f === Core.kwfunc - if length(argtypes) == 2 - ft = widenconst(argtypes[2]) - if isa(ft, DataType) && isdefined(ft.name, :mt) && isdefined(ft.name.mt, :kwsorter) - return Const(ft.name.mt.kwsorter) - end - end - return Any - elseif f === TypeVar - lb = Union{} - ub = Any - ub_certain = lb_certain = true - if length(argtypes) >= 2 && isa(argtypes[2], Const) - nv = argtypes[2].val - ubidx = 3 - if length(argtypes) >= 4 - ubidx = 4 - if isa(argtypes[3], Const) - lb = argtypes[3].val - elseif isType(argtypes[3]) - lb = argtypes[3].parameters[1] - lb_certain = false - else - return TypeVar - end - end - if length(argtypes) >= ubidx - if isa(argtypes[ubidx], Const) - ub = argtypes[ubidx].val - elseif isType(argtypes[ubidx]) - ub = argtypes[ubidx].parameters[1] - ub_certain = false - else - return TypeVar - end - end - tv = TypeVar(nv, lb, ub) - return PartialTypeVar(tv, lb_certain, ub_certain) - end - return TypeVar - elseif f === UnionAll - if length(argtypes) == 3 - canconst = true - if isa(argtypes[3], Const) - body = argtypes[3].val - elseif isType(argtypes[3]) - body = argtypes[3].parameters[1] - canconst = false - else - return Any - end - if !isa(body, Type) && !isa(body, TypeVar) - return Any - end - has_free_typevars(body) || return body - if isa(argtypes[2], Const) - tv = argtypes[2].val - elseif isa(argtypes[2], PartialTypeVar) - ptv = argtypes[2] - tv = ptv.tv - canconst = false - else - return Any - end - !isa(tv, TypeVar) && return Any - theunion = UnionAll(tv, body) - ret = canconst ? abstract_eval_constant(theunion) : Type{theunion} - return ret - end - return Any - elseif f === return_type - rt_rt = return_type_tfunc(argtypes, vtypes, sv) - if rt_rt !== NF - return rt_rt - end - elseif length(argtypes) == 2 && istopfunction(tm, f, :!) - # handle Conditional propagation through !Bool - aty = argtypes[2] - if isa(aty, Conditional) - abstract_call_gf_by_type(f, Any[Const(f), Bool], Tuple{typeof(f), Bool}, sv) # make sure we've inferred `!(::Bool)` - return Conditional(aty.var, aty.elsetype, aty.vtype) - end - elseif length(argtypes) == 3 && istopfunction(tm, f, :!==) - # mark !== as exactly a negated call to === - rty = abstract_call((===), fargs, argtypes, vtypes, sv) - if isa(rty, Conditional) - return Conditional(rty.var, rty.elsetype, rty.vtype) # swap if-else - elseif isa(rty, Const) - return Const(rty.val === false) - end - return rty - elseif length(argtypes) == 3 && istopfunction(tm, f, :(>:)) - # mark issupertype as a exact alias for issubtype - # swap T1 and T2 arguments and call <: - if length(fargs) == 3 - fargs = Any[<:, fargs[3], fargs[2]] - else - fargs = () - end - argtypes = Any[typeof(<:), argtypes[3], argtypes[2]] - rty = abstract_call(<:, fargs, argtypes, vtypes, sv) - return rty - elseif length(argtypes) == 2 && isa(argtypes[2], Const) && isa(argtypes[2].val, SimpleVector) && istopfunction(tm, f, :length) - # mark length(::SimpleVector) as @pure - return Const(length(argtypes[2].val)) - elseif length(argtypes) == 3 && isa(argtypes[2], Const) && isa(argtypes[3], Const) && - isa(argtypes[2].val, SimpleVector) && isa(argtypes[3].val, Int) && istopfunction(tm, f, :getindex) - # mark getindex(::SimpleVector, i::Int) as @pure - svecval = argtypes[2].val::SimpleVector - idx = argtypes[3].val::Int - if 1 <= idx <= length(svecval) && isassigned(svecval, idx) - return Const(getindex(svecval, idx)) - end - elseif length(argtypes) == 2 && istopfunction(tm, f, :typename) - return typename_static(argtypes[2]) - end - - atype = argtypes_to_type(argtypes) - t = pure_eval_call(f, argtypes, atype, sv) - t !== false && return t - - if istopfunction(tm, f, :typejoin) || f === return_type - return Type # don't try to infer these function edges directly -- it won't actually come up with anything useful - end - - if sv.params.inlining - # need to model the special inliner for ^ - # to ensure we have added the same edge - if isdefined(Main, :Base) && - ((isdefined(Main.Base, :^) && f === Main.Base.:^) || - (isdefined(Main.Base, :.^) && f === Main.Base.:.^)) && - length(argtypes) == 3 && (argtypes[3] ⊑ Int32 || argtypes[3] ⊑ Int64) - - a1 = argtypes[2] - basenumtype = Union{corenumtype, Main.Base.ComplexF32, Main.Base.ComplexF64, Main.Base.Rational} - if a1 ⊑ basenumtype - ftimes = Main.Base.:* - ta1 = widenconst(a1) - abstract_call_gf_by_type(ftimes, Any[ftimes, a1, a1], Tuple{typeof(ftimes), ta1, ta1}, sv) - end - end - end - return abstract_call_gf_by_type(f, argtypes, atype, sv) -end - -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 - x === Bottom && return Bottom - end - ft = argtypes[1] - if isa(ft, Const) - f = ft.val - else - if isType(ft) && isleaftype(ft.parameters[1]) - f = ft.parameters[1] - elseif isleaftype(ft) && isdefined(ft, :instance) - f = ft.instance - else - for i = 2:(length(argtypes)-1) - if isvarargtype(argtypes[i]) - return Any - end - end - # non-constant function, but type is known - if (isleaftype(ft) || ft <: Type) && !(ft <: Builtin) && !(ft <: IntrinsicFunction) - return abstract_call_gf_by_type(nothing, argtypes, argtypes_to_type(argtypes), sv) - end - return Any - end - end - return abstract_call(f, e.args, argtypes, vtypes, sv) -end - -const _Ref_name = Ref.body.name - -function abstract_eval(@nospecialize(e), vtypes::VarTable, sv::InferenceState) - if isa(e, QuoteNode) - return abstract_eval_constant((e::QuoteNode).value) - elseif isa(e, SSAValue) - return abstract_eval_ssavalue(e::SSAValue, sv.src) - elseif isa(e, Slot) - return vtypes[slot_id(e)].typ - elseif isa(e, Symbol) - return abstract_eval_global(sv.mod, e) - elseif isa(e,GlobalRef) - return abstract_eval_global(e.mod, e.name) - end - - if !isa(e, Expr) - return abstract_eval_constant(e) - end - e = e::Expr - if e.head === :call - t = abstract_eval_call(e, vtypes, sv) - elseif e.head === :new - t = instanceof_tfunc(abstract_eval(e.args[1], vtypes, sv))[1] - for i = 2:length(e.args) - if abstract_eval(e.args[i], vtypes, sv) === Bottom - rt = Bottom - end - end - elseif e.head === :& - abstract_eval(e.args[1], vtypes, sv) - t = Any - elseif e.head === :foreigncall - rt = e.args[2] - if isa(sv.linfo.def, Method) - spsig = sv.linfo.def.sig - if isa(spsig, UnionAll) - if !isempty(sv.linfo.sparam_vals) - env = pointer_from_objref(sv.linfo.sparam_vals) + sizeof(Ptr{Cvoid}) - rt = ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), e.args[2], spsig, env) - else - rt = rewrap_unionall(e.args[2], spsig) - end - end - end - abstract_eval(e.args[1], vtypes, sv) - for i = 3:length(e.args) - if abstract_eval(e.args[i], vtypes, sv) === Bottom - t = Bottom - end - end - if rt === Bottom - t = Bottom - elseif isa(rt, Type) - t = rt - if isa(t, DataType) && (t::DataType).name === _Ref_name - t = t.parameters[1] - if t === Any - t = Bottom # a return type of Box{Any} is invalid - end - end - for v in sv.linfo.sparam_vals - if isa(v,TypeVar) - t = UnionAll(v, t) - end - end - else - t = Any - end - elseif e.head === :static_parameter - n = e.args[1] - t = Any - if 1 <= n <= length(sv.sp) - val = sv.sp[n] - if isa(val, TypeVar) - if Any <: val.ub - # static param bound to typevar - # if the tvar is not known to refer to anything more specific than Any, - # the static param might actually be an integer, symbol, etc. - else - t = UnionAll(val, Type{val}) - end - else - t = abstract_eval_constant(val) - end - end - elseif e.head === :method - t = (length(e.args) == 1) ? Any : Nothing - elseif e.head === :copyast - t = abstract_eval(e.args[1], vtypes, sv) - if t isa Const && t.val isa Expr - # `copyast` makes copies of Exprs - t = Expr - end - elseif e.head === :invoke - error("type inference data-flow error: tried to double infer a function") - elseif e.head === :boundscheck - return Bool - elseif e.head === :isdefined - sym = e.args[1] - t = Bool - if isa(sym, Slot) - vtyp = vtypes[slot_id(sym)] - if vtyp.typ === Bottom - t = Const(false) # never assigned previously - elseif !vtyp.undef - t = Const(true) # definitely assigned previously - end - elseif isa(sym, Symbol) - if isdefined(sv.mod, sym.name) - t = Const(true) - end - elseif isa(sym, GlobalRef) - if isdefined(sym.mod, sym.name) - t = Const(true) - end - elseif isa(sym, Expr) && sym.head === :static_parameter - n = sym.args[1] - if 1 <= n <= length(sv.sp) - val = sv.sp[n] - if !isa(val, TypeVar) - t = Const(true) - end - end - end - else - t = Any - end - if isa(t, TypeVar) - # no need to use a typevar as the type of an expression - t = t.ub - end - if isa(t, DataType) && isdefined(t, :instance) - # replace singleton types with their equivalent Const object - t = Const(t.instance) - end - e.typ = t - return t -end - -const abstract_eval_constant = Const - -function abstract_eval_global(M::Module, s::Symbol) - if isdefined(M,s) && isconst(M,s) - return abstract_eval_constant(getfield(M,s)) - end - return Any -end - -function abstract_eval_ssavalue(s::SSAValue, src::CodeInfo) - typ = src.ssavaluetypes[s.id + 1] - if typ === NF - return Bottom - end - return typ -end - - -#### handling for statement-position expressions #### - -mutable struct StateUpdate - var::Union{Slot,SSAValue} - vtype::VarState - state::VarTable -end - -function abstract_interpret(@nospecialize(e), vtypes::VarTable, sv::InferenceState) - !isa(e, Expr) && return vtypes - # handle assignment - if e.head === :(=) - t = abstract_eval(e.args[2], vtypes, sv) - t === Bottom && return () - lhs = e.args[1] - if isa(lhs, Slot) || isa(lhs, SSAValue) - # don't bother for GlobalRef - return StateUpdate(lhs, VarState(t, false), vtypes) - end - elseif e.head === :call || e.head === :foreigncall - t = abstract_eval(e, vtypes, sv) - t === Bottom && return () - elseif e.head === :gotoifnot - t = abstract_eval(e.args[1], vtypes, sv) - t === Bottom && return () - elseif e.head === :method - fname = e.args[1] - if isa(fname, Slot) - return StateUpdate(fname, VarState(Any, false), vtypes) - end - end - return vtypes -end - -function type_too_complex(@nospecialize(t), d::Int) - if d < 0 - return true - elseif isa(t, Union) - return type_too_complex(t.a, d - 1) || type_too_complex(t.b, d - 1) - elseif isa(t, TypeVar) - return type_too_complex(t.lb, d - 1) || type_too_complex(t.ub, d - 1) - elseif isa(t, UnionAll) - return type_too_complex(t.var, d) || type_too_complex(t.body, d) - elseif isa(t, DataType) - for x in (t.parameters)::SimpleVector - if type_too_complex(x, d - 1) - return true - end - end - end - return false -end - -## lattice operators - -function issubconditional(a::Conditional, b::Conditional) - avar = a.var - bvar = b.var - if (isa(avar, Slot) && isa(bvar, Slot) && slot_id(avar) === slot_id(bvar)) || - (isa(avar, SSAValue) && isa(bvar, SSAValue) && avar === bvar) - if a.vtype ⊑ b.vtype - if a.elsetype ⊑ b.elsetype - return true - end - end - end - return false -end - -function ⊑(@nospecialize(a), @nospecialize(b)) - (a === NF || b === Any) && return true - (a === Any || b === NF) && return false - a === Union{} && return true - b === Union{} && return false - if isa(a, Conditional) - if isa(b, Conditional) - return issubconditional(a, b) - end - a = Bool - elseif isa(b, Conditional) - return a === Bottom - end - if isa(a, Const) - if isa(b, Const) - return a.val === b.val - end - return isa(a.val, widenconst(b)) - elseif isa(b, Const) - return a === Bottom - elseif !(isa(a, Type) || isa(a, TypeVar)) || - !(isa(b, Type) || isa(b, TypeVar)) - return a === b - else - return a <: b - end -end - -widenconst(c::Conditional) = Bool -function widenconst(c::Const) - if isa(c.val, Type) - if isvarargtype(c.val) - return Type - end - return Type{c.val} - else - return typeof(c.val) - end -end -widenconst(c::PartialTypeVar) = TypeVar -widenconst(@nospecialize(t)) = t - -issubstate(a::VarState, b::VarState) = (a.typ ⊑ b.typ && a.undef <= b.undef) - -# Meta expression head, these generally can't be deleted even when they are -# in a dead branch but can be ignored when analyzing uses/liveness. -is_meta_expr_head(head::Symbol) = - (head === :inbounds || head === :boundscheck || head === :meta || head === :simdloop) -is_meta_expr(ex::Expr) = is_meta_expr_head(ex.head) - -function tmerge(@nospecialize(typea), @nospecialize(typeb)) - typea ⊑ typeb && return typeb - typeb ⊑ typea && return typea - if isa(typea, Conditional) && isa(typeb, Conditional) - if typea.var === typeb.var - vtype = tmerge(typea.vtype, typeb.vtype) - elsetype = tmerge(typea.elsetype, typeb.elsetype) - if vtype != elsetype - return Conditional(typea.var, vtype, elsetype) - end - end - return Bool - end - typea, typeb = widenconst(typea), widenconst(typeb) - typea === typeb && return typea - if !(isa(typea,Type) || isa(typea,TypeVar)) || !(isa(typeb,Type) || isa(typeb,TypeVar)) - return Any - end - if (typea <: Tuple) && (typeb <: Tuple) - if isa(typea, DataType) && isa(typeb, DataType) && length(typea.parameters) == length(typeb.parameters) && !isvatuple(typea) && !isvatuple(typeb) - return typejoin(typea, typeb) - end - if isa(typea, Union) || isa(typeb, Union) || (isa(typea,DataType) && length(typea.parameters)>3) || - (isa(typeb,DataType) && length(typeb.parameters)>3) - # widen tuples faster (see #6704), but not too much, to make sure we can infer - # e.g. (t::Union{Tuple{Bool},Tuple{Bool,Int}})[1] - return Tuple - end - end - u = Union{typea, typeb} - if unionlen(u) > MAX_TYPEUNION_LEN || type_too_complex(u, MAX_TYPE_DEPTH) - # don't let type unions get too big - # TODO: something smarter, like a common supertype - return Any - end - return u -end - -function smerge(sa::Union{NotFound,VarState}, sb::Union{NotFound,VarState}) - sa === sb && return sa - sa === NF && return sb - sb === NF && return sa - issubstate(sa, sb) && return sb - issubstate(sb, sa) && return sa - return VarState(tmerge(sa.typ, sb.typ), sa.undef | sb.undef) -end - -@inline tchanged(@nospecialize(n), @nospecialize(o)) = o === NF || (n !== NF && !(n ⊑ o)) -@inline schanged(@nospecialize(n), @nospecialize(o)) = (n !== o) && (o === NF || (n !== NF && !issubstate(n, o))) - -function stupdate!(state::Tuple{}, changes::StateUpdate) - newst = copy(changes.state) - if isa(changes.var, Slot) - newst[slot_id(changes.var::Slot)] = changes.vtype - end - return newst -end - -function stupdate!(state::VarTable, change::StateUpdate) - if !isa(change.var, Slot) - return stupdate!(state, change.state) - end - newstate = false - changeid = slot_id(change.var::Slot) - for i = 1:length(state) - if i == changeid - newtype = change.vtype - else - newtype = change.state[i] - end - oldtype = state[i] - if schanged(newtype, oldtype) - newstate = state - state[i] = smerge(oldtype, newtype) - end - end - return newstate -end - -function stupdate!(state::VarTable, changes::VarTable) - newstate = false - for i = 1:length(state) - newtype = changes[i] - oldtype = state[i] - if schanged(newtype, oldtype) - newstate = state - state[i] = smerge(oldtype, newtype) - end - end - return newstate -end - -stupdate!(state::Tuple{}, changes::VarTable) = copy(changes) - -stupdate!(state::Tuple{}, changes::Tuple{}) = false - -function stupdate1!(state::VarTable, change::StateUpdate) - if !isa(change.var, Slot) - return false - end - i = slot_id(change.var::Slot) - newtype = change.vtype - oldtype = state[i] - if schanged(newtype, oldtype) - state[i] = smerge(oldtype, newtype) - return true - end - return false -end - - -#### helper functions for typeinf initialization and looping #### - -# scan body for the value of the largest referenced label -function label_counter(body::Vector{Any}) - l = 0 - for b in body - label = 0 - if isa(b, GotoNode) - label = b.label::Int - elseif isa(b, LabelNode) - label = b.label - elseif isa(b, Expr) && b.head == :gotoifnot - label = b.args[2]::Int - elseif isa(b, Expr) && b.head == :enter - label = b.args[1]::Int - end - if label > l - l = label - end - end - return l -end -genlabel(sv::OptimizationState) = LabelNode(sv.next_label += 1) - -function get_label_map(body::Vector{Any}) - nlabels = label_counter(body) - labelmap = zeros(Int, nlabels) - for i = 1:length(body) - el = body[i] - if isa(el, LabelNode) - labelmap[el.label] = i - end - end - return labelmap -end - - -function find_ssavalue_uses(body::Vector{Any}, nvals::Int) - uses = BitSet[ BitSet() for i = 1:nvals ] - for line in 1:length(body) - e = body[line] - isa(e, Expr) && find_ssavalue_uses(e, uses, line) - end - return uses -end - -function find_ssavalue_uses(e::Expr, uses::Vector{BitSet}, line::Int) - head = e.head - is_meta_expr_head(head) && return - skiparg = (head === :(=)) - for a in e.args - if skiparg - skiparg = false - elseif isa(a, SSAValue) - push!(uses[a.id + 1], line) - elseif isa(a, Expr) - find_ssavalue_uses(a, uses, line) - end - end -end - -function find_ssavalue_defs(body::Vector{Any}, nvals::Int) - defs = zeros(Int, nvals) - for line in 1:length(body) - e = body[line] - if isa(e, Expr) && e.head === :(=) - lhs = e.args[1] - if isa(lhs, SSAValue) - defs[lhs.id + 1] = line - end - end - end - return defs -end - -function newvar!(sv::OptimizationState, @nospecialize(typ)) - id = length(sv.src.ssavaluetypes) - push!(sv.src.ssavaluetypes, typ) - return SSAValue(id) -end - -inlining_enabled() = (JLOptions().can_inline == 1) -coverage_enabled() = (JLOptions().code_coverage != 0) - -# work towards converging the valid age range for sv -function update_valid_age!(min_valid::UInt, max_valid::UInt, sv::InferenceState) - sv.min_valid = max(sv.min_valid, min_valid) - sv.max_valid = min(sv.max_valid, max_valid) - @assert(!isa(sv.linfo.def, Method) || - !sv.cached || - sv.min_valid <= sv.params.world <= sv.max_valid, - "invalid age range update") - nothing -end -function update_valid_age!(min_valid::UInt, max_valid::UInt, sv::OptimizationState) - sv.min_valid = max(sv.min_valid, min_valid) - sv.max_valid = min(sv.max_valid, max_valid) - @assert(!isa(sv.linfo.def, Method) || - (sv.min_valid == typemax(UInt) && sv.max_valid == typemin(UInt)) || - sv.min_valid <= sv.params.world <= sv.max_valid, - "invalid age range update") - nothing -end -update_valid_age!(edge::InferenceState, sv::InferenceState) = update_valid_age!(edge.min_valid, edge.max_valid, sv) -update_valid_age!(li::MethodInstance, sv::InferenceState) = update_valid_age!(min_world(li), max_world(li), sv) -update_valid_age!(li::MethodInstance, sv::OptimizationState) = update_valid_age!(min_world(li), max_world(li), sv) - -# temporarily accumulate our edges to later add as backedges in the callee -function add_backedge!(li::MethodInstance, caller::InferenceState) - isa(caller.linfo.def, Method) || return # don't add backedges to toplevel exprs - if caller.stmt_edges[caller.currpc] === () - caller.stmt_edges[caller.currpc] = [] - end - push!(caller.stmt_edges[caller.currpc], li) - update_valid_age!(li, caller) - nothing -end - -function add_backedge!(li::MethodInstance, caller::OptimizationState) - isa(caller.linfo.def, Method) || return # don't add backedges to toplevel exprs - push!(caller.backedges, li) - update_valid_age!(li, caller) - nothing -end - -# temporarily accumulate our no method errors to later add as backedges in the callee method table -function add_mt_backedge(mt::MethodTable, @nospecialize(typ), caller::InferenceState) - isa(caller.linfo.def, Method) || return # don't add backedges to toplevel exprs - if caller.stmt_edges[caller.currpc] === () - caller.stmt_edges[caller.currpc] = [] - end - push!(caller.stmt_edges[caller.currpc], mt) - push!(caller.stmt_edges[caller.currpc], typ) - nothing -end - -# add the real backedges now -function finalize_backedges(frame::InferenceState) - toplevel = !isa(frame.linfo.def, Method) - if !toplevel && frame.cached && frame.max_valid == typemax(UInt) - caller = frame.linfo - for edges in frame.stmt_edges - i = 1 - while i <= length(edges) - to = edges[i] - if isa(to, MethodInstance) - ccall(:jl_method_instance_add_backedge, Cvoid, (Any, Any), to, caller) - i += 1 - else - typeassert(to, MethodTable) - typ = edges[i + 1] - ccall(:jl_method_table_add_backedge, Cvoid, (Any, Any, Any), to, typ, caller) - i += 2 - end - end - end - end -end - -function code_for_method(method::Method, @nospecialize(atypes), sparams::SimpleVector, world::UInt, preexisting::Bool=false) - if world < min_world(method) - return nothing - end - if isdefined(method, :generator) && !isleaftype(atypes) - # don't call staged functions on abstract types. - # (see issues #8504, #10230) - # we can't guarantee that their type behavior is monotonic. - # XXX: this test is wrong if Types (such as DataType or Bottom) are present - return nothing - end - if preexisting - if method.specializations !== nothing - # check cached specializations - # for an existing result stored there - return ccall(:jl_specializations_lookup, Any, (Any, Any, UInt), method, atypes, world) - end - return nothing - end - return ccall(:jl_specializations_get_linfo, Ref{MethodInstance}, (Any, Any, Any, UInt), method, atypes, sparams, world) + ValueUse(stmts, stmtidx, expr, exidx) = new(stmts, stmtidx, exidx, expr) + ValueUse(stmts, stmtidx) = new(stmts, stmtidx, 0) end -function add_backedge!(frame::InferenceState, caller::InferenceState, currpc::Int) - update_valid_age!(frame, caller) - backedge = (caller, currpc) - contains_is(frame.backedges, backedge) || push!(frame.backedges, backedge) - return frame +# This struct contains information about a def of certain value (`SSAValue` or `Slot`) +# The `assign` field is usually an assignment but it might be a `NewvarNode` for `Slot`, +# which can happen if the slot might be used before it's defined. +struct ValueDef + assign::Union{Expr,NewvarNode} + # The statement array where `expr` (or its parent assignment) appears in + stmts::Vector{Any} + # The position of the `expr` in the `stmts` array + stmtidx::Int end -# at the end, all items in b's cycle -# will now be added to a's cycle -function union_caller_cycle!(a::InferenceState, b::InferenceState) - callers_in_cycle = b.callers_in_cycle - b.parent = a.parent - b.callers_in_cycle = a.callers_in_cycle - contains_is(a.callers_in_cycle, b) || push!(a.callers_in_cycle, b) - if callers_in_cycle !== a.callers_in_cycle - for caller in callers_in_cycle - if caller !== b - caller.parent = a.parent - caller.callers_in_cycle = a.callers_in_cycle - push!(a.callers_in_cycle, caller) - end - end - end - return +# Uses and defs of a value. +mutable struct ValueInfo + uses::Vector{ValueUse} + defs::Vector{ValueDef} + has_method::Bool + ValueInfo() = new(EMPTY_USES, EMPTY_DEFS, false) end -function merge_call_chain!(parent::InferenceState, ancestor::InferenceState, child::InferenceState) - # add backedge of parent <- child - # then add all backedges of parent <- parent.parent - # and merge all of the callers into ancestor.callers_in_cycle - # and ensure that walking the parent list will get the same result (DAG) from everywhere - while true - add_backedge!(child, parent, parent.currpc) - union_caller_cycle!(ancestor, child) - child = parent - parent = child.parent - child === ancestor && break - end +# The def and use information of all variables in a function. +# `slots` is indexed by slot id and `ssas` is indexed by ssa id + 1. +# This structure is indexable by either a `Slot`, a `SSAValue` or a `id=>is_ssa` pair. +struct ValueInfoMap + slots::Vector{ValueInfo} + ssas::Vector{ValueInfo} + ValueInfoMap() = new(ValueInfo[], ValueInfo[]) end -# Walk through `linfo`'s upstream call chain, starting at `parent`. If a parent -# frame matching `linfo` is encountered, then there is a cycle in the call graph -# (i.e. `linfo` is a descendant callee of itself). Upon encountering this cycle, -# we "resolve" it by merging the call chain, which entails unioning each intermediary -# frame's `callers_in_cycle` field and adding the appropriate backedges. Finally, -# we return `linfo`'s pre-existing frame. If no cycles are found, `nothing` is -# returned instead. -function resolve_call_cycle!(linfo::MethodInstance, parent::InferenceState) - frame = parent - uncached = false - while isa(frame, InferenceState) - uncached |= !frame.cached # ensure we never add an uncached frame to a cycle - if frame.linfo === linfo - uncached && return true - merge_call_chain!(parent, frame, frame) - return frame - end - for caller in frame.callers_in_cycle - if caller.linfo === linfo - uncached && return true - merge_call_chain!(parent, frame, caller) - return caller - end - end - frame = frame.parent - end - return false +struct StructInfo + defs::Vector{Any} + names::Vector{Symbol} + typ::DataType + mutable::Bool + isnew::Bool end -# build (and start inferring) the inference frame for the linfo -function typeinf_frame(linfo::MethodInstance, - optimize::Bool, cached::Bool, params::InferenceParams) - frame = InferenceState(linfo, optimize, cached, params) - frame === nothing && return nothing - cached && (linfo.inInference = true) - typeinf(frame) - return frame +struct InvokeData + mt::MethodTable + entry::TypeMapEntry + types0 + fexpr + texpr end -# compute (and cache) an inferred AST and return the current best estimate of the result type -function typeinf_edge(method::Method, @nospecialize(atypes), sparams::SimpleVector, caller::InferenceState) - code = code_for_method(method, atypes, sparams, caller.params.world) - code === nothing && return Any, nothing - code = code::MethodInstance - if isdefined(code, :inferred) - # return rettype if the code is already inferred - # staged functions make this hard since they have two "inferred" conditions, - # so need to check whether the code itself is also inferred - inf = code.inferred - if !isa(inf, CodeInfo) || (inf::CodeInfo).inferred - if isdefined(code, :inferred_const) - return abstract_eval_constant(code.inferred_const), code - else - return code.rettype, code - end +struct AllocOptContext + infomap::ValueInfoMap + sv::OptimizationState + todo::ObjectIdDict + changes::ObjectIdDict + sym_count::ObjectIdDict + all_fld::ObjectIdDict + setfield_typ::ObjectIdDict + undef_fld::ObjectIdDict + structinfos::Vector{StructInfo} + function AllocOptContext(infomap::ValueInfoMap, sv::OptimizationState) + todo = ObjectIdDict() + for i in 1:length(infomap.ssas) + isassigned(infomap.ssas, i) || continue + todo[i=>true] = nothing + end + for i in 1:length(infomap.slots) + isassigned(infomap.slots, i) || continue + i > sv.nargs || continue + todo[i=>false] = nothing end + return new(infomap, sv, todo, ObjectIdDict(), ObjectIdDict(), + ObjectIdDict(), ObjectIdDict(), ObjectIdDict(), StructInfo[]) end - if !caller.cached && caller.parent === nothing - # this caller exists to return to the user - # (if we asked resolve_call_cyle, it might instead detect that there is a cycle that it can't merge) - frame = false - else - frame = resolve_call_cycle!(code, caller) - end - if frame === false - # completely new - code.inInference = true - frame = InferenceState(code, #=optimize=#true, #=cached=#true, caller.params) # always optimize and cache edge targets - if frame === nothing - # can't get the source for this, so we know nothing - code.inInference = false - return Any, nothing - end - if caller.cached # don't involve uncached functions in cycle resolution - frame.parent = caller - end - typeinf(frame) - return frame.bestguess, frame.inferred ? frame.linfo : nothing - elseif frame === true - # unresolvable cycle - return Any, nothing - end - frame = frame::InferenceState - return frame.bestguess, nothing end -#### entry points for inferring a MethodInstance given a type signature #### +############# +# constants # +############# -# compute an inferred AST and return type -function typeinf_code(method::Method, @nospecialize(atypes), sparams::SimpleVector, - optimize::Bool, cached::Bool, params::InferenceParams) - code = code_for_method(method, atypes, sparams, params.world) - code === nothing && return (nothing, nothing, Any) - return typeinf_code(code::MethodInstance, optimize, cached, params) -end -function typeinf_code(linfo::MethodInstance, optimize::Bool, cached::Bool, - params::InferenceParams) - for i = 1:2 # test-and-lock-and-test - i == 2 && ccall(:jl_typeinf_begin, Cvoid, ()) - if cached && isdefined(linfo, :inferred) - # see if this code already exists in the cache - # staged functions make this hard since they have two "inferred" conditions, - # so need to check whether the code itself is also inferred - if min_world(linfo) <= params.world <= max_world(linfo) - inf = linfo.inferred - if linfo.jlcall_api == 2 - method = linfo.def::Method - tree = ccall(:jl_new_code_info_uninit, Ref{CodeInfo}, ()) - tree.code = Any[ Expr(:return, quoted(linfo.inferred_const)) ] - tree.signature_for_inference_heuristics = nothing - tree.slotnames = Any[ compiler_temp_sym for i = 1:method.nargs ] - tree.slotflags = UInt8[ 0 for i = 1:method.nargs ] - tree.slottypes = nothing - tree.ssavaluetypes = 0 - tree.inferred = true - tree.pure = true - tree.inlineable = true - i == 2 && ccall(:jl_typeinf_end, Cvoid, ()) - return svec(linfo, tree, linfo.rettype) - elseif isa(inf, CodeInfo) - if inf.inferred - i == 2 && ccall(:jl_typeinf_end, Cvoid, ()) - return svec(linfo, inf, linfo.rettype) - end - end - end - end - end - frame = typeinf_frame(linfo, optimize, cached, params) - ccall(:jl_typeinf_end, Cvoid, ()) - frame === nothing && return svec(nothing, nothing, Any) - frame = frame::InferenceState - frame.inferred || return svec(nothing, nothing, Any) - frame.cached || return svec(nothing, frame.src, widenconst(frame.bestguess)) - return svec(frame.linfo, frame.src, widenconst(frame.bestguess)) -end +# The slot has uses that are not statically dominated by any assignment +# This is implied by `SLOT_USEDUNDEF`. +# If this is not set, all the uses are (statically) dominated by the defs. +# In particular, if a slot has `AssignedOnce && !StaticUndef`, it is an SSA. +const SLOT_STATICUNDEF = 1 -# compute (and cache) an inferred AST and return the inferred return type -function typeinf_type(method::Method, @nospecialize(atypes), sparams::SimpleVector, - cached::Bool, params::InferenceParams) - if contains_is(unwrap_unionall(atypes).parameters, Union{}) - return Union{} - end - code = code_for_method(method, atypes, sparams, params.world) - code === nothing && return nothing - code = code::MethodInstance - for i = 1:2 # test-and-lock-and-test - i == 2 && ccall(:jl_typeinf_begin, Cvoid, ()) - if cached && isdefined(code, :inferred) - # see if this rettype already exists in the cache - # staged functions make this hard since they have two "inferred" conditions, - # so need to check whether the code itself is also inferred - inf = code.inferred - if !isa(inf, CodeInfo) || (inf::CodeInfo).inferred - i == 2 && ccall(:jl_typeinf_end, Cvoid, ()) - return code.rettype - end - end - end - frame = typeinf_frame(code, cached, cached, params) - ccall(:jl_typeinf_end, Cvoid, ()) - frame === nothing && return nothing - frame = frame::InferenceState - frame.inferred || return nothing - return widenconst(frame.bestguess) -end +const SLOT_ASSIGNEDONCE = 16 # slot is assigned to only once -function typeinf_ext(linfo::MethodInstance, world::UInt) - if isa(linfo.def, Method) - # method lambda - infer this specialization via the method cache - return typeinf_code(linfo, true, true, InferenceParams(world)) - else - # toplevel lambda - infer directly - ccall(:jl_typeinf_begin, Cvoid, ()) - result = InferenceResult(linfo) - frame = InferenceState(result, linfo.inferred::CodeInfo, - true, true, InferenceParams(world)) - typeinf(frame) - ccall(:jl_typeinf_end, Cvoid, ()) - @assert frame.inferred # TODO: deal with this better - @assert frame.linfo === linfo - linfo.rettype = widenconst(frame.bestguess) - return svec(linfo, frame.src, linfo.rettype) - end -end +const SLOT_USEDUNDEF = 32 # slot has uses that might raise UndefVarError -#### do the work of inference #### - -function typeinf_work(frame::InferenceState) - @assert !frame.inferred - frame.dont_work_on_me = true # mark that this function is currently on the stack - W = frame.ip - s = frame.stmt_types - n = frame.nstmts - while frame.pc´´ <= n - # make progress on the active ip set - local pc::Int = frame.pc´´ # current program-counter - while true # inner loop optimizes the common case where it can run straight from pc to pc + 1 - #print(pc,": ",s[pc],"\n") - local pc´::Int = pc + 1 # next program-counter (after executing instruction) - if pc == frame.pc´´ - # need to update pc´´ to point at the new lowest instruction in W - min_pc = next(W, pc)[2] - if done(W, min_pc) - frame.pc´´ = max(min_pc, n + 1) - else - frame.pc´´ = min_pc - end - end - delete!(W, pc) - frame.currpc = pc - frame.cur_hand = frame.handler_at[pc] - frame.stmt_edges[pc] === () || empty!(frame.stmt_edges[pc]) - stmt = frame.src.code[pc] - changes = abstract_interpret(stmt, s[pc]::VarTable, frame) - if changes === () - break # this line threw an error and so there is no need to continue - # changes = s[pc] - end - if frame.cur_hand !== () && isa(changes, StateUpdate) - # propagate new type info to exception handler - # the handling for Expr(:enter) propagates all changes from before the try/catch - # so this only needs to propagate any changes - l = frame.cur_hand[1] - if stupdate1!(s[l]::VarTable, changes::StateUpdate) !== false - if l < frame.pc´´ - frame.pc´´ = l - end - push!(W, l) - end - end - if isa(changes, StateUpdate) - changes_var = changes.var - if isa(changes_var, SSAValue) - # directly forward changes to an SSAValue to the applicable line - record_ssa_assign(changes_var.id + 1, changes.vtype.typ, frame) - end - elseif isa(stmt, NewvarNode) - sn = slot_id(stmt.slot) - changes = changes::VarTable - changes[sn] = VarState(Bottom, true) - elseif isa(stmt, GotoNode) - pc´ = (stmt::GotoNode).label - elseif isa(stmt, Expr) - stmt = stmt::Expr - hd = stmt.head - if hd === :gotoifnot - condt = abstract_eval(stmt.args[1], s[pc], frame) - condval = isa(condt, Const) ? condt.val : nothing - l = stmt.args[2]::Int - changes = changes::VarTable - # constant conditions - if condval === true - elseif condval === false - pc´ = l - else - # general case - frame.handler_at[l] = frame.cur_hand - if isa(condt, Conditional) - changes_else = StateUpdate(condt.var, VarState(condt.elsetype, false), changes) - changes = StateUpdate(condt.var, VarState(condt.vtype, false), changes) - else - changes_else = changes - end - newstate_else = stupdate!(s[l], changes_else) - if newstate_else !== false - # add else branch to active IP list - if l < frame.pc´´ - frame.pc´´ = l - end - push!(W, l) - s[l] = newstate_else - end - end - elseif hd === :return - pc´ = n + 1 - rt = abstract_eval(stmt.args[1], s[pc], frame) - if !isa(rt, Const) && !isa(rt, Type) - # only propagate information we know we can store - # and is valid inter-procedurally - rt = widenconst(rt) - end - if tchanged(rt, frame.bestguess) - # new (wider) return type for frame - frame.bestguess = tmerge(frame.bestguess, rt) - for (caller, caller_pc) in frame.backedges - # notify backedges of updated type information - if caller.stmt_types[caller_pc] !== () - if caller_pc < caller.pc´´ - caller.pc´´ = caller_pc - end - push!(caller.ip, caller_pc) - end - end - end - elseif hd === :enter - l = stmt.args[1]::Int - frame.cur_hand = (l, frame.cur_hand) - # propagate type info to exception handler - l = frame.cur_hand[1] - old = s[l] - new = s[pc]::Array{Any,1} - newstate_catch = stupdate!(old, new) - if newstate_catch !== false - if l < frame.pc´´ - frame.pc´´ = l - end - push!(W, l) - s[l] = newstate_catch - end - typeassert(s[l], VarTable) - frame.handler_at[l] = frame.cur_hand - elseif hd === :leave - for i = 1:((stmt.args[1])::Int) - frame.cur_hand = frame.cur_hand[2] - end - end - end - pc´ > n && break # can't proceed with the fast-path fall-through - frame.handler_at[pc´] = frame.cur_hand - newstate = stupdate!(s[pc´], changes) - if isa(stmt, GotoNode) && frame.pc´´ < pc´ - # if we are processing a goto node anyways, - # (such as a terminator for a loop, if-else, or try block), - # consider whether we should jump to an older backedge first, - # to try to traverse the statements in approximate dominator order - if newstate !== false - s[pc´] = newstate - end - push!(W, pc´) - pc = frame.pc´´ - elseif newstate !== false - s[pc´] = newstate - pc = pc´ - elseif pc´ in W - pc = pc´ - else - break - end - end - end - frame.dont_work_on_me = false -end +# const SLOT_CALLED = 64 -function typeinf(frame::InferenceState) - typeinf_work(frame) - - # If the current frame is part of a cycle, solve the cycle before finishing - no_active_ips_in_callers = false - while !no_active_ips_in_callers - no_active_ips_in_callers = true - for caller in frame.callers_in_cycle - caller.dont_work_on_me && return - if caller.pc´´ <= caller.nstmts # equivalent to `isempty(caller.ip)` - # Note that `typeinf_work(caller)` can potentially modify the other frames - # `frame.callers_in_cycle`, which is why making incremental progress requires the - # outer while loop. - typeinf_work(caller) - no_active_ips_in_callers = false - end - if caller.min_valid < frame.min_valid - caller.min_valid = frame.min_valid - end - if caller.max_valid > frame.max_valid - caller.max_valid = frame.max_valid - end - end - end +# known affect-free calls (also effect-free) +const _PURE_BUILTINS = Any[tuple, svec, fieldtype, apply_type, ===, isa, typeof, UnionAll, nfields] - # with no active ip's, type inference on frame is done +# known effect-free calls (might not be affect-free) +const _PURE_BUILTINS_VOLATILE = Any[getfield, arrayref, isdefined, Core.sizeof] - if isempty(frame.callers_in_cycle) - @assert !(frame.dont_work_on_me) - frame.dont_work_on_me = true - optimize(frame) - finish(frame) - finalize_backedges(frame) - else # frame is in frame.callers_in_cycle - for caller in frame.callers_in_cycle - @assert !(caller.dont_work_on_me) - caller.dont_work_on_me = true - end - # complete the computation of the src optimizations - for caller in frame.callers_in_cycle - optimize(caller) - if frame.min_valid < caller.min_valid - frame.min_valid = caller.min_valid - end - if frame.max_valid > caller.max_valid - frame.max_valid = caller.max_valid - end - end - # update and store in the global cache - for caller in frame.callers_in_cycle - caller.min_valid = frame.min_valid - end - for caller in frame.callers_in_cycle - finish(caller) - end - for caller in frame.callers_in_cycle - finalize_backedges(caller) - end - end +const TOP_GETFIELD = GlobalRef(Core, :getfield) - nothing -end +const TOP_TUPLE = GlobalRef(Core, :tuple) +const META_POP_LOC = Expr(:meta, :pop_loc) -function record_ssa_assign(ssa_id::Int, @nospecialize(new), frame::InferenceState) - old = frame.src.ssavaluetypes[ssa_id] - if old === NF || !(new ⊑ old) - frame.src.ssavaluetypes[ssa_id] = tmerge(old, new) - W = frame.ip - s = frame.stmt_types - for r in frame.ssavalue_uses[ssa_id] - if s[r] !== () # s[r] === () => unreached statement - if r < frame.pc´´ - frame.pc´´ = r - end - push!(W, r) - end - end - end - nothing -end +const ENABLE_VERIFY_VALUEINFO = (tmp = Array{Bool,0}(uninitialized); tmp[] = false; tmp) +# allocation optimization, must not be mutated. +const EMPTY_USES = ValueUse[] +const EMPTY_DEFS = ValueDef[] -#### finalize and record the result of running type inference #### +######### +# logic # +######### -function isinlineable(m::Method, src::CodeInfo, mod::Module, params::InferenceParams, bonus::Int=0) +function isinlineable(m::Method, src::CodeInfo, mod::Module, params::Params, bonus::Int=0) # compute the cost (size) of inlining this code inlineable = false cost_threshold = params.inline_cost_threshold @@ -3776,8 +261,7 @@ function isinlineable(m::Method, src::CodeInfo, mod::Module, params::InferencePa return inlineable end -# inference completed on `me` -# now converge the optimization work +# converge the optimization work function optimize(me::InferenceState) # annotate fulltree with type information type_annotate!(me) @@ -3843,7 +327,7 @@ function optimize(me::InferenceState) end if proven_pure for fl in me.src.slotflags - if (fl & Slot_UsedUndef) != 0 + if (fl & SLOT_USEDUNDEF) != 0 proven_pure = false break end @@ -3895,7 +379,7 @@ function optimize(me::InferenceState) end me.src.inferred = true if me.optimize && !(me.limited && me.parent !== nothing) - _validate(me.linfo, me.src, "optimized") + validate_code_in_debug_mode(me.linfo, me.src, "optimized") end nothing end @@ -4059,7 +543,7 @@ function type_annotate!(sv::InferenceState) # remove all unused ssa values gt = sv.src.ssavaluetypes for j = 1:length(gt) - if gt[j] === NF + if gt[j] === NOT_FOUND gt[j] = Union{} end end @@ -4113,7 +597,7 @@ function type_annotate!(sv::InferenceState) # finish marking used-undef variables for j = 1:nslots if undefs[j] - src.slotflags[j] |= Slot_UsedUndef | Slot_StaticUndef + src.slotflags[j] |= SLOT_USEDUNDEF | SLOT_STATICUNDEF end end @@ -4266,54 +750,7 @@ function substitute!( return e end -# count occurrences up to n+1 -function occurs_more(@nospecialize(e), pred, n) - if isa(e,Expr) - e = e::Expr - head = e.head - is_meta_expr_head(head) && return 0 - c = 0 - for a = e.args - c += occurs_more(a, pred, n) - if c>n - return c - end - end - return c - end - if pred(e) - return 1 - end - return 0 -end - -function exprtype(@nospecialize(x), src::CodeInfo, mod::Module) - if isa(x, Expr) - return (x::Expr).typ - elseif isa(x, SlotNumber) - return src.slottypes[(x::SlotNumber).id] - elseif isa(x, TypedSlot) - return (x::TypedSlot).typ - elseif isa(x, SSAValue) - return abstract_eval_ssavalue(x::SSAValue, src) - elseif isa(x, Symbol) - return abstract_eval_global(mod, x::Symbol) - elseif isa(x, QuoteNode) - return abstract_eval_constant((x::QuoteNode).value) - elseif isa(x, GlobalRef) - return abstract_eval_global(x.mod, (x::GlobalRef).name) - else - return abstract_eval_constant(x) - end -end - -# known affect-free calls (also effect-free) -const _pure_builtins = Any[tuple, svec, fieldtype, apply_type, ===, isa, typeof, UnionAll, nfields] - -# known effect-free calls (might not be affect-free) -const _pure_builtins_volatile = Any[getfield, arrayref, isdefined, Core.sizeof] - -# whether `f` is pure for Inference +# whether `f` is pure for inference function is_pure_intrinsic_infer(f::IntrinsicFunction) return !(f === Intrinsics.pointerref || # this one is volatile f === Intrinsics.pointerset || # this one is never effect-free @@ -4323,7 +760,7 @@ function is_pure_intrinsic_infer(f::IntrinsicFunction) f === Intrinsics.cglobal) # cglobal lookup answer changes at runtime end -# whether `f` is pure for Optimizations +# whether `f` is pure for optimizations function is_pure_intrinsic_optim(f::IntrinsicFunction) return !(f === Intrinsics.pointerref || # this one is volatile f === Intrinsics.pointerset || # this one is never effect-free @@ -4340,8 +777,8 @@ function is_pure_builtin(@nospecialize(f)) if isa(f, IntrinsicFunction) return is_pure_intrinsic_optim(f) elseif isa(f, Builtin) - return (contains_is(_pure_builtins, f) || - contains_is(_pure_builtins_volatile, f)) + return (contains_is(_PURE_BUILTINS, f) || + contains_is(_PURE_BUILTINS_VOLATILE, f)) else return f === return_type end @@ -4369,7 +806,7 @@ function effect_free(@nospecialize(e), src::CodeInfo, mod::Module, allow_volatil elseif isa(e, Symbol) return allow_volatile elseif isa(e, Slot) - return src.slotflags[slot_id(e)] & Slot_UsedUndef == 0 + return src.slotflags[slot_id(e)] & SLOT_USEDUNDEF == 0 elseif isa(e, Expr) e = e::Expr head = e.head @@ -4410,7 +847,7 @@ function effect_free(@nospecialize(e), src::CodeInfo, mod::Module, allow_volatil # fall-through elseif is_known_call(e, _apply, src, mod) && length(ea) > 1 ft = exprtype(ea[2], src, mod) - if !isa(ft, Const) || (!contains_is(_pure_builtins, ft.val) && + if !isa(ft, Const) || (!contains_is(_PURE_BUILTINS, ft.val) && ft.val !== Core.sizeof) return false end @@ -4456,17 +893,6 @@ function effect_free(@nospecialize(e), src::CodeInfo, mod::Module, allow_volatil return true end - -#### post-inference optimizations #### - -struct InvokeData - mt::MethodTable - entry::TypeMapEntry - types0 - fexpr - texpr -end - function inline_as_constant(@nospecialize(val), argexprs::Vector{Any}, sv::OptimizationState, @nospecialize(invoke_data)) if invoke_data === nothing invoke_fexpr = nothing @@ -4515,7 +941,7 @@ function invoke_NF(argexprs, @nospecialize(etype), atypes::Vector{Any}, sv::Opti @nospecialize(atype_unlimited), @nospecialize(invoke_data)) # converts a :call to :invoke nu = countunionsplit(atypes) - nu > sv.params.MAX_UNION_SPLITTING && return NF + nu > sv.params.MAX_UNION_SPLITTING && return NOT_FOUND if invoke_data === nothing invoke_fexpr = nothing invoke_texpr = nothing @@ -4611,7 +1037,7 @@ function invoke_NF(argexprs, @nospecialize(etype), atypes::Vector{Any}, sv::Opti end else local cache_linfo = get_spec_lambda(atype_unlimited, sv, invoke_data) - cache_linfo === nothing && return NF + cache_linfo === nothing && return NOT_FOUND add_backedge!(cache_linfo, sv) argexprs = copy(argexprs) pushfirst!(argexprs, cache_linfo) @@ -4632,7 +1058,7 @@ function invoke_NF(argexprs, @nospecialize(etype), atypes::Vector{Any}, sv::Opti argexprs[2] = newvar return ex, stmts end - return NF + return NOT_FOUND end # inline functions whose bodies are "inline_worthy" @@ -4667,7 +1093,7 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector istopfunction(topmod, f, :promote_type) || (f === Core.kwfunc && length(argexprs) == 2) || (isbits(val) && Core.sizeof(val) <= MAX_INLINE_CONST_SIZE && - (contains_is(_pure_builtins, f) || + (contains_is(_PURE_BUILTINS, f) || (f === getfield && effect_free(e, sv.src, sv.mod, false)) || (isa(f, IntrinsicFunction) && is_pure_intrinsic_optim(f))))) return inline_as_constant(val, argexprs, sv, nothing) @@ -4681,17 +1107,17 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector ft = widenconst(atypes[2]) invoke_tt = widenconst(atypes[3]) if !isleaftype(ft) || !isleaftype(invoke_tt) || !isType(invoke_tt) - return NF + return NOT_FOUND end if !(isa(invoke_tt.parameters[1], Type) && invoke_tt.parameters[1] <: Tuple) - return NF + return NOT_FOUND end invoke_tt = invoke_tt.parameters[1] invoke_types = rewrap_unionall(Tuple{ft, unwrap_unionall(invoke_tt).parameters...}, invoke_tt) invoke_entry = ccall(:jl_gf_invoke_lookup, Any, (Any, UInt), invoke_types, sv.params.world) - invoke_entry === nothing && return NF + invoke_entry === nothing && return NOT_FOUND invoke_fexpr = argexprs[1] invoke_texpr = argexprs[3] if effect_free(invoke_fexpr, sv.src, sv.mod, false) @@ -4711,14 +1137,14 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector f = isdefined(ft, :instance) ? ft.instance : nothing elseif isa(f, IntrinsicFunction) || ft ⊑ IntrinsicFunction || isa(f, Builtin) || ft ⊑ Builtin - return NF + return NOT_FOUND end atype_unlimited = argtypes_to_type(atypes) if !(invoke_data === nothing) invoke_data = invoke_data::InvokeData # TODO emit a type check and proceed for this case - atype_unlimited <: invoke_data.types0 || return NF + atype_unlimited <: invoke_data.types0 || return NOT_FOUND end if !sv.params.inlining return invoke_NF(argexprs, e.typ, atypes, sv, atype_unlimited, @@ -4818,13 +1244,13 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector # inference step shortened our call args list, even # though we have too many arguments to actually # call this function - return NF + return NOT_FOUND end @assert na == length(argexprs) for i = 1:length(methsp) - isa(methsp[i], TypeVar) && return NF + isa(methsp[i], TypeVar) && return NOT_FOUND end # see if the method has been previously inferred (and cached) @@ -5113,7 +1539,7 @@ plus_saturate(x, y) = max(x, y, x+y) # known return type isknowntype(T) = (T == Union{}) || isleaftype(T) -function statement_cost(ex::Expr, line::Int, src::CodeInfo, mod::Module, params::InferenceParams) +function statement_cost(ex::Expr, line::Int, src::CodeInfo, mod::Module, params::Params) head = ex.head if is_meta_expr(ex) || head == :copyast # not sure if copyast is right return 0 @@ -5136,11 +1562,11 @@ function statement_cost(ex::Expr, line::Int, src::CodeInfo, mod::Module, params: f = (extyp::Const).val if isa(f, IntrinsicFunction) iidx = Int(reinterpret(Int32, f::IntrinsicFunction)) + 1 - if !isassigned(t_ifunc_cost, iidx) + if !isassigned(T_IFUNC_COST, iidx) # unknown/unhandled intrinsic return plus_saturate(argcost, params.inline_nonleaf_penalty) end - return plus_saturate(argcost, t_ifunc_cost[iidx]) + return plus_saturate(argcost, T_IFUNC_COST[iidx]) end if isa(f, Builtin) # The efficiency of operations like a[i] and s.b @@ -5155,13 +1581,13 @@ function statement_cost(ex::Expr, line::Int, src::CodeInfo, mod::Module, params: elseif f == Main.Core.arrayref return plus_saturate(argcost, isknowntype(ex.typ) ? 4 : params.inline_nonleaf_penalty) end - fidx = findfirst(x->x===f, t_ffunc_key) + fidx = findfirst(x->x===f, T_FFUNC_KEY) if fidx === nothing # unknown/unhandled builtin or anonymous function # Use the generic cost of a direct function call return plus_saturate(argcost, 20) end - return plus_saturate(argcost, t_ffunc_cost[fidx]) + return plus_saturate(argcost, T_FFUNC_COST[fidx]) end end return plus_saturate(argcost, params.inline_nonleaf_penalty) @@ -5191,7 +1617,7 @@ function statement_cost(ex::Expr, line::Int, src::CodeInfo, mod::Module, params: end function inline_worthy(body::Array{Any,1}, src::CodeInfo, mod::Module, - params::InferenceParams, + params::Params, cost_threshold::Integer=params.inline_cost_threshold) bodycost = 0 for line = 1:length(body) @@ -5212,13 +1638,13 @@ function inline_worthy(body::Array{Any,1}, src::CodeInfo, mod::Module, return bodycost <= cost_threshold end -function inline_worthy(body::Expr, src::CodeInfo, mod::Module, params::InferenceParams, +function inline_worthy(body::Expr, src::CodeInfo, mod::Module, params::Params, cost_threshold::Integer=params.inline_cost_threshold) bodycost = statement_cost(body, typemax(Int), src, mod, params) return bodycost <= cost_threshold end -function inline_worthy(@nospecialize(body), src::CodeInfo, mod::Module, params::InferenceParams, +function inline_worthy(@nospecialize(body), src::CodeInfo, mod::Module, params::Params, cost_threshold::Integer=params.inline_cost_threshold) newbody = exprtype(body, src, mod) !isa(newbody, Expr) && return true @@ -5237,17 +1663,14 @@ function ssavalue_increment(body::Expr, incr) return body end -const top_getfield = GlobalRef(Core, :getfield) -const top_tuple = GlobalRef(Core, :tuple) - function mk_getfield(texpr, i, T) - e = Expr(:call, top_getfield, texpr, i) + e = Expr(:call, TOP_GETFIELD, texpr, i) e.typ = T return e end function mk_tuplecall(args, sv::OptimizationState) - e = Expr(:call, top_tuple, args...) + e = Expr(:call, TOP_TUPLE, args...) e.typ = tuple_tfunc(Tuple{Any[widenconst(exprtype(x, sv.src, sv.mod)) for x in args]...}) return e end @@ -5306,7 +1729,6 @@ function inlining_pass!(sv::OptimizationState, propagate_inbounds::Bool) end end -const corenumtype = Union{Int32, Int64, Float32, Float64} function inline_expr(e::Expr, sv::OptimizationState, stmts::Vector{Any}, boundscheck::Symbol) if e.head === :call @@ -5373,7 +1795,7 @@ function inline_call(e::Expr, sv::OptimizationState, stmts::Vector{Any}, boundsc triple = (a2 === Int32(3) || a2 === Int64(3)) if square || triple a1 = e.args[2] - basenumtype = Union{corenumtype, Main.Base.ComplexF32, Main.Base.ComplexF64, Main.Base.Rational} + basenumtype = Union{CoreNumType, Main.Base.ComplexF32, Main.Base.ComplexF64, Main.Base.Rational} if isa(a1, basenumtype) || ((isa(a1, Symbol) || isa(a1, Slot) || isa(a1, SSAValue)) && exprtype(a1, sv.src, sv.mod) ⊑ basenumtype) if square @@ -5407,12 +1829,12 @@ function inline_call(e::Expr, sv::OptimizationState, stmts::Vector{Any}, boundsc res = res[1] end - if res !== NF + if res !== NOT_FOUND # 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.src, sv.mod) e = res::Expr - f = _apply; ft = abstract_eval_constant(f) + f = _apply; ft = AbstractEvalConstant(f) else return res end @@ -5514,14 +1936,13 @@ function inline_call(e::Expr, sv::OptimizationState, stmts::Vector{Any}, boundsc return e end -const compiler_temp_sym = Symbol("#temp#") -function add_slot!(src::CodeInfo, @nospecialize(typ), is_sa::Bool, name::Symbol=compiler_temp_sym) +function add_slot!(src::CodeInfo, @nospecialize(typ), is_sa::Bool, name::Symbol=COMPILER_TEMP_SYM) @assert !isa(typ, Const) && !isa(typ, Conditional) id = length(src.slotnames) + 1 push!(src.slotnames, name) push!(src.slottypes, typ) - push!(src.slotflags, is_sa * Slot_AssignedOnce) + push!(src.slotflags, is_sa * SLOT_ASSIGNEDONCE) return SlotNumber(id) end @@ -5553,7 +1974,7 @@ function void_use_elim_pass!(sv::OptimizationState) # Explicitly listed here for clarity return false elseif isa(ex, Slot) - return sv.src.slotflags[slot_id(ex)] & Slot_UsedUndef != 0 + return sv.src.slotflags[slot_id(ex)] & SLOT_USEDUNDEF != 0 elseif isa(ex, GlobalRef) ex = ex::GlobalRef return !isdefined(ex.mod, ex.name) @@ -5801,9 +2222,9 @@ end function get_undef_flag_slot(src::CodeInfo, flagslots, id) flag_id = flagslots[id] flag_id != 0 && return SlotNumber(flag_id) - slot = add_slot!(src, Nothing, src.slotflags[id] & Slot_AssignedOnce != 0, src.slotnames[id]) + slot = add_slot!(src, Nothing, src.slotflags[id] & SLOT_ASSIGNEDONCE != 0, src.slotnames[id]) flag_id = slot_id(slot) - src.slotflags[flag_id] |= Slot_StaticUndef | Slot_UsedUndef + src.slotflags[flag_id] |= SLOT_STATICUNDEF | SLOT_USEDUNDEF flagslots[id] = flag_id return slot end @@ -5904,33 +2325,12 @@ function split_undef_flag_pass!(sv::OptimizationState) end for i in 1:norigslots if flagslots[i] != 0 - sv.src.slotflags[i] = (sv.src.slotflags[i] | Slot_StaticUndef) & ~UInt8(Slot_UsedUndef) + sv.src.slotflags[i] = (sv.src.slotflags[i] | SLOT_STATICUNDEF) & ~UInt8(SLOT_USEDUNDEF) end end end -# This struct contains information about a use of certain value (`SSAValue` or `Slot`) -# This might be a toplevel use for `Slot` in which case the `expr` field is `#undef`, -# and it only happens if the slot might be used before it's defined. -struct ValueUse - # The statement array where `expr` (or its parent assignment) appears in - stmts::Vector{Any} - # The position of the `expr` in the `stmts` array - stmtidx::Int - - # The position the value appears in `expr` - exidx::Int - # The expression the value is used in. - # If `expr` is undef, the use is a statement level use. - # This must be one of the following: - # 1. A statement level `Expr(:(=), ...)`. - # 2. The RHS of a statement level `Expr(:(=), ...)`. - # 3. A `&` ccall argument. - expr::Expr - ValueUse(stmts, stmtidx, expr, exidx) = new(stmts, stmtidx, exidx, expr) - ValueUse(stmts, stmtidx) = new(stmts, stmtidx, 0) -end # Check if the use is still valid. # The code that invalidate this use is responsible for adding new use(s) if any. function check_valid(use::ValueUse, changes::ObjectIdDict) @@ -5939,31 +2339,12 @@ function check_valid(use::ValueUse, changes::ObjectIdDict) return true end -# This struct contains information about a def of certain value (`SSAValue` or `Slot`) -# The `assign` field is usually an assignment but it might be a `NewvarNode` for `Slot`, -# which can happen if the slot might be used before it's defined. -struct ValueDef - assign::Union{Expr,NewvarNode} - # The statement array where `expr` (or its parent assignment) appears in - stmts::Vector{Any} - # The position of the `expr` in the `stmts` array - stmtidx::Int -end # Check if the use is still valid. # The code that invalidate this use is responsible for adding new def(s) if any. check_valid(def::ValueDef, changes::ObjectIdDict) = !haskey(changes, def.stmts=>def.stmtidx) -# Allocation optimization, must not be mutated. -const empty_uses = ValueUse[] -const empty_defs = ValueDef[] -# Uses and defs of a value. -mutable struct ValueInfo - uses::Vector{ValueUse} - defs::Vector{ValueDef} - has_method::Bool - ValueInfo() = new(empty_uses, empty_defs, false) -end + function remove_invalid!(info::ValueInfo, changes::ObjectIdDict) if isempty(changes) return @@ -5975,7 +2356,7 @@ function remove_invalid!(info::ValueInfo, changes::ObjectIdDict) end function add_def(info::ValueInfo, def::ValueDef) - if info.defs === empty_defs + if info.defs === EMPTY_DEFS info.defs = [def] else push!(info.defs, def) @@ -5983,7 +2364,7 @@ function add_def(info::ValueInfo, def::ValueDef) return end function add_use(info::ValueInfo, use::ValueUse) - if info.uses === empty_uses + if info.uses === EMPTY_USES info.uses = [use] else push!(info.uses, use) @@ -5991,14 +2372,6 @@ function add_use(info::ValueInfo, use::ValueUse) return end -# The def and use information of all variables in a function. -# `slots` is indexed by slot id and `ssas` is indexed by ssa id + 1. -# This structure is indexable by either a `Slot`, a `SSAValue` or a `id=>is_ssa` pair. -struct ValueInfoMap - slots::Vector{ValueInfo} - ssas::Vector{ValueInfo} - ValueInfoMap() = new(ValueInfo[], ValueInfo[]) -end @inline get_info_entry(infomap::ValueInfoMap, slot::Slot) = (infomap.slots, slot_id(slot)) @inline get_info_entry(infomap::ValueInfoMap, ssa::SSAValue) = (infomap.ssas, ssa.id + 1) @inline get_info_entry(infomap::ValueInfoMap, pair::Pair{Int,Bool}) = @@ -6031,20 +2404,20 @@ add_use(infomap::ValueInfoMap, var, use::ValueUse) = add_use(infomap[var], use) ssa && return false flags = src.slotflags[id] # The check for `UsedUndef` shouldn't be necessary but doesn't hurt - return flags & Slot_UsedUndef != 0 || flags & Slot_StaticUndef != 0 + return flags & SLOT_USEDUNDEF != 0 || flags & SLOT_STATICUNDEF != 0 end @inline function var_has_undef(src, id, ssa=false) ssa && return false flags = src.slotflags[id] - return flags & Slot_UsedUndef != 0 + return flags & SLOT_USEDUNDEF != 0 end @inline function var_is_ssa(src, id, ssa=false) ssa && return true flags = src.slotflags[id] # The check for `UsedUndef` shouldn't be necessary but doesn't hurt - return flags & (Slot_UsedUndef | Slot_StaticUndef) == 0 && flags & Slot_AssignedOnce != 0 + return flags & (SLOT_USEDUNDEF | SLOT_STATICUNDEF) == 0 && flags & SLOT_ASSIGNEDONCE != 0 end function scan_expr_use!(infomap, body, i, ex, src) @@ -6141,7 +2514,7 @@ function collect_value_infos(body::Vector{Any}, src::CodeInfo, nargs::Int) if isassigned(slotsinfo, i) info = slotsinfo[i] if i > nargs && length(info.defs) == 1 && isa(info.defs[1].assign, Expr) - src.slotflags[i] |= Slot_AssignedOnce + src.slotflags[i] |= SLOT_ASSIGNEDONCE end end end @@ -6149,39 +2522,7 @@ function collect_value_infos(body::Vector{Any}, src::CodeInfo, nargs::Int) return infomap end -struct StructInfo - defs::Vector{Any} - names::Vector{Symbol} - typ::DataType - mutable::Bool - isnew::Bool -end -struct AllocOptContext - infomap::ValueInfoMap - sv::OptimizationState - todo::ObjectIdDict - changes::ObjectIdDict - sym_count::ObjectIdDict - all_fld::ObjectIdDict - setfield_typ::ObjectIdDict - undef_fld::ObjectIdDict - structinfos::Vector{StructInfo} - function AllocOptContext(infomap::ValueInfoMap, sv::OptimizationState) - todo = ObjectIdDict() - for i in 1:length(infomap.ssas) - isassigned(infomap.ssas, i) || continue - todo[i=>true] = nothing - end - for i in 1:length(infomap.slots) - isassigned(infomap.slots, i) || continue - i > sv.nargs || continue - todo[i=>false] = nothing - end - return new(infomap, sv, todo, ObjectIdDict(), ObjectIdDict(), - ObjectIdDict(), ObjectIdDict(), ObjectIdDict(), StructInfo[]) - end -end function delete_valueinfo!(ctx::AllocOptContext, key) # Slot @@ -6455,7 +2796,7 @@ function split_disjoint_assign!(ctx::AllocOptContext, info, key) new_slot = alltypes[rhstyp] if !isa(new_slot, SlotNumber) new_slot = add_slot!(ctx.sv.src, rhstyp, false, name) - ctx.sv.src.slotflags[new_slot.id] = flags | Slot_StaticUndef + ctx.sv.src.slotflags[new_slot.id] = flags | SLOT_STATICUNDEF alltypes[rhstyp] = new_slot add_allocopt_todo(ctx, new_slot) end @@ -6480,7 +2821,7 @@ function split_disjoint_assign!(ctx::AllocOptContext, info, key) new_slot = alltypes[usetyp] if !isa(new_slot, SlotNumber) new_slot = add_slot!(ctx.sv.src, usetyp, false, name) - ctx.sv.src.slotflags[new_slot.id] = flags | Slot_StaticUndef + ctx.sv.src.slotflags[new_slot.id] = flags | SLOT_STATICUNDEF alltypes[usetyp] = new_slot end new_slot = new_slot::SlotNumber @@ -7038,7 +3379,7 @@ function replace_struct_defs!(ctx, info, vars) end function create_struct_field_slots!(ctx, key, vars) - slot_flag = var_has_static_undef(ctx.sv.src, key.first, key.second) * Slot_StaticUndef + slot_flag = var_has_static_undef(ctx.sv.src, key.first, key.second) * SLOT_STATICUNDEF for fld in keys(ctx.all_fld) haskey(vars, fld) && continue local fldidx @@ -7067,7 +3408,7 @@ function create_struct_field_slots!(ctx, key, vars) slot_id = add_slot!(ctx.sv.src, slot_type, false, slot_name).id add_allocopt_todo(ctx, slot_id, false) if haskey(ctx.undef_fld, fld) - ctx.sv.src.slotflags[end] = Slot_StaticUndef + ctx.sv.src.slotflags[end] = SLOT_STATICUNDEF undef_slot = add_slot!(ctx.sv.src, Bool, false, :field_flag) ctx.sv.src.slotflags[end] = slot_flag add_allocopt_todo(ctx, undef_slot) @@ -7161,12 +3502,12 @@ function split_struct_alloc_single!(ctx::AllocOptContext, info, key, nf, has_pre # OK, so someone calls setfield! on this =( # We need to allocate the variable **AND** the undef tag variable flag_slot = add_slot!(ctx.sv.src, Bool, false, fld_name) - ctx.sv.src.slotflags[end] = is_ssa ? 0 : Slot_StaticUndef + ctx.sv.src.slotflags[end] = is_ssa ? 0 : SLOT_STATICUNDEF flag_vars[i] = flag_slot add_allocopt_todo(ctx, flag_slot) var_slot = add_slot!(ctx.sv.src, field_typ, false, fld_name) - ctx.sv.src.slotflags[end] = Slot_StaticUndef + ctx.sv.src.slotflags[end] = SLOT_STATICUNDEF vars[i] = var_slot add_allocopt_todo(ctx, var_slot) @@ -7176,7 +3517,7 @@ function split_struct_alloc_single!(ctx::AllocOptContext, info, key, nf, has_pre continue elseif has_setfld_use var_slot = add_slot!(ctx.sv.src, field_typ, false, fld_name) - ctx.sv.src.slotflags[end] = is_ssa ? 0 : Slot_StaticUndef + ctx.sv.src.slotflags[end] = is_ssa ? 0 : SLOT_STATICUNDEF def_ex = :($var_slot = $orig_def) push!(def_exprs, def_ex) scan_expr_use!(ctx.infomap, def_exprs, length(def_exprs), def_ex, ctx.sv.src) @@ -7202,7 +3543,7 @@ function split_struct_alloc_single!(ctx::AllocOptContext, info, key, nf, has_pre end else var_slot = add_slot!(ctx.sv.src, field_typ, false, fld_name) - ctx.sv.src.slotflags[end] = Slot_StaticUndef + ctx.sv.src.slotflags[end] = SLOT_STATICUNDEF end if need_assign def_ex = :($var_slot = $orig_def) @@ -7469,7 +3810,7 @@ function verify_value_infomap(ctx::AllocOptContext) @check_ast(ctx, assign.args[1] === slotv) end end - if ctx.sv.src.slotflags[i] & Slot_AssignedOnce != 0 + if ctx.sv.src.slotflags[i] & SLOT_ASSIGNEDONCE != 0 @check_ast(ctx, ndef <= 1) end for use in info.uses @@ -7618,8 +3959,7 @@ function optimize_value!(ctx::AllocOptContext, key) return end -const enable_verify_valueinfo = Array{Bool,0}(uninitialized) -enable_verify_valueinfo[] = false + # Simplify the AST and eliminate unnecessary allocations # This does the following optimizations iteratively until there's no changes to be made @@ -7634,13 +3974,13 @@ function alloc_elim_pass!(sv::OptimizationState) body = sv.src.code infomap = collect_value_infos(body, sv.src, sv.nargs) ctx = AllocOptContext(infomap, sv) - enable_verify_valueinfo[] && verify_value_infomap(ctx) + ENABLE_VERIFY_VALUEINFO[] && verify_value_infomap(ctx) while !isempty(ctx.todo) k, v = first(ctx.todo) k = k::Pair{Int,Bool} delete!(ctx.todo, k) optimize_value!(ctx, k) - enable_verify_valueinfo[] && verify_value_infomap(ctx) + ENABLE_VERIFY_VALUEINFO[] && verify_value_infomap(ctx) end len = length(body) next_i = 1 @@ -7702,7 +4042,6 @@ function alloc_elim_pass!(sv::OptimizationState) end end -const meta_pop_loc = Expr(:meta, :pop_loc) function copy_expr_in_array!(ary, seen) for i in 1:length(ary) @@ -7711,8 +4050,8 @@ function copy_expr_in_array!(ary, seen) ex = ex::Expr if ex.head === :meta # Try to save some memory by using the same object for all `:pop_loc` meta node - if ex !== meta_pop_loc && length(ex.args) == 1 && ex.args[1] === :pop_loc - ary[i] = meta_pop_loc + if ex !== META_POP_LOC && length(ex.args) == 1 && ex.args[1] === :pop_loc + ary[i] = META_POP_LOC end continue # No need to copy meta expressions end @@ -7772,7 +4111,7 @@ function reindex_labels!(sv::OptimizationState) end function return_type(@nospecialize(f), @nospecialize(t)) - params = InferenceParams(ccall(:jl_get_tls_world_age, UInt, ())) + params = Params(ccall(:jl_get_tls_world_age, UInt, ())) rt = Union{} if isa(f, Builtin) rt = builtin_tfunction(f, Any[t.parameters...], nothing, params) @@ -7791,37 +4130,3 @@ function return_type(@nospecialize(f), @nospecialize(t)) end return rt end - -#### bootstrapping #### - -# make sure that typeinf is executed before turning on typeinf_ext -# this ensures that typeinf_ext doesn't recurse before it can add the item to the workq -# especially try to make sure any recursive and leaf functions have concrete signatures, -# since we won't be able to specialize & infer them at runtime - -let fs = Any[typeinf_ext, typeinf, typeinf_edge, pure_eval_call], - world = ccall(:jl_get_world_counter, UInt, ()) - for x in t_ffunc_val - push!(fs, x[3]) - end - for i = 1:length(t_ifunc) - if isassigned(t_ifunc, i) - x = t_ifunc[i] - push!(fs, x[3]) - else - println(STDERR, "WARNING: tfunc missing for ", reinterpret(IntrinsicFunction, Int32(i))) - end - end - for f in fs - for m in _methods_by_ftype(Tuple{typeof(f), Vararg{Any}}, 10, typemax(UInt)) - # remove any TypeVars from the intersection - typ = Any[m[1].parameters...] - for i = 1:length(typ) - if isa(typ[i], TypeVar) - typ[i] = typ[i].ub - end - end - typeinf_type(m[3], Tuple{typ...}, m[2], true, InferenceParams(world)) - end - end -end diff --git a/base/compiler/params.jl b/base/compiler/params.jl new file mode 100644 index 0000000000000..5806604f6feeb --- /dev/null +++ b/base/compiler/params.jl @@ -0,0 +1,54 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +struct Params + cache::Vector{InferenceResult} + world::UInt + + # optimization + inlining::Bool + ipo_constant_propagation::Bool + aggressive_constant_propagation::Bool + inline_cost_threshold::Int # number of CPU cycles beyond which it's not worth inlining + inline_nonleaf_penalty::Int # penalty for dynamic dispatch + inline_tupleret_bonus::Int # extra willingness for non-isbits tuple return types + + # don't consider more than N methods. this trades off between + # compiler performance and generated code performance. + # typically, considering many methods means spending lots of time + # obtaining poor type information. + # It is important for N to be >= the number of methods in the error() + # function, so we can still know that error() is always Bottom. + MAX_METHODS::Int + # the maximum number of union-tuples to swap / expand + # before computing the set of matching methods + MAX_UNION_SPLITTING::Int + # the maximum number of union-tuples to swap / expand + # when inferring a call to _apply + MAX_APPLY_UNION_ENUM::Int + + # parameters limiting large types + MAX_TUPLETYPE_LEN::Int + MAX_TUPLE_DEPTH::Int + + # when attempting to inlining _apply, abort the optimization if the tuple + # contains more than this many elements + MAX_TUPLE_SPLAT::Int + + # reasonable defaults + function Params(world::UInt; + inlining::Bool = inlining_enabled(), + inline_cost_threshold::Int = 100, + inline_nonleaf_penalty::Int = 1000, + inline_tupleret_bonus::Int = 400, + max_methods::Int = 4, + tupletype_len::Int = 15, + tuple_depth::Int = 4, + tuple_splat::Int = 16, + union_splitting::Int = 4, + apply_union_enum::Int = 8) + return new(Vector{InferenceResult}(), + world, inlining, true, false, inline_cost_threshold, inline_nonleaf_penalty, + inline_tupleret_bonus, max_methods, union_splitting, apply_union_enum, + tupletype_len, tuple_depth, tuple_splat) + end +end diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl new file mode 100644 index 0000000000000..6576ccb6cc080 --- /dev/null +++ b/base/compiler/tfuncs.jl @@ -0,0 +1,905 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +############# +# constants # +############# + +const AbstractEvalConstant = Const + +const _NAMEDTUPLE_NAME = NamedTuple.body.body.name + +const INT_INF = typemax(Int) # integer infinity + +const N_IFUNC = reinterpret(Int32, arraylen) + 1 +const T_IFUNC = Vector{Tuple{Int, Int, Any}}(uninitialized, N_IFUNC) +const T_IFUNC_COST = Vector{Int}(uninitialized, N_IFUNC) +const T_FFUNC_KEY = Vector{Any}() +const T_FFUNC_VAL = Vector{Tuple{Int, Int, Any}}() +const T_FFUNC_COST = Vector{Int}() + +const DATATYPE_NAME_FIELDINDEX = fieldindex(DataType, :name) +const DATATYPE_PARAMETERS_FIELDINDEX = fieldindex(DataType, :parameters) +const DATATYPE_TYPES_FIELDINDEX = fieldindex(DataType, :types) +const DATATYPE_SUPER_FIELDINDEX = fieldindex(DataType, :super) +const DATATYPE_MUTABLE_FIELDINDEX = fieldindex(DataType, :mutable) + +const TYPENAME_NAME_FIELDINDEX = fieldindex(TypeName, :name) +const TYPENAME_MODULE_FIELDINDEX = fieldindex(TypeName, :module) +const TYPENAME_WRAPPER_FIELDINDEX = fieldindex(TypeName, :wrapper) + +########## +# tfuncs # +########## + +function add_tfunc(f::IntrinsicFunction, minarg::Int, maxarg::Int, @nospecialize(tfunc), cost::Int) + idx = reinterpret(Int32, f) + 1 + T_IFUNC[idx] = (minarg, maxarg, tfunc) + T_IFUNC_COST[idx] = cost +end +# TODO: add @nospecialize on `f` and declare its type as `Builtin` when that's supported +function add_tfunc(f::Function, minarg::Int, maxarg::Int, @nospecialize(tfunc), cost::Int) + push!(T_FFUNC_KEY, f) + push!(T_FFUNC_VAL, (minarg, maxarg, tfunc)) + push!(T_FFUNC_COST, cost) +end + +add_tfunc(throw, 1, 1, (@nospecialize(x)) -> Bottom, 0) + +# the inverse of typeof_tfunc +# returns (type, isexact) +# if isexact is false, the actual runtime type may (will) be a subtype of t +function instanceof_tfunc(@nospecialize(t)) + if t === Bottom || t === typeof(Bottom) + return Bottom, true + elseif isa(t, Const) + if isa(t.val, Type) + return t.val, true + end + elseif isType(t) + tp = t.parameters[1] + return tp, !has_free_typevars(tp) + elseif isa(t, UnionAll) + t′ = unwrap_unionall(t) + t′′, isexact = instanceof_tfunc(t′) + return rewrap_unionall(t′′, t), isexact + elseif isa(t, Union) + ta, isexact_a = instanceof_tfunc(t.a) + tb, isexact_b = instanceof_tfunc(t.b) + return Union{ta, tb}, false # at runtime, will be exactly one of these + end + return Any, false +end +bitcast_tfunc(@nospecialize(t), @nospecialize(x)) = instanceof_tfunc(t)[1] +math_tfunc(@nospecialize(x)) = widenconst(x) +math_tfunc(@nospecialize(x), @nospecialize(y)) = widenconst(x) +math_tfunc(@nospecialize(x), @nospecialize(y), @nospecialize(z)) = widenconst(x) +fptoui_tfunc(@nospecialize(t), @nospecialize(x)) = bitcast_tfunc(t, x) +fptosi_tfunc(@nospecialize(t), @nospecialize(x)) = bitcast_tfunc(t, x) +function fptoui_tfunc(@nospecialize(x)) + T = widenconst(x) + T === Float64 && return UInt64 + T === Float32 && return UInt32 + T === Float16 && return UInt16 + return Any +end +function fptosi_tfunc(@nospecialize(x)) + T = widenconst(x) + T === Float64 && return Int64 + T === Float32 && return Int32 + T === Float16 && return Int16 + return Any +end + + ## conversion ## +add_tfunc(bitcast, 2, 2, bitcast_tfunc, 1) +add_tfunc(sext_int, 2, 2, bitcast_tfunc, 1) +add_tfunc(zext_int, 2, 2, bitcast_tfunc, 1) +add_tfunc(trunc_int, 2, 2, bitcast_tfunc, 1) +add_tfunc(fptoui, 1, 2, fptoui_tfunc, 1) +add_tfunc(fptosi, 1, 2, fptosi_tfunc, 1) +add_tfunc(uitofp, 2, 2, bitcast_tfunc, 1) +add_tfunc(sitofp, 2, 2, bitcast_tfunc, 1) +add_tfunc(fptrunc, 2, 2, bitcast_tfunc, 1) +add_tfunc(fpext, 2, 2, bitcast_tfunc, 1) + ## arithmetic ## +add_tfunc(neg_int, 1, 1, math_tfunc, 1) +add_tfunc(add_int, 2, 2, math_tfunc, 1) +add_tfunc(sub_int, 2, 2, math_tfunc, 1) +add_tfunc(mul_int, 2, 2, math_tfunc, 4) +add_tfunc(sdiv_int, 2, 2, math_tfunc, 30) +add_tfunc(udiv_int, 2, 2, math_tfunc, 30) +add_tfunc(srem_int, 2, 2, math_tfunc, 30) +add_tfunc(urem_int, 2, 2, math_tfunc, 30) +add_tfunc(add_ptr, 2, 2, math_tfunc, 1) +add_tfunc(sub_ptr, 2, 2, math_tfunc, 1) +add_tfunc(neg_float, 1, 1, math_tfunc, 1) +add_tfunc(add_float, 2, 2, math_tfunc, 1) +add_tfunc(sub_float, 2, 2, math_tfunc, 1) +add_tfunc(mul_float, 2, 2, math_tfunc, 4) +add_tfunc(div_float, 2, 2, math_tfunc, 20) +add_tfunc(rem_float, 2, 2, math_tfunc, 20) +add_tfunc(fma_float, 3, 3, math_tfunc, 5) +add_tfunc(muladd_float, 3, 3, math_tfunc, 5) + ## fast arithmetic ## +add_tfunc(neg_float_fast, 1, 1, math_tfunc, 1) +add_tfunc(add_float_fast, 2, 2, math_tfunc, 1) +add_tfunc(sub_float_fast, 2, 2, math_tfunc, 1) +add_tfunc(mul_float_fast, 2, 2, math_tfunc, 2) +add_tfunc(div_float_fast, 2, 2, math_tfunc, 10) +add_tfunc(rem_float_fast, 2, 2, math_tfunc, 10) + ## bitwise operators ## +add_tfunc(and_int, 2, 2, math_tfunc, 1) +add_tfunc(or_int, 2, 2, math_tfunc, 1) +add_tfunc(xor_int, 2, 2, math_tfunc, 1) +add_tfunc(not_int, 1, 1, math_tfunc, 1) +add_tfunc(shl_int, 2, 2, math_tfunc, 1) +add_tfunc(lshr_int, 2, 2, math_tfunc, 1) +add_tfunc(ashr_int, 2, 2, math_tfunc, 1) +add_tfunc(bswap_int, 1, 1, math_tfunc, 1) +add_tfunc(ctpop_int, 1, 1, math_tfunc, 1) +add_tfunc(ctlz_int, 1, 1, math_tfunc, 1) +add_tfunc(cttz_int, 1, 1, math_tfunc, 1) +add_tfunc(checked_sdiv_int, 2, 2, math_tfunc, 40) +add_tfunc(checked_udiv_int, 2, 2, math_tfunc, 40) +add_tfunc(checked_srem_int, 2, 2, math_tfunc, 40) +add_tfunc(checked_urem_int, 2, 2, math_tfunc, 40) + ## functions ## +add_tfunc(abs_float, 1, 1, math_tfunc, 2) +add_tfunc(copysign_float, 2, 2, math_tfunc, 2) +add_tfunc(flipsign_int, 2, 2, math_tfunc, 1) +add_tfunc(ceil_llvm, 1, 1, math_tfunc, 10) +add_tfunc(floor_llvm, 1, 1, math_tfunc, 10) +add_tfunc(trunc_llvm, 1, 1, math_tfunc, 10) +add_tfunc(rint_llvm, 1, 1, math_tfunc, 10) +add_tfunc(sqrt_llvm, 1, 1, math_tfunc, 20) + ## same-type comparisons ## +cmp_tfunc(@nospecialize(x), @nospecialize(y)) = Bool +add_tfunc(eq_int, 2, 2, cmp_tfunc, 1) +add_tfunc(ne_int, 2, 2, cmp_tfunc, 1) +add_tfunc(slt_int, 2, 2, cmp_tfunc, 1) +add_tfunc(ult_int, 2, 2, cmp_tfunc, 1) +add_tfunc(sle_int, 2, 2, cmp_tfunc, 1) +add_tfunc(ule_int, 2, 2, cmp_tfunc, 1) +add_tfunc(eq_float, 2, 2, cmp_tfunc, 2) +add_tfunc(ne_float, 2, 2, cmp_tfunc, 2) +add_tfunc(lt_float, 2, 2, cmp_tfunc, 2) +add_tfunc(le_float, 2, 2, cmp_tfunc, 2) +add_tfunc(fpiseq, 2, 2, cmp_tfunc, 1) +add_tfunc(fpislt, 2, 2, cmp_tfunc, 1) +add_tfunc(eq_float_fast, 2, 2, cmp_tfunc, 1) +add_tfunc(ne_float_fast, 2, 2, cmp_tfunc, 1) +add_tfunc(lt_float_fast, 2, 2, cmp_tfunc, 1) +add_tfunc(le_float_fast, 2, 2, cmp_tfunc, 1) + + ## checked arithmetic ## +chk_tfunc(@nospecialize(x), @nospecialize(y)) = Tuple{widenconst(x), Bool} +add_tfunc(checked_sadd_int, 2, 2, chk_tfunc, 10) +add_tfunc(checked_uadd_int, 2, 2, chk_tfunc, 10) +add_tfunc(checked_ssub_int, 2, 2, chk_tfunc, 10) +add_tfunc(checked_usub_int, 2, 2, chk_tfunc, 10) +add_tfunc(checked_smul_int, 2, 2, chk_tfunc, 10) +add_tfunc(checked_umul_int, 2, 2, chk_tfunc, 10) + ## other, misc intrinsics ## +add_tfunc(Core.Intrinsics.llvmcall, 3, INT_INF, + (@nospecialize(fptr), @nospecialize(rt), @nospecialize(at), a...) -> instanceof_tfunc(rt)[1], 10) +cglobal_tfunc(@nospecialize(fptr)) = Ptr{Cvoid} +cglobal_tfunc(@nospecialize(fptr), @nospecialize(t)) = (isType(t) ? Ptr{t.parameters[1]} : Ptr) +cglobal_tfunc(@nospecialize(fptr), t::Const) = (isa(t.val, Type) ? Ptr{t.val} : Ptr) +add_tfunc(Core.Intrinsics.cglobal, 1, 2, cglobal_tfunc, 5) +add_tfunc(Core.Intrinsics.select_value, 3, 3, + function (@nospecialize(cnd), @nospecialize(x), @nospecialize(y)) + if isa(cnd, Const) + if cnd.val === true + return x + elseif cnd.val === false + return y + else + return Bottom + end + end + (Bool ⊑ cnd) || return Bottom + return tmerge(x, y) + end, 1) +add_tfunc(===, 2, 2, + function (@nospecialize(x), @nospecialize(y)) + if isa(x, Const) && isa(y, Const) + return Const(x.val === y.val) + elseif typeintersect(widenconst(x), widenconst(y)) === Bottom + return Const(false) + elseif (isa(x, Const) && y === typeof(x.val) && isdefined(y, :instance)) || + (isa(y, Const) && x === typeof(y.val) && isdefined(x, :instance)) + return Const(true) + elseif isa(x, Conditional) && isa(y, Const) + y.val === false && return Conditional(x.var, x.elsetype, x.vtype) + y.val === true && return x + return x + elseif isa(y, Conditional) && isa(x, Const) + x.val === false && return Conditional(y.var, y.elsetype, y.vtype) + x.val === true && return y + end + return Bool + end, 1) +function isdefined_tfunc(args...) + arg1 = args[1] + if isa(arg1, Const) + a1 = typeof(arg1.val) + else + a1 = widenconst(arg1) + end + if isType(a1) + return Bool + end + a1 = unwrap_unionall(a1) + if isa(a1, DataType) && !a1.abstract + if a1 <: Array # TODO update when deprecation is removed + elseif a1 === Module + length(args) == 2 || return Bottom + sym = args[2] + Symbol <: widenconst(sym) || return Bottom + if isa(sym, Const) && isa(sym.val, Symbol) && isa(arg1, Const) && isdefined(arg1.val, sym.val) + return Const(true) + end + elseif length(args) == 2 && isa(args[2], Const) + val = args[2].val + idx::Int = 0 + if isa(val, Symbol) + idx = fieldindex(a1, val, false) + elseif isa(val, Int) + idx = val + else + return Bottom + end + if 1 <= idx <= a1.ninitialized + return Const(true) + elseif a1.name === _NAMEDTUPLE_NAME + if isleaftype(a1) + return Const(false) + end + elseif idx <= 0 || (!isvatuple(a1) && idx > fieldcount(a1)) + return Const(false) + elseif !isvatuple(a1) && isbits(fieldtype(a1, idx)) + return Const(true) + elseif isa(arg1, Const) && isimmutable((arg1::Const).val) + return Const(isdefined((arg1::Const).val, idx)) + end + end + end + Bool +end +# TODO change INT_INF to 2 when deprecation is removed +add_tfunc(isdefined, 1, INT_INF, isdefined_tfunc, 1) +_const_sizeof(@nospecialize(x)) = try + # Constant Vector does not have constant size + isa(x, Vector) && return Int + return Const(Core.sizeof(x)) +catch + return Int +end +add_tfunc(Core.sizeof, 1, 1, + function (@nospecialize(x),) + isa(x, Const) && return _const_sizeof(x.val) + isa(x, Conditional) && return _const_sizeof(Bool) + isconstType(x) && return _const_sizeof(x.parameters[1]) + x !== DataType && isleaftype(x) && return _const_sizeof(x) + return Int + end, 0) +old_nfields(@nospecialize x) = length((isa(x,DataType) ? x : typeof(x)).types) +add_tfunc(nfields, 1, 1, + function (@nospecialize(x),) + isa(x,Const) && return Const(old_nfields(x.val)) + isa(x,Conditional) && return Const(old_nfields(Bool)) + if isType(x) + # TODO: remove with deprecation in builtins.c for nfields(::Type) + isleaftype(x.parameters[1]) && return Const(old_nfields(x.parameters[1])) + elseif isa(x,DataType) && !x.abstract && !(x.name === Tuple.name && isvatuple(x)) && x !== DataType + if !(x.name === _NAMEDTUPLE_NAME && !isleaftype(x)) + return Const(length(x.types)) + end + end + return Int + end, 0) +add_tfunc(Core._expr, 1, INT_INF, (args...)->Expr, 100) +add_tfunc(applicable, 1, INT_INF, (@nospecialize(f), args...)->Bool, 100) +add_tfunc(Core.Intrinsics.arraylen, 1, 1, x->Int, 4) +add_tfunc(arraysize, 2, 2, (@nospecialize(a), @nospecialize(d))->Int, 4) +add_tfunc(pointerref, 3, 3, + function (@nospecialize(a), @nospecialize(i), @nospecialize(align)) + a = widenconst(a) + if a <: Ptr + if isa(a,DataType) && isa(a.parameters[1],Type) + return a.parameters[1] + elseif isa(a,UnionAll) && !has_free_typevars(a) + unw = unwrap_unionall(a) + if isa(unw,DataType) + return rewrap_unionall(unw.parameters[1], a) + end + end + end + return Any + end, 4) +add_tfunc(pointerset, 4, 4, (@nospecialize(a), @nospecialize(v), @nospecialize(i), @nospecialize(align)) -> a, 5) + +function typeof_tfunc(@nospecialize(t)) + if isa(t, Const) + return Const(typeof(t.val)) + elseif isa(t, Conditional) + return Const(Bool) + elseif isType(t) + tp = t.parameters[1] + if !isleaftype(tp) + return DataType # typeof(Kind::Type)::DataType + else + return Const(typeof(tp)) # XXX: this is not necessarily true + end + elseif isa(t, DataType) + if isleaftype(t) || isvarargtype(t) + return Const(t) + elseif t === Any + return DataType + else + return Type{<:t} + end + elseif isa(t, Union) + a = widenconst(typeof_tfunc(t.a)) + b = widenconst(typeof_tfunc(t.b)) + return Union{a, b} + elseif isa(t, TypeVar) && !(Any <: t.ub) + return typeof_tfunc(t.ub) + elseif isa(t, UnionAll) + return rewrap_unionall(widenconst(typeof_tfunc(unwrap_unionall(t))), t) + else + return DataType # typeof(anything)::DataType + end +end +add_tfunc(typeof, 1, 1, typeof_tfunc, 0) +add_tfunc(typeassert, 2, 2, + function (@nospecialize(v), @nospecialize(t)) + t, isexact = instanceof_tfunc(t) + t === Any && return v + if isa(v, Const) + if !has_free_typevars(t) && !isa(v.val, t) + return Bottom + end + return v + elseif isa(v, Conditional) + if !(Bool <: t) + return Bottom + end + return v + end + return typeintersect(v, t) + end, 4) +add_tfunc(isa, 2, 2, + function (@nospecialize(v), @nospecialize(t)) + t, isexact = instanceof_tfunc(t) + if !has_free_typevars(t) + if t === Bottom + return Const(false) + elseif v ⊑ t + if isexact + return Const(true) + end + elseif isa(v, Const) || isa(v, Conditional) || (isleaftype(v) && !iskindtype(v)) + return Const(false) + elseif isexact && typeintersect(v, t) === Bottom + if !iskindtype(v) #= subtyping currently intentionally answers this query incorrectly for kinds =# + return Const(false) + end + end + end + # TODO: handle non-leaftype(t) by testing against lower and upper bounds + return Bool + end, 0) +add_tfunc(<:, 2, 2, + function (@nospecialize(a), @nospecialize(b)) + a, isexact_a = instanceof_tfunc(a) + b, isexact_b = instanceof_tfunc(b) + if !has_free_typevars(a) && !has_free_typevars(b) + if a <: b + if isexact_b || a === Bottom + return Const(true) + end + else + if isexact_a || (b !== Bottom && typeintersect(a, b) === Union{}) + return Const(false) + end + end + end + return Bool + end, 0) + +function const_datatype_getfield_tfunc(sv, fld) + if (fld == DATATYPE_NAME_FIELDINDEX || + fld == DATATYPE_PARAMETERS_FIELDINDEX || + fld == DATATYPE_TYPES_FIELDINDEX || + fld == DATATYPE_SUPER_FIELDINDEX || + fld == DATATYPE_MUTABLE_FIELDINDEX) + return AbstractEvalConstant(getfield(sv, fld)) + end + return nothing +end + +getfield_tfunc(@nospecialize(s00), @nospecialize(name), @nospecialize(inbounds)) = + getfield_tfunc(s00, name) +function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) + if isa(s00, TypeVar) + s00 = s00.ub + end + s = unwrap_unionall(s00) + if isa(s, Union) + return tmerge(rewrap(getfield_tfunc(s.a, name),s00), + rewrap(getfield_tfunc(s.b, name),s00)) + elseif isa(s, Conditional) + return Bottom # Bool has no fields + elseif isa(s, Const) || isconstType(s) + if !isa(s, Const) + sv = s.parameters[1] + else + sv = s.val + end + if isa(name, Const) + nv = name.val + if isa(sv, UnionAll) + if nv === :var || nv === 1 + return Const(sv.var) + elseif nv === :body || nv === 2 + return Const(sv.body) + end + elseif isa(sv, DataType) + t = const_datatype_getfield_tfunc(sv, isa(nv, Symbol) ? + fieldindex(DataType, nv, false) : nv) + t !== nothing && return t + elseif isa(sv, TypeName) + fld = isa(nv, Symbol) ? fieldindex(TypeName, nv, false) : nv + if (fld == TYPENAME_NAME_FIELDINDEX || + fld == TYPENAME_MODULE_FIELDINDEX || + fld == TYPENAME_WRAPPER_FIELDINDEX) + return AbstractEvalConstant(getfield(sv, fld)) + end + end + if isa(sv, Module) && isa(nv, Symbol) + return abstract_eval_global(sv, nv) + end + if !(isa(nv,Symbol) || isa(nv,Int)) + return Bottom + end + if (isa(sv, SimpleVector) || isimmutable(sv)) && isdefined(sv, nv) + return AbstractEvalConstant(getfield(sv, nv)) + end + end + s = typeof(sv) + end + if isType(s) || !isa(s, DataType) || s.abstract + return Any + end + if s <: Tuple && name ⊑ Symbol + return Bottom + end + if s <: Module + if name ⊑ Int + return Bottom + end + return Any + end + if s.name === _NAMEDTUPLE_NAME && !isleaftype(s) + # TODO: better approximate inference + return Any + end + if isempty(s.types) + return Bottom + end + if isa(name, Conditional) + return Bottom # can't index fields with Bool + end + if !isa(name, Const) + if !(Int <: name || Symbol <: name) + return Bottom + end + if length(s.types) == 1 + return rewrap_unionall(unwrapva(s.types[1]), s00) + end + # union together types of all fields + R = reduce(tmerge, Bottom, map(t -> rewrap_unionall(unwrapva(t), s00), s.types)) + # do the same limiting as the known-symbol case to preserve type-monotonicity + if isempty(s.parameters) + return R + end + return limit_type_depth(R, MAX_TYPE_DEPTH) + end + fld = name.val + if isa(fld,Symbol) + fld = fieldindex(s, fld, false) + end + if !isa(fld,Int) + return Bottom + end + nf = length(s.types) + if s <: Tuple && fld >= nf && isvarargtype(s.types[nf]) + return rewrap_unionall(unwrapva(s.types[nf]), s00) + end + if fld < 1 || fld > nf + return Bottom + end + if isType(s00) && isleaftype(s00.parameters[1]) + sp = s00.parameters[1] + elseif isa(s00, Const) && isa(s00.val, DataType) + sp = s00.val + else + sp = nothing + end + if sp !== nothing + t = const_datatype_getfield_tfunc(sp, fld) + t !== nothing && return t + end + R = s.types[fld] + if isempty(s.parameters) + return R + end + # TODO jb/subtype is this still necessary? + # conservatively limit the type depth here, + # since the UnionAll type bound is otherwise incorrect + # in the current type system + return rewrap_unionall(limit_type_depth(R, MAX_TYPE_DEPTH), s00) +end +add_tfunc(getfield, 2, 3, getfield_tfunc, 1) +add_tfunc(setfield!, 3, 3, (@nospecialize(o), @nospecialize(f), @nospecialize(v)) -> v, 3) +fieldtype_tfunc(@nospecialize(s0), @nospecialize(name), @nospecialize(inbounds)) = + fieldtype_tfunc(s0, name) +function fieldtype_tfunc(@nospecialize(s0), @nospecialize(name)) + if s0 === Any || s0 === Type || DataType ⊑ s0 || UnionAll ⊑ s0 + return Type + end + # fieldtype only accepts DataType and UnionAll, errors on `Module` + if isa(s0,Const) && (!(isa(s0.val,DataType) || isa(s0.val,UnionAll)) || s0.val === Module) + return Bottom + end + if s0 == Type{Module} || s0 == Type{Union{}} || isa(s0, Conditional) + return Bottom + end + + s = instanceof_tfunc(s0)[1] + u = unwrap_unionall(s) + + if isa(u,Union) + return tmerge(rewrap(fieldtype_tfunc(u.a, name),s), + rewrap(fieldtype_tfunc(u.b, name),s)) + end + + if !isa(u,DataType) || u.abstract + return Type + end + if u.name === _NAMEDTUPLE_NAME && !isleaftype(u) + return Type + end + ftypes = u.types + if isempty(ftypes) + return Bottom + end + + if !isa(name, Const) + if !(Int <: name || Symbol <: name) + return Bottom + end + return reduce(tmerge, Bottom, + Any[ fieldtype_tfunc(s0, Const(i)) for i = 1:length(ftypes) ]) + end + + fld = name.val + if isa(fld,Symbol) + fld = fieldindex(u, fld, false) + end + if !isa(fld, Int) + return Bottom + end + nf = length(ftypes) + if u.name === Tuple.name && fld >= nf && isvarargtype(ftypes[nf]) + ft = unwrapva(ftypes[nf]) + elseif fld < 1 || fld > nf + return Bottom + else + ft = ftypes[fld] + end + + exact = (isa(s0, Const) || isType(s0)) && !has_free_typevars(s) + ft = rewrap_unionall(ft,s) + if exact + return Const(ft) + end + return Type{<:ft} +end +add_tfunc(fieldtype, 2, 3, fieldtype_tfunc, 0) + +# TODO: handle e.g. apply_type(T, R::Union{Type{Int32},Type{Float64}}) +function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...) + if isa(headtypetype, Const) + headtype = headtypetype.val + elseif isType(headtypetype) && isleaftype(headtypetype.parameters[1]) + headtype = headtypetype.parameters[1] + else + return Any + end + largs = length(args) + if headtype === Union + largs == 0 && return Const(Bottom) + largs == 1 && return args[1] + for i = 1:largs + ai = args[i] + if !isa(ai, Const) || !isa(ai.val, Type) + if !isType(ai) + return Any + end + end + end + ty = Union{} + allconst = true + for i = 1:largs + ai = args[i] + if isType(ai) + aty = ai.parameters[1] + isleaftype(aty) || (allconst = false) + else + aty = (ai::Const).val + end + ty = Union{ty, aty} + end + return allconst ? Const(ty) : Type{ty} + end + istuple = (headtype == Tuple) + if !istuple && !isa(headtype, UnionAll) + # TODO: return `Bottom` for trying to apply a non-UnionAll + return Any + end + uncertain = false + canconst = true + tparams = Any[] + outervars = Any[] + for i = 1:largs + ai = args[i] + if isType(ai) + aip1 = ai.parameters[1] + canconst &= !has_free_typevars(aip1) + push!(tparams, aip1) + elseif isa(ai, Const) && (isa(ai.val, Type) || isa(ai.val, TypeVar) || valid_tparam(ai.val)) + push!(tparams, ai.val) + elseif isa(ai, PartialTypeVar) + canconst = false + push!(tparams, ai.tv) + else + # TODO: return `Bottom` for trying to apply a non-UnionAll + uncertain = true + # These blocks improve type info but make compilation a bit slower. + # XXX + #unw = unwrap_unionall(ai) + #isT = isType(unw) + #if isT && isa(ai,UnionAll) && contains_is(outervars, ai.var) + # ai = rename_unionall(ai) + # unw = unwrap_unionall(ai) + #end + if istuple + if i == largs + push!(tparams, Vararg) + # XXX + #elseif isT + # push!(tparams, rewrap_unionall(unw.parameters[1], ai)) + else + push!(tparams, Any) + end + # XXX + #elseif isT + # push!(tparams, unw.parameters[1]) + # while isa(ai, UnionAll) + # push!(outervars, ai.var) + # ai = ai.body + # end + else + v = TypeVar(:_) + push!(tparams, v) + push!(outervars, v) + end + end + end + local appl + try + appl = apply_type(headtype, tparams...) + catch ex + # type instantiation might fail if one of the type parameters + # doesn't match, which could happen if a type estimate is too coarse + return Type{<:headtype} + end + !uncertain && canconst && return Const(appl) + if isvarargtype(headtype) + return Type + end + if uncertain && type_too_complex(appl, MAX_TYPE_DEPTH) + return Type{<:headtype} + end + if istuple + return Type{<:appl} + end + ans = Type{appl} + for i = length(outervars):-1:1 + ans = UnionAll(outervars[i], ans) + end + return ans +end +add_tfunc(apply_type, 1, INT_INF, apply_type_tfunc, 10) + +function invoke_tfunc(@nospecialize(f), @nospecialize(types), @nospecialize(argtype), sv::InferenceState) + if !isleaftype(Type{types}) + return Any + end + argtype = typeintersect(types,limit_tuple_type(argtype, sv.params)) + if argtype === Bottom + return Bottom + end + ft = type_typeof(f) + types = rewrap_unionall(Tuple{ft, unwrap_unionall(types).parameters...}, types) + argtype = Tuple{ft, argtype.parameters...} + entry = ccall(:jl_gf_invoke_lookup, Any, (Any, UInt), types, sv.params.world) + if entry === nothing + return Any + end + meth = entry.func + (ti, env) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), argtype, meth.sig)::SimpleVector + rt, edge = typeinf_edge(meth::Method, ti, env, sv) + edge !== nothing && add_backedge!(edge::MethodInstance, sv) + return rt +end + +function tuple_tfunc(@nospecialize(argtype)) + if isa(argtype, DataType) && argtype.name === Tuple.name + p = Vector{Any}() + for x in argtype.parameters + if isType(x) && !isa(x.parameters[1], TypeVar) + xparam = x.parameters[1] + if isleaftype(xparam) || xparam === Bottom + push!(p, typeof(xparam)) + else + push!(p, Type) + end + else + push!(p, x) + end + end + t = Tuple{p...} + # replace a singleton type with its equivalent Const object + isdefined(t, :instance) && return Const(t.instance) + return t + end + return argtype +end + +function builtin_tfunction(@nospecialize(f), argtypes::Array{Any,1}, + sv::Union{InferenceState,Nothing}, params::Params = sv.params) + isva = !isempty(argtypes) && isvarargtype(argtypes[end]) + if f === tuple + for a in argtypes + if !isa(a, Const) + return tuple_tfunc(limit_tuple_depth(params, argtypes_to_type(argtypes))) + end + end + return Const(tuple(anymap(a->a.val, argtypes)...)) + elseif f === svec + return SimpleVector + elseif f === arrayset + if length(argtypes) < 4 + isva && return Any + return Bottom + end + return argtypes[2] + elseif f === arrayref + if length(argtypes) < 3 + isva && return Any + return Bottom + end + a = widenconst(argtypes[2]) + if a <: Array + if isa(a, DataType) && (isa(a.parameters[1], Type) || isa(a.parameters[1], TypeVar)) + # TODO: the TypeVar case should not be needed here + a = a.parameters[1] + return isa(a, TypeVar) ? a.ub : a + elseif isa(a, UnionAll) && !has_free_typevars(a) + unw = unwrap_unionall(a) + if isa(unw, DataType) + return rewrap_unionall(unw.parameters[1], a) + end + end + end + return Any + elseif f === Expr + if length(argtypes) < 1 && !isva + return Bottom + end + return Expr + elseif f === invoke + if length(argtypes)>1 && isa(argtypes[1], Const) + af = argtypes[1].val + sig = argtypes[2] + if isa(sig, Const) + sigty = sig.val + elseif isType(sig) + sigty = sig.parameters[1] + else + sigty = nothing + end + if isa(sigty, Type) && sigty <: Tuple && sv !== nothing + return invoke_tfunc(af, sigty, argtypes_to_type(argtypes[3:end]), sv) + end + end + return Any + end + if isva + return Any + end + if isa(f, IntrinsicFunction) + if is_pure_intrinsic_infer(f) && all(a -> isa(a, Const), argtypes) + argvals = anymap(a -> a.val, argtypes) + try + return Const(f(argvals...)) + end + end + iidx = Int(reinterpret(Int32, f::IntrinsicFunction)) + 1 + if iidx < 0 || iidx > length(T_IFUNC) + # invalid intrinsic + return Any + end + tf = T_IFUNC[iidx] + else + fidx = findfirst(x->x===f, T_FFUNC_KEY) + if fidx === nothing + # unknown/unhandled builtin function + return Any + end + tf = T_FFUNC_VAL[fidx] + end + tf = tf::Tuple{Int, Int, Any} + if !(tf[1] <= length(argtypes) <= tf[2]) + # wrong # of args + return Bottom + end + return tf[3](argtypes...) +end + +# TODO: this function is a very buggy and poor model of the return_type function +# since abstract_call_gf_by_type is a very inaccurate model of _method and of typeinf_type, +# while this assumes that it is a precisely accurate and exact model of both +function return_type_tfunc(argtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState) + if length(argtypes) == 3 + tt = argtypes[3] + if isa(tt, Const) || (isType(tt) && !has_free_typevars(tt)) + aft = argtypes[2] + if isa(aft, Const) || (isType(aft) && !has_free_typevars(aft)) || + (isleaftype(aft) && !(aft <: Builtin)) + af_argtype = isa(tt, Const) ? tt.val : tt.parameters[1] + if isa(af_argtype, DataType) && af_argtype <: Tuple + argtypes_vec = Any[aft, af_argtype.parameters...] + if contains_is(argtypes_vec, Union{}) + return Const(Union{}) + end + astype = argtypes_to_type(argtypes_vec) + if isa(aft, Const) + rt = abstract_call(aft.val, (), argtypes_vec, vtypes, sv) + elseif isconstType(aft) + rt = abstract_call(aft.parameters[1], (), argtypes_vec, vtypes, sv) + else + rt = abstract_call_gf_by_type(nothing, argtypes_vec, astype, sv) + end + if isa(rt, Const) + # output was computed to be constant + return Const(typeof(rt.val)) + elseif isleaftype(rt) || rt === Bottom + # output type was known for certain + return Const(rt) + elseif (isa(tt, Const) || isconstType(tt)) && + (isa(aft, Const) || isconstType(aft)) + # input arguments were known for certain + return Const(rt) + else + return Type{<:rt} + end + end + end + end + end + return NOT_FOUND +end diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl new file mode 100644 index 0000000000000..77c4560f2658b --- /dev/null +++ b/base/compiler/typeinfer.jl @@ -0,0 +1,467 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +const COMPILER_TEMP_SYM = Symbol("#temp#") + +# add the real backedges +function finalize_backedges(frame::InferenceState) + toplevel = !isa(frame.linfo.def, Method) + if !toplevel && frame.cached && frame.max_valid == typemax(UInt) + caller = frame.linfo + for edges in frame.stmt_edges + i = 1 + while i <= length(edges) + to = edges[i] + if isa(to, MethodInstance) + ccall(:jl_method_instance_add_backedge, Cvoid, (Any, Any), to, caller) + i += 1 + else + typeassert(to, MethodTable) + typ = edges[i + 1] + ccall(:jl_method_table_add_backedge, Cvoid, (Any, Any, Any), to, typ, caller) + i += 2 + end + end + end + end +end + +# at the end, all items in b's cycle +# will now be added to a's cycle +function union_caller_cycle!(a::InferenceState, b::InferenceState) + callers_in_cycle = b.callers_in_cycle + b.parent = a.parent + b.callers_in_cycle = a.callers_in_cycle + contains_is(a.callers_in_cycle, b) || push!(a.callers_in_cycle, b) + if callers_in_cycle !== a.callers_in_cycle + for caller in callers_in_cycle + if caller !== b + caller.parent = a.parent + caller.callers_in_cycle = a.callers_in_cycle + push!(a.callers_in_cycle, caller) + end + end + end + return +end + +function merge_call_chain!(parent::InferenceState, ancestor::InferenceState, child::InferenceState) + # add backedge of parent <- child + # then add all backedges of parent <- parent.parent + # and merge all of the callers into ancestor.callers_in_cycle + # and ensure that walking the parent list will get the same result (DAG) from everywhere + while true + add_backedge!(child, parent, parent.currpc) + union_caller_cycle!(ancestor, child) + child = parent + parent = child.parent + child === ancestor && break + end +end + +# Walk through `linfo`'s upstream call chain, starting at `parent`. If a parent +# frame matching `linfo` is encountered, then there is a cycle in the call graph +# (i.e. `linfo` is a descendant callee of itself). Upon encountering this cycle, +# we "resolve" it by merging the call chain, which entails unioning each intermediary +# frame's `callers_in_cycle` field and adding the appropriate backedges. Finally, +# we return `linfo`'s pre-existing frame. If no cycles are found, `nothing` is +# returned instead. +function resolve_call_cycle!(linfo::MethodInstance, parent::InferenceState) + frame = parent + uncached = false + while isa(frame, InferenceState) + uncached |= !frame.cached # ensure we never add an uncached frame to a cycle + if frame.linfo === linfo + uncached && return true + merge_call_chain!(parent, frame, frame) + return frame + end + for caller in frame.callers_in_cycle + if caller.linfo === linfo + uncached && return true + merge_call_chain!(parent, frame, caller) + return caller + end + end + frame = frame.parent + end + return false +end + +# build (and start inferring) the inference frame for the linfo +function typeinf_frame(linfo::MethodInstance, + optimize::Bool, cached::Bool, params::Params) + frame = InferenceState(linfo, optimize, cached, params) + frame === nothing && return nothing + cached && (linfo.inInference = true) + typeinf(frame) + return frame +end + +# compute (and cache) an inferred AST and return the current best estimate of the result type +function typeinf_edge(method::Method, @nospecialize(atypes), sparams::SimpleVector, caller::InferenceState) + code = code_for_method(method, atypes, sparams, caller.params.world) + code === nothing && return Any, nothing + code = code::MethodInstance + if isdefined(code, :inferred) + # return rettype if the code is already inferred + # staged functions make this hard since they have two "inferred" conditions, + # so need to check whether the code itself is also inferred + inf = code.inferred + if !isa(inf, CodeInfo) || (inf::CodeInfo).inferred + if isdefined(code, :inferred_const) + return AbstractEvalConstant(code.inferred_const), code + else + return code.rettype, code + end + end + end + if !caller.cached && caller.parent === nothing + # this caller exists to return to the user + # (if we asked resolve_call_cyle, it might instead detect that there is a cycle that it can't merge) + frame = false + else + frame = resolve_call_cycle!(code, caller) + end + if frame === false + # completely new + code.inInference = true + frame = InferenceState(code, #=optimize=#true, #=cached=#true, caller.params) # always optimize and cache edge targets + if frame === nothing + # can't get the source for this, so we know nothing + code.inInference = false + return Any, nothing + end + if caller.cached # don't involve uncached functions in cycle resolution + frame.parent = caller + end + typeinf(frame) + return frame.bestguess, frame.inferred ? frame.linfo : nothing + elseif frame === true + # unresolvable cycle + return Any, nothing + end + frame = frame::InferenceState + return frame.bestguess, nothing +end + + +#### entry points for inferring a MethodInstance given a type signature #### + +# compute an inferred AST and return type +function typeinf_code(method::Method, @nospecialize(atypes), sparams::SimpleVector, + optimize::Bool, cached::Bool, params::Params) + code = code_for_method(method, atypes, sparams, params.world) + code === nothing && return (nothing, nothing, Any) + return typeinf_code(code::MethodInstance, optimize, cached, params) +end +function typeinf_code(linfo::MethodInstance, optimize::Bool, cached::Bool, + params::Params) + for i = 1:2 # test-and-lock-and-test + i == 2 && ccall(:jl_typeinf_begin, Cvoid, ()) + if cached && isdefined(linfo, :inferred) + # see if this code already exists in the cache + # staged functions make this hard since they have two "inferred" conditions, + # so need to check whether the code itself is also inferred + if min_world(linfo) <= params.world <= max_world(linfo) + inf = linfo.inferred + if linfo.jlcall_api == 2 + method = linfo.def::Method + tree = ccall(:jl_new_code_info_uninit, Ref{CodeInfo}, ()) + tree.code = Any[ Expr(:return, quoted(linfo.inferred_const)) ] + tree.signature_for_inference_heuristics = nothing + tree.slotnames = Any[ COMPILER_TEMP_SYM for i = 1:method.nargs ] + tree.slotflags = UInt8[ 0 for i = 1:method.nargs ] + tree.slottypes = nothing + tree.ssavaluetypes = 0 + tree.inferred = true + tree.pure = true + tree.inlineable = true + i == 2 && ccall(:jl_typeinf_end, Cvoid, ()) + return svec(linfo, tree, linfo.rettype) + elseif isa(inf, CodeInfo) + if inf.inferred + i == 2 && ccall(:jl_typeinf_end, Cvoid, ()) + return svec(linfo, inf, linfo.rettype) + end + end + end + end + end + frame = typeinf_frame(linfo, optimize, cached, params) + ccall(:jl_typeinf_end, Cvoid, ()) + frame === nothing && return svec(nothing, nothing, Any) + frame = frame::InferenceState + frame.inferred || return svec(nothing, nothing, Any) + frame.cached || return svec(nothing, frame.src, widenconst(frame.bestguess)) + return svec(frame.linfo, frame.src, widenconst(frame.bestguess)) +end + +# compute (and cache) an inferred AST and return the inferred return type +function typeinf_type(method::Method, @nospecialize(atypes), sparams::SimpleVector, + cached::Bool, params::Params) + if contains_is(unwrap_unionall(atypes).parameters, Union{}) + return Union{} + end + code = code_for_method(method, atypes, sparams, params.world) + code === nothing && return nothing + code = code::MethodInstance + for i = 1:2 # test-and-lock-and-test + i == 2 && ccall(:jl_typeinf_begin, Cvoid, ()) + if cached && isdefined(code, :inferred) + # see if this rettype already exists in the cache + # staged functions make this hard since they have two "inferred" conditions, + # so need to check whether the code itself is also inferred + inf = code.inferred + if !isa(inf, CodeInfo) || (inf::CodeInfo).inferred + i == 2 && ccall(:jl_typeinf_end, Cvoid, ()) + return code.rettype + end + end + end + frame = typeinf_frame(code, cached, cached, params) + ccall(:jl_typeinf_end, Cvoid, ()) + frame === nothing && return nothing + frame = frame::InferenceState + frame.inferred || return nothing + return widenconst(frame.bestguess) +end + +function typeinf_ext(linfo::MethodInstance, world::UInt) + if isa(linfo.def, Method) + # method lambda - infer this specialization via the method cache + return typeinf_code(linfo, true, true, Params(world)) + else + # toplevel lambda - infer directly + ccall(:jl_typeinf_begin, Cvoid, ()) + result = InferenceResult(linfo) + frame = InferenceState(result, linfo.inferred::CodeInfo, + true, true, Params(world)) + typeinf(frame) + ccall(:jl_typeinf_end, Cvoid, ()) + @assert frame.inferred # TODO: deal with this better + @assert frame.linfo === linfo + linfo.rettype = widenconst(frame.bestguess) + return svec(linfo, frame.src, linfo.rettype) + end +end + +#### do the work of inference #### + +function typeinf_work(frame::InferenceState) + @assert !frame.inferred + frame.dont_work_on_me = true # mark that this function is currently on the stack + W = frame.ip + s = frame.stmt_types + n = frame.nstmts + while frame.pc´´ <= n + # make progress on the active ip set + local pc::Int = frame.pc´´ # current program-counter + while true # inner loop optimizes the common case where it can run straight from pc to pc + 1 + #print(pc,": ",s[pc],"\n") + local pc´::Int = pc + 1 # next program-counter (after executing instruction) + if pc == frame.pc´´ + # need to update pc´´ to point at the new lowest instruction in W + min_pc = next(W, pc)[2] + if done(W, min_pc) + frame.pc´´ = max(min_pc, n + 1) + else + frame.pc´´ = min_pc + end + end + delete!(W, pc) + frame.currpc = pc + frame.cur_hand = frame.handler_at[pc] + frame.stmt_edges[pc] === () || empty!(frame.stmt_edges[pc]) + stmt = frame.src.code[pc] + changes = abstract_interpret(stmt, s[pc]::VarTable, frame) + if changes === () + break # this line threw an error and so there is no need to continue + # changes = s[pc] + end + if frame.cur_hand !== () && isa(changes, StateUpdate) + # propagate new type info to exception handler + # the handling for Expr(:enter) propagates all changes from before the try/catch + # so this only needs to propagate any changes + l = frame.cur_hand[1] + if stupdate1!(s[l]::VarTable, changes::StateUpdate) !== false + if l < frame.pc´´ + frame.pc´´ = l + end + push!(W, l) + end + end + if isa(changes, StateUpdate) + changes_var = changes.var + if isa(changes_var, SSAValue) + # directly forward changes to an SSAValue to the applicable line + record_ssa_assign(changes_var.id + 1, changes.vtype.typ, frame) + end + elseif isa(stmt, NewvarNode) + sn = slot_id(stmt.slot) + changes = changes::VarTable + changes[sn] = VarState(Bottom, true) + elseif isa(stmt, GotoNode) + pc´ = (stmt::GotoNode).label + elseif isa(stmt, Expr) + stmt = stmt::Expr + hd = stmt.head + if hd === :gotoifnot + condt = abstract_eval(stmt.args[1], s[pc], frame) + condval = isa(condt, Const) ? condt.val : nothing + l = stmt.args[2]::Int + changes = changes::VarTable + # constant conditions + if condval === true + elseif condval === false + pc´ = l + else + # general case + frame.handler_at[l] = frame.cur_hand + if isa(condt, Conditional) + changes_else = StateUpdate(condt.var, VarState(condt.elsetype, false), changes) + changes = StateUpdate(condt.var, VarState(condt.vtype, false), changes) + else + changes_else = changes + end + newstate_else = stupdate!(s[l], changes_else) + if newstate_else !== false + # add else branch to active IP list + if l < frame.pc´´ + frame.pc´´ = l + end + push!(W, l) + s[l] = newstate_else + end + end + elseif hd === :return + pc´ = n + 1 + rt = abstract_eval(stmt.args[1], s[pc], frame) + if !isa(rt, Const) && !isa(rt, Type) + # only propagate information we know we can store + # and is valid inter-procedurally + rt = widenconst(rt) + end + if tchanged(rt, frame.bestguess) + # new (wider) return type for frame + frame.bestguess = tmerge(frame.bestguess, rt) + for (caller, caller_pc) in frame.backedges + # notify backedges of updated type information + if caller.stmt_types[caller_pc] !== () + if caller_pc < caller.pc´´ + caller.pc´´ = caller_pc + end + push!(caller.ip, caller_pc) + end + end + end + elseif hd === :enter + l = stmt.args[1]::Int + frame.cur_hand = (l, frame.cur_hand) + # propagate type info to exception handler + l = frame.cur_hand[1] + old = s[l] + new = s[pc]::Array{Any,1} + newstate_catch = stupdate!(old, new) + if newstate_catch !== false + if l < frame.pc´´ + frame.pc´´ = l + end + push!(W, l) + s[l] = newstate_catch + end + typeassert(s[l], VarTable) + frame.handler_at[l] = frame.cur_hand + elseif hd === :leave + for i = 1:((stmt.args[1])::Int) + frame.cur_hand = frame.cur_hand[2] + end + end + end + pc´ > n && break # can't proceed with the fast-path fall-through + frame.handler_at[pc´] = frame.cur_hand + newstate = stupdate!(s[pc´], changes) + if isa(stmt, GotoNode) && frame.pc´´ < pc´ + # if we are processing a goto node anyways, + # (such as a terminator for a loop, if-else, or try block), + # consider whether we should jump to an older backedge first, + # to try to traverse the statements in approximate dominator order + if newstate !== false + s[pc´] = newstate + end + push!(W, pc´) + pc = frame.pc´´ + elseif newstate !== false + s[pc´] = newstate + pc = pc´ + elseif pc´ in W + pc = pc´ + else + break + end + end + end + frame.dont_work_on_me = false +end + +function typeinf(frame::InferenceState) + typeinf_work(frame) + + # If the current frame is part of a cycle, solve the cycle before finishing + no_active_ips_in_callers = false + while !no_active_ips_in_callers + no_active_ips_in_callers = true + for caller in frame.callers_in_cycle + caller.dont_work_on_me && return + if caller.pc´´ <= caller.nstmts # equivalent to `isempty(caller.ip)` + # Note that `typeinf_work(caller)` can potentially modify the other frames + # `frame.callers_in_cycle`, which is why making incremental progress requires the + # outer while loop. + typeinf_work(caller) + no_active_ips_in_callers = false + end + if caller.min_valid < frame.min_valid + caller.min_valid = frame.min_valid + end + if caller.max_valid > frame.max_valid + caller.max_valid = frame.max_valid + end + end + end + + # with no active ip's, type inference on frame is done + + if isempty(frame.callers_in_cycle) + @assert !(frame.dont_work_on_me) + frame.dont_work_on_me = true + optimize(frame) + finish(frame) + finalize_backedges(frame) + else # frame is in frame.callers_in_cycle + for caller in frame.callers_in_cycle + @assert !(caller.dont_work_on_me) + caller.dont_work_on_me = true + end + # complete the computation of the src optimizations + for caller in frame.callers_in_cycle + optimize(caller) + if frame.min_valid < caller.min_valid + frame.min_valid = caller.min_valid + end + if frame.max_valid > caller.max_valid + frame.max_valid = caller.max_valid + end + end + # update and store in the global cache + for caller in frame.callers_in_cycle + caller.min_valid = frame.min_valid + end + for caller in frame.callers_in_cycle + finish(caller) + end + for caller in frame.callers_in_cycle + finalize_backedges(caller) + end + end + + nothing +end diff --git a/base/compiler/typelattice.jl b/base/compiler/typelattice.jl new file mode 100644 index 0000000000000..ce167ece68b78 --- /dev/null +++ b/base/compiler/typelattice.jl @@ -0,0 +1,266 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +##################### +# structs/constants # +##################### + + +# The type of a value might be constant +struct Const + val + actual::Bool # if true, we obtained `val` by actually calling a @pure function + Const(@nospecialize(v)) = new(v, false) + Const(@nospecialize(v), a::Bool) = new(v, a) +end + +# The type of this value might be Bool. +# However, to enable a limited amount of back-propagagation, +# we also keep some information about how this Bool value was created. +# In particular, if you branch on this value, then may assume that in +# the true branch, the type of `var` will be limited by `vtype` and in +# the false branch, it will be limited by `elsetype`. Example: +# ``` +# cond = isa(x::Union{Int, Float}, Int)::Conditional(x, Int, Float) +# if cond +# # May assume x is `Int` now +# else +# # May assume x is `Float` now +# end +# ``` +mutable struct Conditional + var::Union{Slot,SSAValue} + vtype + elsetype + function Conditional( + @nospecialize(var), + @nospecialize(vtype), + @nospecialize(nottype)) + return new(var, vtype, nottype) + end +end + +struct PartialTypeVar + tv::TypeVar + # N.B.: Currently unused, but would allow turning something back + # into Const, if the bounds are pulled out of this TypeVar + lb_certain::Bool + ub_certain::Bool + PartialTypeVar(tv::TypeVar, lb_certain::Bool, ub_certain::Bool) = new(tv, lb_certain, ub_certain) +end + +# The type of a variable load is either a value or an UndefVarError +mutable struct VarState + typ + undef::Bool + VarState(@nospecialize(typ), undef::Bool) = new(typ, undef) +end + +const VarTable = Array{Any,1} + +mutable struct StateUpdate + var::Union{Slot,SSAValue} + vtype::VarState + state::VarTable +end + +struct NotFound end + +const NOT_FOUND = NotFound() + +##################### +# lattice utilities # +##################### + +function rewrap(@nospecialize(t), @nospecialize(u)) + isa(t, Const) && return t + isa(t, Conditional) && return t + return rewrap_unionall(t, u) +end + +_typename(a) = Union{} +_typename(a::Vararg) = Any +_typename(a::TypeVar) = Any +function _typename(a::Union) + ta = _typename(a.a) + tb = _typename(a.b) + ta === tb ? tb : (ta === Any || tb === Any) ? Any : Union{} +end +_typename(union::UnionAll) = _typename(union.body) + +_typename(a::DataType) = Const(a.name) + +# N.B.: typename maps type equivalence classes to a single value +typename_static(@nospecialize(t)) = isType(t) ? _typename(t.parameters[1]) : Any +typename_static(t::Const) = _typename(t.val) + +################# +# lattice logic # +################# + +function issubconditional(a::Conditional, b::Conditional) + avar = a.var + bvar = b.var + if (isa(avar, Slot) && isa(bvar, Slot) && slot_id(avar) === slot_id(bvar)) || + (isa(avar, SSAValue) && isa(bvar, SSAValue) && avar === bvar) + if a.vtype ⊑ b.vtype + if a.elsetype ⊑ b.elsetype + return true + end + end + end + return false +end + +function ⊑(@nospecialize(a), @nospecialize(b)) + (a === NOT_FOUND || b === Any) && return true + (a === Any || b === NOT_FOUND) && return false + a === Union{} && return true + b === Union{} && return false + if isa(a, Conditional) + if isa(b, Conditional) + return issubconditional(a, b) + end + a = Bool + elseif isa(b, Conditional) + return a === Bottom + end + if isa(a, Const) + if isa(b, Const) + return a.val === b.val + end + return isa(a.val, widenconst(b)) + elseif isa(b, Const) + return a === Bottom + elseif !(isa(a, Type) || isa(a, TypeVar)) || + !(isa(b, Type) || isa(b, TypeVar)) + return a === b + else + return a <: b + end +end + +widenconst(c::Conditional) = Bool +function widenconst(c::Const) + if isa(c.val, Type) + if isvarargtype(c.val) + return Type + end + return Type{c.val} + else + return typeof(c.val) + end +end +widenconst(c::PartialTypeVar) = TypeVar +widenconst(@nospecialize(t)) = t + +issubstate(a::VarState, b::VarState) = (a.typ ⊑ b.typ && a.undef <= b.undef) + +function tmerge(@nospecialize(typea), @nospecialize(typeb)) + typea ⊑ typeb && return typeb + typeb ⊑ typea && return typea + if isa(typea, Conditional) && isa(typeb, Conditional) + if typea.var === typeb.var + vtype = tmerge(typea.vtype, typeb.vtype) + elsetype = tmerge(typea.elsetype, typeb.elsetype) + if vtype != elsetype + return Conditional(typea.var, vtype, elsetype) + end + end + return Bool + end + typea, typeb = widenconst(typea), widenconst(typeb) + typea === typeb && return typea + if !(isa(typea,Type) || isa(typea,TypeVar)) || !(isa(typeb,Type) || isa(typeb,TypeVar)) + return Any + end + if (typea <: Tuple) && (typeb <: Tuple) + if isa(typea, DataType) && isa(typeb, DataType) && length(typea.parameters) == length(typeb.parameters) && !isvatuple(typea) && !isvatuple(typeb) + return typejoin(typea, typeb) + end + if isa(typea, Union) || isa(typeb, Union) || (isa(typea,DataType) && length(typea.parameters)>3) || + (isa(typeb,DataType) && length(typeb.parameters)>3) + # widen tuples faster (see #6704), but not too much, to make sure we can infer + # e.g. (t::Union{Tuple{Bool},Tuple{Bool,Int}})[1] + return Tuple + end + end + u = Union{typea, typeb} + if unionlen(u) > MAX_TYPEUNION_LEN || type_too_complex(u, MAX_TYPE_DEPTH) + # don't let type unions get too big + # TODO: something smarter, like a common supertype + return Any + end + return u +end + +function smerge(sa::Union{NotFound,VarState}, sb::Union{NotFound,VarState}) + sa === sb && return sa + sa === NOT_FOUND && return sb + sb === NOT_FOUND && return sa + issubstate(sa, sb) && return sb + issubstate(sb, sa) && return sa + return VarState(tmerge(sa.typ, sb.typ), sa.undef | sb.undef) +end + +@inline tchanged(@nospecialize(n), @nospecialize(o)) = o === NOT_FOUND || (n !== NOT_FOUND && !(n ⊑ o)) +@inline schanged(@nospecialize(n), @nospecialize(o)) = (n !== o) && (o === NOT_FOUND || (n !== NOT_FOUND && !issubstate(n, o))) + +function stupdate!(state::Tuple{}, changes::StateUpdate) + newst = copy(changes.state) + if isa(changes.var, Slot) + newst[slot_id(changes.var::Slot)] = changes.vtype + end + return newst +end + +function stupdate!(state::VarTable, change::StateUpdate) + if !isa(change.var, Slot) + return stupdate!(state, change.state) + end + newstate = false + changeid = slot_id(change.var::Slot) + for i = 1:length(state) + if i == changeid + newtype = change.vtype + else + newtype = change.state[i] + end + oldtype = state[i] + if schanged(newtype, oldtype) + newstate = state + state[i] = smerge(oldtype, newtype) + end + end + return newstate +end + +function stupdate!(state::VarTable, changes::VarTable) + newstate = false + for i = 1:length(state) + newtype = changes[i] + oldtype = state[i] + if schanged(newtype, oldtype) + newstate = state + state[i] = smerge(oldtype, newtype) + end + end + return newstate +end + +stupdate!(state::Tuple{}, changes::VarTable) = copy(changes) + +stupdate!(state::Tuple{}, changes::Tuple{}) = false + +function stupdate1!(state::VarTable, change::StateUpdate) + if !isa(change.var, Slot) + return false + end + i = slot_id(change.var::Slot) + newtype = change.vtype + oldtype = state[i] + if schanged(newtype, oldtype) + state[i] = smerge(oldtype, newtype) + return true + end + return false +end diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl new file mode 100644 index 0000000000000..4930445cb59d7 --- /dev/null +++ b/base/compiler/typelimits.jl @@ -0,0 +1,369 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +######################### +# limitation parameters # +######################### + +const MAX_TYPEUNION_LEN = 3 +const MAX_TYPE_DEPTH = 8 +const MAX_INLINE_CONST_SIZE = 256 +const TUPLE_COMPLEXITY_LIMIT_DEPTH = 3 + +######################### +# limitation heuristics # +######################### + +function limit_type_depth(@nospecialize(t), d::Int) + r = limit_type_depth(t, d, true, TypeVar[]) + @assert !isa(t, Type) || t <: r + return r +end + +limit_tuple_depth(params::Params, @nospecialize(t)) = limit_tuple_depth_(params,t,0) + +function limit_tuple_depth_(params::Params, @nospecialize(t), d::Int) + if isa(t,Union) + # also limit within Union types. + # may have to recur into other stuff in the future too. + return Union{limit_tuple_depth_(params, t.a, d+1), + limit_tuple_depth_(params, t.b, d+1)} + elseif isa(t,UnionAll) + ub = limit_tuple_depth_(params, t.var.ub, d) + if ub !== t.var.ub + var = TypeVar(t.var.name, t.var.lb, ub) + body = t{var} + else + var = t.var + body = t.body + end + body = limit_tuple_depth_(params, body, d) + return UnionAll(var, body) + elseif !(isa(t,DataType) && t.name === Tuple.name) + return t + elseif d > params.MAX_TUPLE_DEPTH + return Tuple + end + p = map(x->limit_tuple_depth_(params,x,d+1), t.parameters) + Tuple{p...} +end + +limit_tuple_type = (@nospecialize(t), params::Params) -> limit_tuple_type_n(t, params.MAX_TUPLETYPE_LEN) + +function limit_tuple_type_n(@nospecialize(t), lim::Int) + if isa(t,UnionAll) + return UnionAll(t.var, limit_tuple_type_n(t.body, lim)) + end + p = t.parameters + n = length(p) + if n > lim + tail = reduce(typejoin, Bottom, Any[p[lim:(n-1)]..., unwrapva(p[n])]) + return Tuple{p[1:(lim-1)]..., Vararg{tail}} + end + return t +end + +function limit_type_depth(@nospecialize(t), d::Int, cov::Bool, vars::Vector{TypeVar}=TypeVar[]) + if isa(t, Union) + if d < 0 + if cov + return Any + else + var = TypeVar(:_) + push!(vars, var) + return var + end + end + return Union{limit_type_depth(t.a, d - 1, cov, vars), + limit_type_depth(t.b, d - 1, cov, vars)} + elseif isa(t, UnionAll) + v = t.var + if v.ub === Any + if v.lb === Bottom + return UnionAll(t.var, limit_type_depth(t.body, d, cov, vars)) + end + ub = Any + else + ub = limit_type_depth(v.ub, d - 1, cov, vars) + end + if v.lb === Bottom || type_depth(v.lb) > d + # note: lower bounds need to be widened by making them lower + lb = Bottom + else + lb = v.lb + end + v2 = TypeVar(v.name, lb, ub) + return UnionAll(v2, limit_type_depth(t{v2}, d, cov, vars)) + elseif !isa(t,DataType) + return t + end + P = t.parameters + isempty(P) && return t + if d < 0 + if isvarargtype(t) + # never replace Vararg with non-Vararg + # passing depth=0 avoids putting a bare typevar here, for the diagonal rule + return Vararg{limit_type_depth(P[1], 0, cov, vars), P[2]} + end + widert = t.name.wrapper + if !(t <: widert) + # This can happen when a typevar has bounds too wide for its context, e.g. + # `Complex{T} where T` is not a subtype of `Complex`. In that case widen even + # faster to something safe to ensure the result is a supertype of the input. + widert = Any + end + cov && return widert + var = TypeVar(:_, widert) + push!(vars, var) + return var + end + stillcov = cov && (t.name === Tuple.name) + newdepth = d - 1 + if isvarargtype(t) + newdepth = max(newdepth, 0) + end + Q = map(x -> limit_type_depth(x, newdepth, stillcov, vars), P) + R = t.name.wrapper{Q...} + if cov && !stillcov + for var in vars + R = UnionAll(var, R) + end + end + return R +end + +# limit the complexity of type `t` to be simpler than the comparison type `compare` +# no new values may be introduced, so the parameter `source` encodes the set of all values already present +# the outermost tuple type is permitted to have up to `allowed_tuplelen` parameters +function limit_type_size(@nospecialize(t), @nospecialize(compare), @nospecialize(source), allowed_tuplelen::Int) + source = svec(unwrap_unionall(compare), unwrap_unionall(source)) + source[1] === source[2] && (source = svec(source[1])) + type_more_complex(t, compare, source, 1, TUPLE_COMPLEXITY_LIMIT_DEPTH, allowed_tuplelen) || return t + r = _limit_type_size(t, compare, source, 1, allowed_tuplelen) + @assert t <: r + #@assert r === _limit_type_size(r, t, source) # this monotonicity constraint is slightly stronger than actually required, + # since we only actually need to demonstrate that repeated application would reaches a fixed point, + #not that it is already at the fixed point + return r +end + +# type vs. comparison or which was derived from source +function _limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVector, depth::Int, allowed_tuplelen::Int) + if t === c + return t # quick egal test + elseif t === Union{} + return t # easy case + elseif isa(t, DataType) && isempty(t.parameters) + return t # fast path: unparameterized are always simple + elseif isa(unwrap_unionall(t), DataType) && isa(c, Type) && c !== Union{} && c <: t + return t # t is already wider than the comparison in the type lattice + elseif is_derived_type_from_any(unwrap_unionall(t), sources, depth) + return t # t isn't something new + end + if isa(t, TypeVar) + if isa(c, TypeVar) + if t.ub === c.ub && t.lb === c.lb + return t + end + end + elseif isa(t, Union) + if isa(c, Union) + a = _limit_type_size(t.a, c.a, sources, depth, allowed_tuplelen) + b = _limit_type_size(t.b, c.b, sources, depth, allowed_tuplelen) + return Union{a, b} + end + elseif isa(t, UnionAll) + if isa(c, UnionAll) + tv = t.var + cv = c.var + if tv.ub === cv.ub + if tv.lb === cv.lb + return UnionAll(tv, _limit_type_size(t.body, c.body, sources, depth + 1, allowed_tuplelen)) + end + ub = tv.ub + else + ub = _limit_type_size(tv.ub, cv.ub, sources, depth + 1, 0) + end + if tv.lb === cv.lb + lb = tv.lb + else + # note: lower bounds need to be widened by making them lower + lb = Bottom + end + v2 = TypeVar(tv.name, lb, ub) + return UnionAll(v2, _limit_type_size(t{v2}, c{v2}, sources, depth + 1, allowed_tuplelen)) + end + tbody = _limit_type_size(t.body, c, sources, depth + 1, allowed_tuplelen) + tbody === t.body && return t + return UnionAll(t.var, tbody) + elseif isa(c, UnionAll) + # peel off non-matching wrapper of comparison + return _limit_type_size(t, c.body, sources, depth, allowed_tuplelen) + elseif isa(t, DataType) + if isa(c, DataType) + tP = t.parameters + cP = c.parameters + if t.name === c.name && !isempty(cP) + if isvarargtype(t) + VaT = _limit_type_size(tP[1], cP[1], sources, depth + 1, 0) + N = tP[2] + if isa(N, TypeVar) || N === cP[2] + return Vararg{VaT, N} + end + return Vararg{VaT} + elseif t.name === Tuple.name + # for covariant datatypes (Tuple), + # apply type-size limit element-wise + ltP = length(tP) + lcP = length(cP) + np = min(ltP, max(lcP, allowed_tuplelen)) + Q = Any[ tP[i] for i in 1:np ] + if ltP > np + # combine tp[np:end] into tP[np] using Vararg + Q[np] = tuple_tail_elem(Bottom, Any[ tP[i] for i in np:ltP ]) + end + for i = 1:np + # now apply limit element-wise to Q + # padding out the comparison as needed to allowed_tuplelen elements + if i <= lcP + cPi = cP[i] + elseif isvarargtype(cP[lcP]) + cPi = cP[lcP] + else + cPi = Any + end + Q[i] = _limit_type_size(Q[i], cPi, sources, depth + 1, 0) + end + return Tuple{Q...} + end + elseif isvarargtype(c) + # Tuple{Vararg{T}} --> Tuple{T} is OK + return _limit_type_size(t, cP[1], sources, depth, 0) + end + end + if isType(t) # allow taking typeof as Type{...}, but ensure it doesn't start nesting + tt = unwrap_unionall(t.parameters[1]) + if isa(tt, DataType) && !isType(tt) + is_derived_type_from_any(tt, sources, depth) && return t + end + end + if isvarargtype(t) + # never replace Vararg with non-Vararg + return Vararg + end + widert = t.name.wrapper + if !(t <: widert) + # This can happen when a typevar has bounds too wide for its context, e.g. + # `Complex{T} where T` is not a subtype of `Complex`. In that case widen even + # faster to something safe to ensure the result is a supertype of the input. + return Any + end + return widert + end + return Any +end + +function type_more_complex(@nospecialize(t), @nospecialize(c), sources::SimpleVector, depth::Int, tupledepth::Int, allowed_tuplelen::Int) + # detect cases where the comparison is trivial + if t === c + return false + elseif t === Union{} + return false # Bottom is as simple as they come + elseif isa(t, DataType) && isempty(t.parameters) + return false # fastpath: unparameterized types are always finite + elseif tupledepth > 0 && isa(unwrap_unionall(t), DataType) && isa(c, Type) && c !== Union{} && c <: t + return false # t is already wider than the comparison in the type lattice + elseif tupledepth > 0 && is_derived_type_from_any(unwrap_unionall(t), sources, depth) + return false # t isn't something new + end + # peel off wrappers + if isa(c, UnionAll) + # allow wrapping type with fewer UnionAlls than comparison if in a covariant context + if !isa(t, UnionAll) && tupledepth == 0 + return true + end + t = unwrap_unionall(t) + c = unwrap_unionall(c) + end + # rules for various comparison types + if isa(c, TypeVar) + tupledepth = 1 # allow replacing a TypeVar with a concrete value (since we know the UnionAll must be in covariant position) + if isa(t, TypeVar) + return !(t.lb === Union{} || t.lb === c.lb) || # simplify lb towards Union{} + type_more_complex(t.ub, c.ub, sources, depth + 1, tupledepth, 0) + end + c.lb === Union{} || return true + return type_more_complex(t, c.ub, sources, depth, tupledepth, 0) + elseif isa(c, Union) + if isa(t, Union) + return type_more_complex(t.a, c.a, sources, depth, tupledepth, allowed_tuplelen) || + type_more_complex(t.b, c.b, sources, depth, tupledepth, allowed_tuplelen) + end + return type_more_complex(t, c.a, sources, depth, tupledepth, allowed_tuplelen) && + type_more_complex(t, c.b, sources, depth, tupledepth, allowed_tuplelen) + elseif isa(t, Int) && isa(c, Int) + return t !== 1 # alternatively, could use !(0 <= t < c) + end + # base case for data types + if isa(t, DataType) + tP = t.parameters + if isa(c, DataType) && t.name === c.name + cP = c.parameters + length(cP) < length(tP) && return true + ntail = length(cP) - length(tP) # assume parameters were dropped from the tuple head + # allow creating variation within a nested tuple, but only so deep + if t.name === Tuple.name && tupledepth > 0 + tupledepth -= 1 + elseif !isvarargtype(t) + tupledepth = 0 + end + isgenerator = (t.name.name === :Generator && t.name.module === _topmod(t.name.module)) + for i = 1:length(tP) + tPi = tP[i] + cPi = cP[i + ntail] + if isgenerator + let tPi = unwrap_unionall(tPi), + cPi = unwrap_unionall(cPi) + if isa(tPi, DataType) && isa(cPi, DataType) && + !tPi.abstract && !cPi.abstract && + sym_isless(cPi.name.name, tPi.name.name) + # allow collect on (anonymous) Generators to nest, provided that their functions are appropriately ordered + # TODO: is there a better way? + continue + end + end + end + type_more_complex(tPi, cPi, sources, depth + 1, tupledepth, 0) && return true + end + return false + elseif isvarargtype(c) + return type_more_complex(t, unwrapva(c), sources, depth, tupledepth, 0) + end + if isType(t) # allow taking typeof any source type anywhere as Type{...}, as long as it isn't nesting Type{Type{...}} + tt = unwrap_unionall(t.parameters[1]) + if isa(tt, DataType) && !isType(tt) + is_derived_type_from_any(tt, sources, depth) || return true + return false + end + end + end + return true +end + +function type_too_complex(@nospecialize(t), d::Int) + if d < 0 + return true + elseif isa(t, Union) + return type_too_complex(t.a, d - 1) || type_too_complex(t.b, d - 1) + elseif isa(t, TypeVar) + return type_too_complex(t.lb, d - 1) || type_too_complex(t.ub, d - 1) + elseif isa(t, UnionAll) + return type_too_complex(t.var, d) || type_too_complex(t.body, d) + elseif isa(t, DataType) + for x in (t.parameters)::SimpleVector + if type_too_complex(x, d - 1) + return true + end + end + end + return false +end diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl new file mode 100644 index 0000000000000..9e82535777786 --- /dev/null +++ b/base/compiler/typeutils.jl @@ -0,0 +1,171 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +const _TYPE_NAME = Type.body.name + +isType(@nospecialize t) = isa(t, DataType) && (t::DataType).name === _TYPE_NAME + +# true if Type is inlineable as constant (is a singleton) +function isconstType(@nospecialize t) + isType(t) || return false + p1 = t.parameters[1] + # typeof(Bottom) is special since even though it is as leaftype, + # at runtime, it might be Type{Union{}} instead, so don't attempt inference of it + p1 === typeof(Union{}) && return false + p1 === Union{} && return true + isleaftype(p1) && return true + return false +end + +iskindtype(@nospecialize t) = (t === DataType || t === UnionAll || t === Union || t === typeof(Bottom)) + +argtypes_to_type(argtypes::Array{Any,1}) = Tuple{anymap(widenconst, argtypes)...} + +isknownlength(t::DataType) = !isvatuple(t) || (length(t.parameters) > 0 && isa(unwrap_unionall(t.parameters[end]).parameters[2],Int)) + +function type_depth(@nospecialize(t)) + if t === Bottom + return 0 + elseif isa(t, Union) + return max(type_depth(t.a), type_depth(t.b)) + 1 + elseif isa(t, DataType) + return (t::DataType).depth + elseif isa(t, UnionAll) + if t.var.ub === Any && t.var.lb === Bottom + return type_depth(t.body) + end + return max(type_depth(t.var.ub) + 1, type_depth(t.var.lb) + 1, type_depth(t.body)) + end + return 0 +end + +# try to find `type` somewhere in `comparison` type +# at a minimum nesting depth of `mindepth` +function is_derived_type(@nospecialize(t), @nospecialize(c), mindepth::Int) + if mindepth > 0 + mindepth -= 1 + end + if t === c + return mindepth == 0 + end + if isa(c, TypeVar) + # see if it is replacing a TypeVar upper bound with something simpler + return is_derived_type(t, c.ub, mindepth) + elseif isa(c, Union) + # see if it is one of the elements of the union + return is_derived_type(t, c.a, mindepth + 1) || is_derived_type(t, c.b, mindepth + 1) + elseif isa(c, UnionAll) + # see if it is derived from the body + return is_derived_type(t, c.body, mindepth) + elseif isa(c, DataType) + if isa(t, DataType) + # see if it is one of the supertypes of a parameter + super = supertype(c) + while super !== Any + t === super && return true + super = supertype(super) + end + end + # see if it was extracted from a type parameter + cP = c.parameters + for p in cP + is_derived_type(t, p, mindepth) && return true + end + if isleaftype(c) && isbits(c) + # see if it was extracted from a fieldtype + # however, only look through types that can be inlined + # to ensure monotonicity of derivation + # since we know that for immutable types, + # the field types must have been constructed prior to the type, + # it cannot have a reference cycle in the type graph + cF = c.types + for f in cF + is_derived_type(t, f, mindepth) && return true + end + end + end + return false +end + +function is_derived_type_from_any(@nospecialize(t), sources::SimpleVector, mindepth::Int) + for s in sources + is_derived_type(t, s, mindepth) && return true + end + return false +end + +function valid_tparam(@nospecialize(x)) + if isa(x,Tuple) + for t in x + !valid_tparam(t) && return false + end + return true + end + return isa(x,Int) || isa(x,Symbol) || isa(x,Bool) || (!isa(x,Type) && isbits(x)) +end + +has_free_typevars(@nospecialize(t)) = ccall(:jl_has_free_typevars, Cint, (Any,), t)!=0 + +@pure function type_typeof(@nospecialize(v)) + if isa(v, Type) + return Type{v} + end + return typeof(v) +end + +# return an upper-bound on type `a` with type `b` removed +# such that `return <: a` && `Union{return, b} == Union{a, b}` +function typesubtract(@nospecialize(a), @nospecialize(b)) + if a <: b + return Bottom + end + if isa(a, Union) + return Union{typesubtract(a.a, b), + typesubtract(a.b, b)} + end + return a # TODO: improve this bound? +end + +function tuple_tail_elem(@nospecialize(init), ct) + return Vararg{widenconst(foldl((a, b) -> tmerge(a, unwrapva(b)), init, ct))} +end + +# t[n:end] +function tupleparam_tail(t::SimpleVector, n) + lt = length(t) + if n > lt + va = t[lt] + if isvarargtype(va) + # assumes that we should never see Vararg{T, x}, where x is a constant (should be guaranteed by construction) + return Tuple{va} + end + return Tuple{} + end + return Tuple{t[n:lt]...} +end + +# take a Tuple where one or more parameters are Unions +# and return an array such that those Unions are removed +# and `Union{return...} == ty` +function switchtupleunion(@nospecialize(ty)) + tparams = (unwrap_unionall(ty)::DataType).parameters + return _switchtupleunion(Any[tparams...], length(tparams), [], ty) +end + +function _switchtupleunion(t::Vector{Any}, i::Int, tunion::Vector{Any}, @nospecialize(origt)) + if i == 0 + tpl = rewrap_unionall(Tuple{t...}, origt) + push!(tunion, tpl) + else + ti = t[i] + if isa(ti, Union) + for ty in uniontypes(ti::Union) + t[i] = ty + _switchtupleunion(t, i - 1, tunion, origt) + end + t[i] = ti + else + _switchtupleunion(t, i - 1, tunion, origt) + end + end + return tunion +end diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl new file mode 100644 index 0000000000000..9df7d5f09638b --- /dev/null +++ b/base/compiler/utilities.jl @@ -0,0 +1,269 @@ +########### +# generic # +########### + +# avoid cycle due to over-specializing `any` when used by inference +function _any(@nospecialize(f), a) + for x in a + f(x) && return true + end + return false +end + +function contains_is(itr, @nospecialize(x)) + for y in itr + if y === x + return true + end + end + return false +end + +anymap(f::Function, a::Array{Any,1}) = Any[ f(a[i]) for i in 1:length(a) ] + +########### +# scoping # +########### + +_topmod(m::Module) = ccall(:jl_base_relative_to, Any, (Any,), m)::Module + +function istopfunction(topmod, @nospecialize(f), sym) + if isdefined(Main, :Base) && isdefined(Main.Base, sym) && isconst(Main.Base, sym) && f === getfield(Main.Base, sym) + return true + elseif isdefined(topmod, sym) && isconst(topmod, sym) && f === getfield(topmod, sym) + return true + end + return false +end + +####### +# AST # +####### + +# Meta expression head, these generally can't be deleted even when they are +# in a dead branch but can be ignored when analyzing uses/liveness. +is_meta_expr_head(head::Symbol) = (head === :inbounds || head === :boundscheck || head === :meta || head === :simdloop) +is_meta_expr(ex::Expr) = is_meta_expr_head(ex.head) + +sym_isless(a::Symbol, b::Symbol) = ccall(:strcmp, Int32, (Ptr{UInt8}, Ptr{UInt8}), a, b) < 0 + +function is_self_quoting(@nospecialize(x)) + return isa(x,Number) || isa(x,AbstractString) || isa(x,Tuple) || isa(x,Type) || + isa(x,Char) || x === nothing || isa(x,Function) +end + +function quoted(@nospecialize(x)) + return is_self_quoting(x) ? x : QuoteNode(x) +end + +# count occurrences up to n+1 +function occurs_more(@nospecialize(e), pred, n) + if isa(e,Expr) + e = e::Expr + head = e.head + is_meta_expr_head(head) && return 0 + c = 0 + for a = e.args + c += occurs_more(a, pred, n) + if c>n + return c + end + end + return c + end + if pred(e) + return 1 + end + return 0 +end + +########################### +# MethodInstance/CodeInfo # +########################### + +function get_staged(li::MethodInstance) + try + # user code might throw errors – ignore them + return ccall(:jl_code_for_staged, Any, (Any,), li)::CodeInfo + catch + return nothing + end +end + +# create copies of the CodeInfo definition, and any fields that type-inference might modify +function copy_code_info(c::CodeInfo) + cnew = ccall(:jl_copy_code_info, Ref{CodeInfo}, (Any,), c) + cnew.code = copy_exprargs(cnew.code) + cnew.slotnames = copy(cnew.slotnames) + cnew.slotflags = copy(cnew.slotflags) + return cnew +end + +function retrieve_code_info(linfo::MethodInstance) + m = linfo.def::Method + if isdefined(m, :generator) + # user code might throw errors – ignore them + return get_staged(linfo) + else + # TODO: post-inference see if we can swap back to the original arrays? + if isa(m.source, Array{UInt8,1}) + c = ccall(:jl_uncompress_ast, Any, (Any, Any), m, m.source) + else + c = copy_code_info(m.source) + end + end + return c +end + +function code_for_method(method::Method, @nospecialize(atypes), sparams::SimpleVector, world::UInt, preexisting::Bool=false) + if world < min_world(method) + return nothing + end + if isdefined(method, :generator) && !isleaftype(atypes) + # don't call staged functions on abstract types. + # (see issues #8504, #10230) + # we can't guarantee that their type behavior is monotonic. + # XXX: this test is wrong if Types (such as DataType or Bottom) are present + return nothing + end + if preexisting + if method.specializations !== nothing + # check cached specializations + # for an existing result stored there + return ccall(:jl_specializations_lookup, Any, (Any, Any, UInt), method, atypes, world) + end + return nothing + end + return ccall(:jl_specializations_get_linfo, Ref{MethodInstance}, (Any, Any, Any, UInt), method, atypes, sparams, world) +end + +# TODO: Use these functions instead of directly manipulating +# the "actual" method for appropriate places in inference (see #24676) +function method_for_inference_heuristics(cinfo, default) + if isa(cinfo, CodeInfo) + # appropriate format for `sig` is svec(ftype, argtypes, world) + sig = cinfo.signature_for_inference_heuristics + if isa(sig, SimpleVector) && length(sig) == 3 + methods = _methods(sig[1], sig[2], -1, sig[3]) + if length(methods) == 1 + _, _, m = methods[] + if isa(m, Method) + return m + end + end + end + end + return default +end + +function method_for_inference_heuristics(method::Method, @nospecialize(sig), sparams, world) + if isdefined(method, :generator) && method.generator.expand_early + method_instance = code_for_method(method, sig, sparams, world, false) + if isa(method_instance, MethodInstance) + return method_for_inference_heuristics(get_staged(method_instance), method) + end + end + return method +end + +function exprtype(@nospecialize(x), src::CodeInfo, mod::Module) + if isa(x, Expr) + return (x::Expr).typ + elseif isa(x, SlotNumber) + return src.slottypes[(x::SlotNumber).id] + elseif isa(x, TypedSlot) + return (x::TypedSlot).typ + elseif isa(x, SSAValue) + return abstract_eval_ssavalue(x::SSAValue, src) + elseif isa(x, Symbol) + return abstract_eval_global(mod, x::Symbol) + elseif isa(x, QuoteNode) + return AbstractEvalConstant((x::QuoteNode).value) + elseif isa(x, GlobalRef) + return abstract_eval_global(x.mod, (x::GlobalRef).name) + else + return AbstractEvalConstant(x) + end +end + +################### +# SSAValues/Slots # +################### + +function find_ssavalue_uses(body::Vector{Any}, nvals::Int) + uses = BitSet[ BitSet() for i = 1:nvals ] + for line in 1:length(body) + e = body[line] + isa(e, Expr) && find_ssavalue_uses(e, uses, line) + end + return uses +end + +function find_ssavalue_uses(e::Expr, uses::Vector{BitSet}, line::Int) + head = e.head + is_meta_expr_head(head) && return + skiparg = (head === :(=)) + for a in e.args + if skiparg + skiparg = false + elseif isa(a, SSAValue) + push!(uses[a.id + 1], line) + elseif isa(a, Expr) + find_ssavalue_uses(a, uses, line) + end + end +end + +function find_ssavalue_defs(body::Vector{Any}, nvals::Int) + defs = zeros(Int, nvals) + for line in 1:length(body) + e = body[line] + if isa(e, Expr) && e.head === :(=) + lhs = e.args[1] + if isa(lhs, SSAValue) + defs[lhs.id + 1] = line + end + end + end + return defs +end + +# using a function to ensure we can infer this +@inline slot_id(s) = isa(s, SlotNumber) ? (s::SlotNumber).id : (s::TypedSlot).id + +############## +# LabelNodes # +############## + +# scan body for the value of the largest referenced label +function label_counter(body::Vector{Any}) + l = 0 + for b in body + label = 0 + if isa(b, GotoNode) + label = b.label::Int + elseif isa(b, LabelNode) + label = b.label + elseif isa(b, Expr) && b.head == :gotoifnot + label = b.args[2]::Int + elseif isa(b, Expr) && b.head == :enter + label = b.args[1]::Int + end + if label > l + l = label + end + end + return l +end + +function get_label_map(body::Vector{Any}) + nlabels = label_counter(body) + labelmap = zeros(Int, nlabels) + for i = 1:length(body) + el = body[i] + if isa(el, LabelNode) + labelmap[el.label] = i + end + end + return labelmap +end diff --git a/base/codevalidation.jl b/base/compiler/validation.jl similarity index 90% rename from base/codevalidation.jl rename to base/compiler/validation.jl index 94d39af460e09..05a8500bd8743 100644 --- a/base/codevalidation.jl +++ b/base/compiler/validation.jl @@ -52,6 +52,23 @@ struct InvalidCodeError <: Exception end InvalidCodeError(kind::AbstractString) = InvalidCodeError(kind, nothing) +function validate_code_in_debug_mode(linfo::MethodInstance, src::CodeInfo, kind::String) + if JLOptions().debug_level == 2 + # this is a debug build of julia, so let's validate linfo + errors = validate_code(linfo, src) + if !isempty(errors) + for e in errors + if linfo.def isa Method + println(STDERR, "WARNING: Encountered invalid ", kind, " code for method ", + linfo.def, ": ", e) + else + println(STDERR, "WARNING: Encountered invalid ", kind, " code for top level expression in ", + linfo.def, ": ", e) + end + end + end + end +end """ validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo) @@ -157,7 +174,7 @@ end """ validate_code!(errors::Vector{>:InvalidCodeError}, mi::MethodInstance, - c::Union{Nothing,CodeInfo} = Core.Inference.retrieve_code_info(mi)) + c::Union{Nothing,CodeInfo} = Core.Compiler.retrieve_code_info(mi)) Validate `mi`, logging any violation by pushing an `InvalidCodeError` into `errors`. @@ -165,14 +182,14 @@ If `isa(c, CodeInfo)`, also call `validate_code!(errors, c)`. It is assumed that the `CodeInfo` instance associated with `mi`. """ function validate_code!(errors::Vector{>:InvalidCodeError}, mi::Core.MethodInstance, - c::Union{Nothing,CodeInfo} = Core.Inference.retrieve_code_info(mi)) + c::Union{Nothing,CodeInfo} = Core.Compiler.retrieve_code_info(mi)) is_top_level = mi.def isa Module if is_top_level mnargs = 0 else m = mi.def::Method mnargs = m.nargs - n_sig_params = length(Core.Inference.unwrap_unionall(m.sig).parameters) + n_sig_params = length(Core.Compiler.unwrap_unionall(m.sig).parameters) if (m.isva ? (n_sig_params < (mnargs - 1)) : (n_sig_params != mnargs)) push!(errors, InvalidCodeError(SIGNATURE_NARGS_MISMATCH, (m.isva, n_sig_params, mnargs))) end diff --git a/base/deprecated.jl b/base/deprecated.jl index bc7776350873c..6c84be2be96b9 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -490,7 +490,7 @@ import .LinAlg: Bidiagonal @deprecate Bidiagonal(A::AbstractMatrix, isupper::Bool) Bidiagonal(A, ifelse(isupper, :U, :L)) @deprecate fieldnames(v) fieldnames(typeof(v)) -# nfields(::Type) deprecation in builtins.c: update nfields tfunc in inference.jl when it is removed. +# nfields(::Type) deprecation in builtins.c: update nfields tfunc in compiler/tfuncs.jl when it is removed. # also replace `_nfields` with `nfields` in summarysize.c when this is removed. # ::ANY is deprecated in src/method.c @@ -650,7 +650,7 @@ function (::Type{T})(arg) where {T} end convert(T, arg)::T end -# related items to remove in: abstractarray.jl, dates/periods.jl, inference.jl +# related items to remove in: abstractarray.jl, dates/periods.jl, compiler.jl # also remove all uses of is_default_method # Issue #19923 diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 722e701f78cac..3a98e9edd876a 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -225,7 +225,7 @@ end const HWReal = Union{Int8,Int16,Int32,Int64,UInt8,UInt16,UInt32,UInt64,Float32,Float64} const HWNumber = Union{HWReal, Complex{<:HWReal}, Rational{<:HWReal}} -# inference.jl has complicated logic to inline x^2 and x^3 for +# Core.Compiler has complicated logic to inline x^2 and x^3 for # numeric types. In terms of Val we can do it much more simply. # (The first argument prevents unexpected behavior if a function ^ # is defined that is not equal to Base.^) diff --git a/base/precompile.jl b/base/precompile.jl index 8f38d86a315a4..998bd258cc4b2 100644 --- a/base/precompile.jl +++ b/base/precompile.jl @@ -34,10 +34,10 @@ precompile(Tuple{typeof(Base.dec), UInt16, Int64, Bool}) precompile(Tuple{typeof(Base.Libc.strerror), Int32}) precompile(Tuple{typeof(Base.copyto!), Array{Any, 1}, Base.KeySet{Any, Base.Dict{Any, Any}}}) precompile(Tuple{typeof(Base.promoteK), Type{Any}}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{Core.Inference.Const, DataType, Core.Inference.Const, Core.Inference.Const}}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{Core.Inference.Const, DataType, Core.Inference.Const, Core.Inference.Const}, Int64}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{Core.Compiler.Const, DataType, Core.Compiler.Const, Core.Compiler.Const}}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{Core.Compiler.Const, DataType, Core.Compiler.Const, Core.Compiler.Const}, Int64}) precompile(Tuple{typeof(Base.sync_add), Task}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{Int64, Tuple{Int64, Tuple{}}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{Int64, Tuple{Int64, Tuple{}}}, Int64}) precompile(Tuple{typeof(Base.Sort.Float.isnan), Base.Order.ForwardOrdering, Float32}) precompile(Tuple{typeof(Base.Sort.Float.isnan), Base.Order.ForwardOrdering, Float64}) precompile(Tuple{typeof(Base.Sort.Float.isnan), Base.Order.ReverseOrdering{Base.Order.ForwardOrdering}, Float32}) @@ -46,10 +46,10 @@ precompile(Tuple{typeof(Base.Sort.Float.issignleft), Base.Order.ForwardOrdering, precompile(Tuple{typeof(Base.Sort.Float.issignleft), Base.Order.ReverseOrdering{Base.Order.ForwardOrdering}, Float32}) precompile(Tuple{typeof(Base.Sort.Float.issignleft), Base.Order.ForwardOrdering, Float64}) precompile(Tuple{typeof(Base.Sort.Float.issignleft), Base.Order.ReverseOrdering{Base.Order.ForwardOrdering}, Float64}) -precompile(Tuple{typeof(Core.Inference.isbits), Base.Sort.Float.Left}) +precompile(Tuple{typeof(Core.Compiler.isbits), Base.Sort.Float.Left}) precompile(Tuple{typeof(Base.Sort.Float.left), Base.Order.ForwardOrdering}) precompile(Tuple{typeof(Base.Sort.Float.left), Base.Order.ReverseOrdering{Base.Order.ForwardOrdering}}) -precompile(Tuple{typeof(Core.Inference.isbits), Base.Sort.Float.Right}) +precompile(Tuple{typeof(Core.Compiler.isbits), Base.Sort.Float.Right}) precompile(Tuple{typeof(Base.Sort.Float.right), Base.Order.ForwardOrdering}) precompile(Tuple{typeof(Base.Sort.Float.right), Base.Order.ReverseOrdering{Base.Order.ForwardOrdering}}) precompile(Tuple{typeof(Base.stat), Int64}) @@ -60,104 +60,104 @@ precompile(Tuple{typeof(Base.lstrip), Base.SubString{String}, Array{Char, 1}}) precompile(Tuple{getfield(Base, Symbol("#kw##split")), Array{Any, 1}, typeof(Base.split), String, Char}) precompile(Tuple{getfield(Base, Symbol("#kw##split")), Array{Any, 1}, typeof(Base.split), Base.SubString{String}, Char}) precompile(Tuple{typeof(Base.map!), typeof(Base.strip), Array{Base.SubString{String}, 1}, Array{Base.SubString{String}, 1}}) -precompile(Tuple{Type{Core.Inference.Generator{I, F} where F where I}, Type{Core.Inference.Const}, Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}}) -precompile(Tuple{Type{Core.Inference.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{Core.Inference.Const}}}, Type{Core.Inference.Const}, Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}}) -precompile(Tuple{typeof(Core.Inference.convert), Type{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}}, Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}}) -precompile(Tuple{typeof(Core.Inference.collect), Type{Any}, Core.Inference.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference._collect), Type{Any}, Core.Inference.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{Core.Inference.Const}}, Core.Inference.HasLength}) -precompile(Tuple{typeof(Core.Inference.length), Core.Inference.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}}) -precompile(Tuple{typeof(Core.Inference.copyto!), Array{Any, 1}, Core.Inference.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.start), Core.Inference.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.start), Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}}) -precompile(Tuple{typeof(Core.Inference.done), Core.Inference.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{Core.Inference.Const}}, Int64}) -precompile(Tuple{typeof(Core.Inference.done), Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Int64}) -precompile(Tuple{typeof(Core.Inference.next), Core.Inference.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{Core.Inference.Const}}, Int64}) -precompile(Tuple{typeof(Core.Inference.next), Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Int64}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Int64}) -precompile(Tuple{typeof(Core.Inference.start), Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}, Int64}}) -precompile(Tuple{typeof(Core.Inference.indexed_next), Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}, Int64}, Int64, Int64}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}, Int64}, Int64}) -precompile(Tuple{typeof(Core.Inference.isbits), Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}) -precompile(Tuple{typeof(Core.Inference.isbits), Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}}) -precompile(Tuple{Type{Core.Inference.Generator{I, F} where F where I}, Type{QuoteNode}, Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}}) -precompile(Tuple{Type{Core.Inference.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{QuoteNode}}}, Type{QuoteNode}, Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}}) -precompile(Tuple{typeof(Core.Inference._collect), Type{Any}, Core.Inference.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{QuoteNode}}, Core.Inference.HasLength}) -precompile(Tuple{typeof(Core.Inference.length), Core.Inference.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{QuoteNode}}}) -precompile(Tuple{typeof(Core.Inference.copyto!), Array{Any, 1}, Core.Inference.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{QuoteNode}}}) -precompile(Tuple{typeof(Core.Inference.start), Core.Inference.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{QuoteNode}}}) -precompile(Tuple{typeof(Core.Inference.done), Core.Inference.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{QuoteNode}}, Int64}) -precompile(Tuple{typeof(Core.Inference.next), Core.Inference.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{QuoteNode}}, Int64}) -precompile(Tuple{typeof(Core.Inference.isbits), Base.DevNullStream}) +precompile(Tuple{Type{Core.Compiler.Generator{I, F} where F where I}, Type{Core.Compiler.Const}, Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}}) +precompile(Tuple{Type{Core.Compiler.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{Core.Compiler.Const}}}, Type{Core.Compiler.Const}, Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}}) +precompile(Tuple{typeof(Core.Compiler.convert), Type{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}}, Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}}) +precompile(Tuple{typeof(Core.Compiler.collect), Type{Any}, Core.Compiler.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler._collect), Type{Any}, Core.Compiler.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{Core.Compiler.Const}}, Core.Compiler.HasLength}) +precompile(Tuple{typeof(Core.Compiler.length), Core.Compiler.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}}) +precompile(Tuple{typeof(Core.Compiler.copyto!), Array{Any, 1}, Core.Compiler.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.start), Core.Compiler.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.start), Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}}) +precompile(Tuple{typeof(Core.Compiler.done), Core.Compiler.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{Core.Compiler.Const}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.done), Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.next), Core.Compiler.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{Core.Compiler.Const}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.next), Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.start), Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}, Int64}}) +precompile(Tuple{typeof(Core.Compiler.indexed_next), Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}, Int64}, Int64, Int64}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}, Int64}, Int64}) +precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}) +precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}}) +precompile(Tuple{Type{Core.Compiler.Generator{I, F} where F where I}, Type{QuoteNode}, Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}}) +precompile(Tuple{Type{Core.Compiler.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{QuoteNode}}}, Type{QuoteNode}, Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}}) +precompile(Tuple{typeof(Core.Compiler._collect), Type{Any}, Core.Compiler.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{QuoteNode}}, Core.Compiler.HasLength}) +precompile(Tuple{typeof(Core.Compiler.length), Core.Compiler.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{QuoteNode}}}) +precompile(Tuple{typeof(Core.Compiler.copyto!), Array{Any, 1}, Core.Compiler.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{QuoteNode}}}) +precompile(Tuple{typeof(Core.Compiler.start), Core.Compiler.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{QuoteNode}}}) +precompile(Tuple{typeof(Core.Compiler.done), Core.Compiler.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{QuoteNode}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.next), Core.Compiler.Generator{Tuple{Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}, Type{QuoteNode}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.isbits), Base.DevNullStream}) precompile(Tuple{typeof(Base.finalizer), typeof(Base.uvfinalize), Base.Process}) -precompile(Tuple{typeof(Core.Inference.isbits), Tuple{Base.DevNullStream, Bool}}) +precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{Base.DevNullStream, Bool}}) precompile(Tuple{Type{Ref{Base.Cstring}}, Array{String, 1}}) -precompile(Tuple{typeof(Core.Inference.eltype), Type{Array{String, 1}}}) +precompile(Tuple{typeof(Core.Compiler.eltype), Type{Array{String, 1}}}) precompile(Tuple{typeof(Base.string), Nothing, String, DataType}) precompile(Tuple{typeof(Base.print), Base.IOContext{Base.GenericIOBuffer{Array{UInt8, 1}}}, String, Char}) precompile(Tuple{typeof(Base.throw_boundserror), Base.UnitRange{Int64}, Tuple{Base.UnitRange{Int64}}}) -precompile(Tuple{typeof(Core.Inference.convert), Type{DataType}, Type{Bool}}) -precompile(Tuple{typeof(Core.Inference.convert), Type{DataType}, Type{Base.OneTo{Int64}}}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{DataType, DataType, typeof(Type), Core.Inference.Const, Core.Inference.Const}}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{DataType, DataType, typeof(Type), Core.Inference.Const, Core.Inference.Const}, Int64}) -precompile(Tuple{Type{Core.Inference.Generator{I, F} where F where I}, Type{Core.Inference.Const}, Tuple{Tuple{}, Tuple{}, Int64}}) -precompile(Tuple{Type{Core.Inference.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{Core.Inference.Const}}}, Type{Core.Inference.Const}, Tuple{Tuple{}, Tuple{}, Int64}}) -precompile(Tuple{typeof(Core.Inference.convert), Type{Tuple{Tuple{}, Tuple{}, Int64}}, Tuple{Tuple{}, Tuple{}, Int64}}) -precompile(Tuple{typeof(Core.Inference.collect), Type{Any}, Core.Inference.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference._collect), Type{Any}, Core.Inference.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{Core.Inference.Const}}, Core.Inference.HasLength}) -precompile(Tuple{typeof(Core.Inference.length), Core.Inference.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{Tuple{}, Tuple{}, Int64}}) -precompile(Tuple{typeof(Core.Inference.copyto!), Array{Any, 1}, Core.Inference.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.start), Core.Inference.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.start), Tuple{Tuple{}, Tuple{}, Int64}}) -precompile(Tuple{typeof(Core.Inference.done), Core.Inference.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{Core.Inference.Const}}, Int64}) -precompile(Tuple{typeof(Core.Inference.done), Tuple{Tuple{}, Tuple{}, Int64}, Int64}) -precompile(Tuple{typeof(Core.Inference.next), Core.Inference.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{Core.Inference.Const}}, Int64}) -precompile(Tuple{typeof(Core.Inference.next), Tuple{Tuple{}, Tuple{}, Int64}, Int64}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{Tuple{}, Tuple{}, Int64}, Int64}) -precompile(Tuple{typeof(Core.Inference.isbits), Tuple{Tuple{}, Tuple{}, Int64}}) -precompile(Tuple{Type{Core.Inference.Generator{I, F} where F where I}, Type{QuoteNode}, Tuple{Tuple{}, Tuple{}, Int64}}) -precompile(Tuple{Type{Core.Inference.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{QuoteNode}}}, Type{QuoteNode}, Tuple{Tuple{}, Tuple{}, Int64}}) -precompile(Tuple{typeof(Core.Inference._collect), Type{Any}, Core.Inference.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{QuoteNode}}, Core.Inference.HasLength}) -precompile(Tuple{typeof(Core.Inference.length), Core.Inference.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{QuoteNode}}}) -precompile(Tuple{typeof(Core.Inference.copyto!), Array{Any, 1}, Core.Inference.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{QuoteNode}}}) -precompile(Tuple{typeof(Core.Inference.start), Core.Inference.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{QuoteNode}}}) -precompile(Tuple{typeof(Core.Inference.done), Core.Inference.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{QuoteNode}}, Int64}) -precompile(Tuple{typeof(Core.Inference.next), Core.Inference.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{QuoteNode}}, Int64}) -precompile(Tuple{Type{Core.Inference.Generator{I, F} where F where I}, Type{Core.Inference.Const}, Tuple{Tuple{}, Tuple{}}}) -precompile(Tuple{Type{Core.Inference.Generator{Tuple{Tuple{}, Tuple{}}, Type{Core.Inference.Const}}}, Type{Core.Inference.Const}, Tuple{Tuple{}, Tuple{}}}) -precompile(Tuple{typeof(Core.Inference.convert), Type{Tuple{Tuple{}, Tuple{}}}, Tuple{Tuple{}, Tuple{}}}) -precompile(Tuple{typeof(Core.Inference.collect), Type{Any}, Core.Inference.Generator{Tuple{Tuple{}, Tuple{}}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference._collect), Type{Any}, Core.Inference.Generator{Tuple{Tuple{}, Tuple{}}, Type{Core.Inference.Const}}, Core.Inference.HasLength}) -precompile(Tuple{typeof(Core.Inference.length), Core.Inference.Generator{Tuple{Tuple{}, Tuple{}}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{Tuple{}, Tuple{}}}) -precompile(Tuple{typeof(Core.Inference.copyto!), Array{Any, 1}, Core.Inference.Generator{Tuple{Tuple{}, Tuple{}}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.start), Core.Inference.Generator{Tuple{Tuple{}, Tuple{}}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.start), Tuple{Tuple{}, Tuple{}}}) -precompile(Tuple{typeof(Core.Inference.done), Core.Inference.Generator{Tuple{Tuple{}, Tuple{}}, Type{Core.Inference.Const}}, Int64}) -precompile(Tuple{typeof(Core.Inference.done), Tuple{Tuple{}, Tuple{}}, Int64}) -precompile(Tuple{typeof(Core.Inference.next), Core.Inference.Generator{Tuple{Tuple{}, Tuple{}}, Type{Core.Inference.Const}}, Int64}) -precompile(Tuple{typeof(Core.Inference.next), Tuple{Tuple{}, Tuple{}}, Int64}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{Tuple{}, Tuple{}}, Int64}) -precompile(Tuple{Type{Core.Inference.Generator{I, F} where F where I}, Type{QuoteNode}, Tuple{Tuple{}, Tuple{}}}) -precompile(Tuple{Type{Core.Inference.Generator{Tuple{Tuple{}, Tuple{}}, Type{QuoteNode}}}, Type{QuoteNode}, Tuple{Tuple{}, Tuple{}}}) -precompile(Tuple{typeof(Core.Inference._collect), Type{Any}, Core.Inference.Generator{Tuple{Tuple{}, Tuple{}}, Type{QuoteNode}}, Core.Inference.HasLength}) -precompile(Tuple{typeof(Core.Inference.length), Core.Inference.Generator{Tuple{Tuple{}, Tuple{}}, Type{QuoteNode}}}) -precompile(Tuple{typeof(Core.Inference.copyto!), Array{Any, 1}, Core.Inference.Generator{Tuple{Tuple{}, Tuple{}}, Type{QuoteNode}}}) -precompile(Tuple{typeof(Core.Inference.start), Core.Inference.Generator{Tuple{Tuple{}, Tuple{}}, Type{QuoteNode}}}) -precompile(Tuple{typeof(Core.Inference.done), Core.Inference.Generator{Tuple{Tuple{}, Tuple{}}, Type{QuoteNode}}, Int64}) -precompile(Tuple{typeof(Core.Inference.next), Core.Inference.Generator{Tuple{Tuple{}, Tuple{}}, Type{QuoteNode}}, Int64}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{DataType, DataType, DataType, Core.Inference.Const, Core.Inference.Const}}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{DataType, DataType, DataType, Core.Inference.Const, Core.Inference.Const}, Int64}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{DataType, DataType, typeof(Type), typeof(Type), Core.Inference.Const}}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{DataType, DataType, typeof(Type), typeof(Type), Core.Inference.Const}, Int64}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{DataType, DataType, DataType, DataType, Core.Inference.Const}}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{DataType, DataType, DataType, DataType, Core.Inference.Const}, Int64}) +precompile(Tuple{typeof(Core.Compiler.convert), Type{DataType}, Type{Bool}}) +precompile(Tuple{typeof(Core.Compiler.convert), Type{DataType}, Type{Base.OneTo{Int64}}}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{DataType, DataType, typeof(Type), Core.Compiler.Const, Core.Compiler.Const}}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{DataType, DataType, typeof(Type), Core.Compiler.Const, Core.Compiler.Const}, Int64}) +precompile(Tuple{Type{Core.Compiler.Generator{I, F} where F where I}, Type{Core.Compiler.Const}, Tuple{Tuple{}, Tuple{}, Int64}}) +precompile(Tuple{Type{Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{Core.Compiler.Const}}}, Type{Core.Compiler.Const}, Tuple{Tuple{}, Tuple{}, Int64}}) +precompile(Tuple{typeof(Core.Compiler.convert), Type{Tuple{Tuple{}, Tuple{}, Int64}}, Tuple{Tuple{}, Tuple{}, Int64}}) +precompile(Tuple{typeof(Core.Compiler.collect), Type{Any}, Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler._collect), Type{Any}, Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{Core.Compiler.Const}}, Core.Compiler.HasLength}) +precompile(Tuple{typeof(Core.Compiler.length), Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{Tuple{}, Tuple{}, Int64}}) +precompile(Tuple{typeof(Core.Compiler.copyto!), Array{Any, 1}, Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.start), Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.start), Tuple{Tuple{}, Tuple{}, Int64}}) +precompile(Tuple{typeof(Core.Compiler.done), Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{Core.Compiler.Const}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.done), Tuple{Tuple{}, Tuple{}, Int64}, Int64}) +precompile(Tuple{typeof(Core.Compiler.next), Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{Core.Compiler.Const}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.next), Tuple{Tuple{}, Tuple{}, Int64}, Int64}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{Tuple{}, Tuple{}, Int64}, Int64}) +precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{Tuple{}, Tuple{}, Int64}}) +precompile(Tuple{Type{Core.Compiler.Generator{I, F} where F where I}, Type{QuoteNode}, Tuple{Tuple{}, Tuple{}, Int64}}) +precompile(Tuple{Type{Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{QuoteNode}}}, Type{QuoteNode}, Tuple{Tuple{}, Tuple{}, Int64}}) +precompile(Tuple{typeof(Core.Compiler._collect), Type{Any}, Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{QuoteNode}}, Core.Compiler.HasLength}) +precompile(Tuple{typeof(Core.Compiler.length), Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{QuoteNode}}}) +precompile(Tuple{typeof(Core.Compiler.copyto!), Array{Any, 1}, Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{QuoteNode}}}) +precompile(Tuple{typeof(Core.Compiler.start), Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{QuoteNode}}}) +precompile(Tuple{typeof(Core.Compiler.done), Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{QuoteNode}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.next), Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}, Int64}, Type{QuoteNode}}, Int64}) +precompile(Tuple{Type{Core.Compiler.Generator{I, F} where F where I}, Type{Core.Compiler.Const}, Tuple{Tuple{}, Tuple{}}}) +precompile(Tuple{Type{Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}}, Type{Core.Compiler.Const}}}, Type{Core.Compiler.Const}, Tuple{Tuple{}, Tuple{}}}) +precompile(Tuple{typeof(Core.Compiler.convert), Type{Tuple{Tuple{}, Tuple{}}}, Tuple{Tuple{}, Tuple{}}}) +precompile(Tuple{typeof(Core.Compiler.collect), Type{Any}, Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler._collect), Type{Any}, Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}}, Type{Core.Compiler.Const}}, Core.Compiler.HasLength}) +precompile(Tuple{typeof(Core.Compiler.length), Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{Tuple{}, Tuple{}}}) +precompile(Tuple{typeof(Core.Compiler.copyto!), Array{Any, 1}, Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.start), Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.start), Tuple{Tuple{}, Tuple{}}}) +precompile(Tuple{typeof(Core.Compiler.done), Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}}, Type{Core.Compiler.Const}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.done), Tuple{Tuple{}, Tuple{}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.next), Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}}, Type{Core.Compiler.Const}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.next), Tuple{Tuple{}, Tuple{}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{Tuple{}, Tuple{}}, Int64}) +precompile(Tuple{Type{Core.Compiler.Generator{I, F} where F where I}, Type{QuoteNode}, Tuple{Tuple{}, Tuple{}}}) +precompile(Tuple{Type{Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}}, Type{QuoteNode}}}, Type{QuoteNode}, Tuple{Tuple{}, Tuple{}}}) +precompile(Tuple{typeof(Core.Compiler._collect), Type{Any}, Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}}, Type{QuoteNode}}, Core.Compiler.HasLength}) +precompile(Tuple{typeof(Core.Compiler.length), Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}}, Type{QuoteNode}}}) +precompile(Tuple{typeof(Core.Compiler.copyto!), Array{Any, 1}, Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}}, Type{QuoteNode}}}) +precompile(Tuple{typeof(Core.Compiler.start), Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}}, Type{QuoteNode}}}) +precompile(Tuple{typeof(Core.Compiler.done), Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}}, Type{QuoteNode}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.next), Core.Compiler.Generator{Tuple{Tuple{}, Tuple{}}, Type{QuoteNode}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{DataType, DataType, DataType, Core.Compiler.Const, Core.Compiler.Const}}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{DataType, DataType, DataType, Core.Compiler.Const, Core.Compiler.Const}, Int64}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{DataType, DataType, typeof(Type), typeof(Type), Core.Compiler.Const}}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{DataType, DataType, typeof(Type), typeof(Type), Core.Compiler.Const}, Int64}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{DataType, DataType, DataType, DataType, Core.Compiler.Const}}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{DataType, DataType, DataType, DataType, Core.Compiler.Const}, Int64}) precompile(Tuple{typeof(Base.unpreserve_handle), Base.Process}) precompile(Tuple{typeof(Base.stream_wait), Base.Process, Base.Condition}) precompile(Tuple{typeof(Base.kill), Base.Process, Int64}) precompile(Tuple{typeof(Base.banner), Base.Terminals.TTYTerminal}) -precompile(Tuple{typeof(Core.Inference.isbits), Base.Dict{Any, Any}}) +precompile(Tuple{typeof(Core.Compiler.isbits), Base.Dict{Any, Any}}) precompile(Tuple{typeof(Base.answer_color)}) precompile(Tuple{typeof(Base.input_color)}) precompile(Tuple{typeof(Base.show_circular), Base.IOContext{Base.GenericIOBuffer{Array{UInt8, 1}}}, Tuple{}}) @@ -179,48 +179,48 @@ precompile(Tuple{typeof(Base._rsearch), Array{Int8, 1}, Array{UInt8, 1}, Int64}) precompile(Tuple{typeof(Base._rsearch), Array{UInt8, 1}, Char, Int64}) precompile(Tuple{typeof(Base._rsearch), Array{Int8, 1}, Char, Int64}) precompile(Tuple{typeof(Base.splice!), Array{Base.Multimedia.AbstractDisplay, 1}, Int64, Array{Any, 1}}) -precompile(Tuple{typeof(Core.Inference.isbits), Base.LineEdit.EmptyCompletionProvider}) -precompile(Tuple{typeof(Core.Inference.isbits), Base.LineEdit.EmptyHistoryProvider}) +precompile(Tuple{typeof(Core.Compiler.isbits), Base.LineEdit.EmptyCompletionProvider}) +precompile(Tuple{typeof(Core.Compiler.isbits), Base.LineEdit.EmptyHistoryProvider}) precompile(Tuple{typeof(Base._setindex!), Base.Dict{Symbol, Any}, Base.LineEdit.Prompt, Symbol, Int64}) precompile(Tuple{typeof(Base.sizehint!), Base.Dict{Symbol, Any}, Int64}) precompile(Tuple{typeof(Base.setindex!), Base.Dict{Symbol, Any}, Base.LineEdit.Prompt, Symbol}) precompile(Tuple{typeof(Base.copyto!), Base.IndexLinear, Array{Symbol, 1}, Base.IndexLinear, Array{UInt8, 1}}) precompile(Tuple{getfield(Base, Symbol("#kw##readline")), Array{Any, 1}, typeof(Base.readline), Base.IOStream}) -precompile(Tuple{Type{Core.Inference.Generator{I, F} where F where I}, Type{Core.Inference.Const}, Tuple{Nothing}}) -precompile(Tuple{Type{Core.Inference.Generator{Tuple{Nothing}, Type{Core.Inference.Const}}}, Type{Core.Inference.Const}, Tuple{Nothing}}) -precompile(Tuple{typeof(Core.Inference.convert), Type{Tuple{Nothing}}, Tuple{Nothing}}) -precompile(Tuple{typeof(Core.Inference.collect), Type{Any}, Core.Inference.Generator{Tuple{Nothing}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference._collect), Type{Any}, Core.Inference.Generator{Tuple{Nothing}, Type{Core.Inference.Const}}, Core.Inference.HasLength}) -precompile(Tuple{typeof(Core.Inference.length), Core.Inference.Generator{Tuple{Nothing}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{Nothing}}) -precompile(Tuple{typeof(Core.Inference.copyto!), Array{Any, 1}, Core.Inference.Generator{Tuple{Nothing}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.start), Core.Inference.Generator{Tuple{Nothing}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.start), Tuple{Nothing}}) -precompile(Tuple{typeof(Core.Inference.done), Core.Inference.Generator{Tuple{Nothing}, Type{Core.Inference.Const}}, Int64}) -precompile(Tuple{typeof(Core.Inference.done), Tuple{Nothing}, Int64}) -precompile(Tuple{typeof(Core.Inference.next), Core.Inference.Generator{Tuple{Nothing}, Type{Core.Inference.Const}}, Int64}) -precompile(Tuple{typeof(Core.Inference.next), Tuple{Nothing}, Int64}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{Nothing}, Int64}) +precompile(Tuple{Type{Core.Compiler.Generator{I, F} where F where I}, Type{Core.Compiler.Const}, Tuple{Nothing}}) +precompile(Tuple{Type{Core.Compiler.Generator{Tuple{Nothing}, Type{Core.Compiler.Const}}}, Type{Core.Compiler.Const}, Tuple{Nothing}}) +precompile(Tuple{typeof(Core.Compiler.convert), Type{Tuple{Nothing}}, Tuple{Nothing}}) +precompile(Tuple{typeof(Core.Compiler.collect), Type{Any}, Core.Compiler.Generator{Tuple{Nothing}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler._collect), Type{Any}, Core.Compiler.Generator{Tuple{Nothing}, Type{Core.Compiler.Const}}, Core.Compiler.HasLength}) +precompile(Tuple{typeof(Core.Compiler.length), Core.Compiler.Generator{Tuple{Nothing}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{Nothing}}) +precompile(Tuple{typeof(Core.Compiler.copyto!), Array{Any, 1}, Core.Compiler.Generator{Tuple{Nothing}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.start), Core.Compiler.Generator{Tuple{Nothing}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.start), Tuple{Nothing}}) +precompile(Tuple{typeof(Core.Compiler.done), Core.Compiler.Generator{Tuple{Nothing}, Type{Core.Compiler.Const}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.done), Tuple{Nothing}, Int64}) +precompile(Tuple{typeof(Core.Compiler.next), Core.Compiler.Generator{Tuple{Nothing}, Type{Core.Compiler.Const}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.next), Tuple{Nothing}, Int64}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{Nothing}, Int64}) precompile(Tuple{typeof(Base.join), Base.GenericIOBuffer{Array{UInt8, 1}}, Array{String, 1}, Char}) -precompile(Tuple{Type{Core.Inference.Generator{I, F} where F where I}, Type{Core.Inference.Const}, Tuple{String, typeof(Base.info)}}) -precompile(Tuple{Type{Core.Inference.Generator{Tuple{String, typeof(Base.info)}, Type{Core.Inference.Const}}}, Type{Core.Inference.Const}, Tuple{String, typeof(Base.info)}}) -precompile(Tuple{typeof(Core.Inference.convert), Type{Tuple{String, typeof(Base.info)}}, Tuple{String, typeof(Base.info)}}) -precompile(Tuple{typeof(Core.Inference.collect), Type{Any}, Core.Inference.Generator{Tuple{String, typeof(Base.info)}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference._collect), Type{Any}, Core.Inference.Generator{Tuple{String, typeof(Base.info)}, Type{Core.Inference.Const}}, Core.Inference.HasLength}) -precompile(Tuple{typeof(Core.Inference.length), Core.Inference.Generator{Tuple{String, typeof(Base.info)}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{String, typeof(Base.info)}}) -precompile(Tuple{typeof(Core.Inference.copyto!), Array{Any, 1}, Core.Inference.Generator{Tuple{String, typeof(Base.info)}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.start), Core.Inference.Generator{Tuple{String, typeof(Base.info)}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.start), Tuple{String, typeof(Base.info)}}) -precompile(Tuple{typeof(Core.Inference.done), Core.Inference.Generator{Tuple{String, typeof(Base.info)}, Type{Core.Inference.Const}}, Int64}) -precompile(Tuple{typeof(Core.Inference.done), Tuple{String, typeof(Base.info)}, Int64}) -precompile(Tuple{typeof(Core.Inference.next), Core.Inference.Generator{Tuple{String, typeof(Base.info)}, Type{Core.Inference.Const}}, Int64}) -precompile(Tuple{typeof(Core.Inference.next), Tuple{String, typeof(Base.info)}, Int64}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{String, typeof(Base.info)}, Int64}) -precompile(Tuple{typeof(Core.Inference.start), Tuple{String, Int64}}) -precompile(Tuple{typeof(Core.Inference.start), Tuple{typeof(Base.info), Int64}}) -precompile(Tuple{typeof(Core.Inference.indexed_next), Tuple{typeof(Base.info), Int64}, Int64, Int64}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{typeof(Base.info), Int64}, Int64}) +precompile(Tuple{Type{Core.Compiler.Generator{I, F} where F where I}, Type{Core.Compiler.Const}, Tuple{String, typeof(Base.info)}}) +precompile(Tuple{Type{Core.Compiler.Generator{Tuple{String, typeof(Base.info)}, Type{Core.Compiler.Const}}}, Type{Core.Compiler.Const}, Tuple{String, typeof(Base.info)}}) +precompile(Tuple{typeof(Core.Compiler.convert), Type{Tuple{String, typeof(Base.info)}}, Tuple{String, typeof(Base.info)}}) +precompile(Tuple{typeof(Core.Compiler.collect), Type{Any}, Core.Compiler.Generator{Tuple{String, typeof(Base.info)}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler._collect), Type{Any}, Core.Compiler.Generator{Tuple{String, typeof(Base.info)}, Type{Core.Compiler.Const}}, Core.Compiler.HasLength}) +precompile(Tuple{typeof(Core.Compiler.length), Core.Compiler.Generator{Tuple{String, typeof(Base.info)}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{String, typeof(Base.info)}}) +precompile(Tuple{typeof(Core.Compiler.copyto!), Array{Any, 1}, Core.Compiler.Generator{Tuple{String, typeof(Base.info)}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.start), Core.Compiler.Generator{Tuple{String, typeof(Base.info)}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.start), Tuple{String, typeof(Base.info)}}) +precompile(Tuple{typeof(Core.Compiler.done), Core.Compiler.Generator{Tuple{String, typeof(Base.info)}, Type{Core.Compiler.Const}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.done), Tuple{String, typeof(Base.info)}, Int64}) +precompile(Tuple{typeof(Core.Compiler.next), Core.Compiler.Generator{Tuple{String, typeof(Base.info)}, Type{Core.Compiler.Const}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.next), Tuple{String, typeof(Base.info)}, Int64}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{String, typeof(Base.info)}, Int64}) +precompile(Tuple{typeof(Core.Compiler.start), Tuple{String, Int64}}) +precompile(Tuple{typeof(Core.Compiler.start), Tuple{typeof(Base.info), Int64}}) +precompile(Tuple{typeof(Core.Compiler.indexed_next), Tuple{typeof(Base.info), Int64}, Int64, Int64}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{typeof(Base.info), Int64}, Int64}) precompile(Tuple{typeof(Base.print), Base.IOContext{Base.GenericIOBuffer{Array{UInt8, 1}}}, Base.SubString{String}, Char}) precompile(Tuple{typeof(Base.sizehint!), Base.Dict{Any, Any}, Int64}) precompile(Tuple{typeof(Base.LineEdit.getEntry), Base.Dict{Char, Any}, Char}) @@ -264,7 +264,7 @@ precompile(Tuple{typeof(Base.unlock), Base.ReentrantLock}) precompile(Tuple{typeof(Base.cmd_gen), Tuple{Tuple{}}}) precompile(Tuple{typeof(Base.extrema), Array{Int64, 1}}) precompile(Tuple{typeof(Base.Sort.sort_int_range!), Array{Int64, 1}, Int64, Int64}) -precompile(Tuple{typeof(Core.Inference.isbits), Base.Sort.QuickSortAlg}) +precompile(Tuple{typeof(Core.Compiler.isbits), Base.Sort.QuickSortAlg}) precompile(Tuple{typeof(Base.ht_keyindex2!), Base.Dict{Any, Any}, Symbol}) precompile(Tuple{typeof(Base._setindex!), Base.Dict{Any, Any}, Symbol, Symbol, Int64}) precompile(Tuple{Type{Base.Dict{Any, Any}}, Base.Pair{Symbol, Symbol}, Base.Pair{Symbol, String}, Base.Pair{Symbol, String}, Base.Pair{Symbol, Base.Cmd}, Base.Pair{Symbol, Bool}}) @@ -285,28 +285,28 @@ precompile(Tuple{typeof(Base.LineEdit.postprocess!), Base.Dict{Char, Any}}) precompile(Tuple{typeof(Base.LineEdit.keymap_unify), Array{Base.Dict{Any, Any}, 1}}) precompile(Tuple{typeof(Base.LineEdit.validate_keymap), Base.Dict{Char, Any}}) precompile(Tuple{Type{Expr}, Symbol, GlobalRef, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue, SSAValue}) -precompile(Tuple{typeof(Core.Inference.isbits), Tuple{String, typeof(Base.info)}}) +precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{String, typeof(Base.info)}}) precompile(Tuple{typeof(Base.:(==)), Nothing, String}) precompile(Tuple{Type{Symbol}, Nothing}) precompile(Tuple{Type{Symbol}, Base.SubString{String}}) -precompile(Tuple{typeof(Core.Inference.isbits), Base.REPL.REPLCompletionProvider}) +precompile(Tuple{typeof(Core.Compiler.isbits), Base.REPL.REPLCompletionProvider}) precompile(Tuple{getfield(Core, Symbol("#kw#Type")), Array{Any, 1}, Type{Base.LineEdit.Prompt}, String}) -precompile(Tuple{typeof(Core.Inference.isbits), Base.REPL.ShellCompletionProvider}) +precompile(Tuple{typeof(Core.Compiler.isbits), Base.REPL.ShellCompletionProvider}) precompile(Tuple{Type{Base.Dict{Symbol, Any}}, Base.Pair{Symbol, Base.LineEdit.Prompt}, Base.Pair{Symbol, Base.LineEdit.Prompt}, Base.Pair{Symbol, Base.LineEdit.Prompt}}) precompile(Tuple{Type{Base.REPL.REPLHistoryProvider}, Base.Dict{Symbol, Any}}) precompile(Tuple{typeof(Base.REPL.find_hist_file)}) precompile(Tuple{typeof(Base.REPL.history_reset_state), Base.REPL.REPLHistoryProvider}) precompile(Tuple{typeof(Base.LineEdit.setup_search_keymap), Base.REPL.REPLHistoryProvider}) -precompile(Tuple{typeof(Core.Inference.isbits), Base.REPL.LatexCompletions}) +precompile(Tuple{typeof(Core.Compiler.isbits), Base.REPL.LatexCompletions}) precompile(Tuple{typeof(Base.LineEdit.setup_prefix_keymap), Base.REPL.REPLHistoryProvider, Base.LineEdit.Prompt}) precompile(Tuple{typeof(Base.getindex), Type{Base.Dict{Any, Any}}, Base.Dict{Any, Any}, Base.Dict{Any, Any}, Base.Dict{Any, Any}, Base.Dict{Any, Any}, Base.Dict{Any, Any}, Base.Dict{Any, Any}}) precompile(Tuple{typeof(Base.prepend!), Array{Base.Dict{Any, Any}, 1}, Array{Base.Dict{Any, Any}, 1}}) precompile(Tuple{typeof(Base.REPL.mode_keymap), Base.LineEdit.Prompt}) -precompile(Tuple{typeof(Core.Inference.isbits), Array{Base.Multimedia.AbstractDisplay, 1}}) +precompile(Tuple{typeof(Core.Compiler.isbits), Array{Base.Multimedia.AbstractDisplay, 1}}) precompile(Tuple{typeof(Base.Multimedia.popdisplay), Base.REPL.REPLDisplay{Base.REPL.LineEditREPL}}) precompile(Tuple{Type{String}, Base.BitArray{1}}) precompile(Tuple{typeof(Base.REPL.ends_with_semicolon), String}) -precompile(Tuple{typeof(Core.Inference.isbits), Tuple{Nothing, Int64}}) +precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{Nothing, Int64}}) precompile(Tuple{typeof(Base.Multimedia.popdisplay), Base.REPL.REPLDisplay{Base.REPL.BasicREPL}}) precompile(Tuple{typeof(Base.Multimedia.popdisplay), Base.REPL.REPLDisplay{Base.REPL.StreamREPL}}) precompile(Tuple{typeof(Base.REPL.start_repl_backend), Base.Channel{Any}, Base.Channel{Any}}) @@ -318,11 +318,11 @@ precompile(Tuple{typeof(Base.mapreduce_impl), typeof(Base.success), typeof(Base. precompile(Tuple{typeof(Base.string), Base.Cmd}) precompile(Tuple{typeof(Base.test_success), Base.Process}) precompile(Tuple{typeof(Base._mapreduce), typeof(Base.success), typeof(Base.:(&)), Base.IndexLinear, Array{Base.Process, 1}}) -precompile(Tuple{typeof(Core.Inference.isbits), Tuple{String, String, String}}) +precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{String, String, String}}) precompile(Tuple{getfield(Base, Symbol("#kw##shell_escape")), Array{Any, 1}, typeof(Base.shell_escape), Base.Cmd}) precompile(Tuple{typeof(Base.show), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Cmd}) precompile(Tuple{typeof(Base.print), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Cmd}) -precompile(Tuple{typeof(Core.Inference.isbits), Ptr{Cvoid}}) +precompile(Tuple{typeof(Core.Compiler.isbits), Ptr{Cvoid}}) precompile(Tuple{typeof(Base.cconvert), Type{Ptr{Base.Cstring}}, Ptr{Cvoid}}) precompile(Tuple{typeof(Base.cconvert), Type{Ptr{Base.Cstring}}, Nothing}) precompile(Tuple{typeof(Base.cconvert), Type{Base.Cstring}, Ptr{Cvoid}}) @@ -330,7 +330,7 @@ precompile(Tuple{typeof(Base.unsafe_convert), Type{Ptr{Base.Cstring}}, Ptr{Cvoid precompile(Tuple{typeof(Base.unsafe_convert), Type{Ptr{Base.Cstring}}, Base.RefArray{Base.Cstring, Array{Base.Cstring, 1}, Any}}) precompile(Tuple{Type{Base.Process}, Base.Cmd, Ptr{Cvoid}, Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}) precompile(Tuple{getfield(Base, Symbol("#kw##spawn")), Array{Any, 1}, typeof(Base.spawn), Base.Cmd, Tuple{Base.DevNullStream, Base.DevNullStream, Base.DevNullStream}}) -precompile(Tuple{typeof(Core.Inference.isbits), Tuple{Tuple{String}, Tuple{String}, Tuple{String}}}) +precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{Tuple{String}, Tuple{String}, Tuple{String}}}) precompile(Tuple{typeof(Base.cmd_gen), Tuple{Tuple{String}, Tuple{String}, Tuple{String}}}) precompile(Tuple{typeof(Base.append!), Array{String, 1}, Array{Any, 1}}) precompile(Tuple{typeof(Base.process_options), Base.JLOptions}) @@ -406,7 +406,7 @@ precompile(Tuple{typeof(Base.LineEdit.init_state), Base.Terminals.TTYTerminal, B precompile(Tuple{typeof(Base.setindex!), Base.Dict{Any, Any}, Base.LineEdit.PrefixSearchState, Base.LineEdit.PrefixHistoryPrompt{Base.REPL.REPLHistoryProvider}}) precompile(Tuple{typeof(Base.ht_keyindex2!), Base.Dict{Any, Any}, Base.LineEdit.PrefixHistoryPrompt{Base.REPL.REPLHistoryProvider}}) precompile(Tuple{typeof(Base._setindex!), Base.Dict{Any, Any}, Base.LineEdit.PrefixSearchState, Base.LineEdit.PrefixHistoryPrompt{Base.REPL.REPLHistoryProvider}, Int64}) -precompile(Tuple{typeof(Core.Inference.isbits), Tuple{Tuple{}, Nothing}}) +precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{Tuple{}, Nothing}}) precompile(Tuple{typeof(Base.take!), Base.Channel{Any}}) precompile(Tuple{typeof(Base.n_avail), Base.Channel{Any}}) precompile(Tuple{typeof(Base.check_channel_state), Base.Channel{Any}}) @@ -510,8 +510,8 @@ precompile(Tuple{getfield(Base, Symbol("#kw##show")), Array{Any, 1}, typeof(Base precompile(Tuple{getfield(Base, Symbol("#kw##show_trace_entry")), Array{Any, 1}, typeof(Base.show_trace_entry), Base.IOContext{Base.Terminals.TTYTerminal}, Base.StackTraces.StackFrame, Int64}) precompile(Tuple{typeof(Base.show_backtrace), Base.Terminals.TTYTerminal, Array{Ptr{Cvoid}, 1}}) precompile(Tuple{typeof(Base.Multimedia.display), Int64}) -precompile(Tuple{typeof(Core.Inference.isbits), Base.MIME{Symbol("text/plain")}}) -precompile(Tuple{typeof(Core.Inference.isbits), Tuple{typeof(Base.Multimedia.display), typeof(Base.show)}}) +precompile(Tuple{typeof(Core.Compiler.isbits), Base.MIME{Symbol("text/plain")}}) +precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{typeof(Base.Multimedia.display), typeof(Base.show)}}) precompile(Tuple{typeof(Base.Multimedia.display), Base.REPL.REPLDisplay{Base.REPL.LineEditREPL}, Int64}) precompile(Tuple{Type{Base.MIME{Symbol("text/plain")}}}) precompile(Tuple{typeof(Base.Multimedia.display), Base.REPL.REPLDisplay{Base.REPL.LineEditREPL}, Base.MIME{Symbol("text/plain")}, Int64}) @@ -530,9 +530,9 @@ precompile(Tuple{typeof(Base.LineEdit.complete_line), Base.REPL.REPLCompletionPr precompile(Tuple{getfield(Base, Symbol("#kw##parse")), Array{Any, 1}, typeof(Base.parse), String}) precompile(Tuple{typeof(Base._rsearch), String, Array{Char, 1}, Int64}) precompile(Tuple{getfield(Base.REPLCompletions, Symbol("#kw##find_start_brace")), Array{Any, 1}, typeof(Base.REPLCompletions.find_start_brace), String}) -precompile(Tuple{typeof(Core.Inference.isbits), Tuple{Nothing, Nothing, Nothing}}) +precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{Nothing, Nothing, Nothing}}) precompile(Tuple{typeof(Base.isidentifier), Base.SubString{String}}) -precompile(Tuple{typeof(Core.Inference.isbits), Tuple{Bool, Nothing, Nothing}}) +precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{Bool, Nothing, Nothing}}) precompile(Tuple{typeof(Base.Filesystem.readdir), String}) precompile(Tuple{typeof(Base.cmp), String, Base.SubString{String}}) precompile(Tuple{typeof(Base.skip_deleted), Base.Dict{String, Nothing}, Int64}) @@ -542,8 +542,8 @@ precompile(Tuple{typeof(Base.nextind), String, UInt64}) precompile(Tuple{typeof(Base.matchall), Base.Regex, String, Bool}) precompile(Tuple{typeof(Base.skip_deleted), Base.Dict{String, String}, Int64}) precompile(Tuple{typeof(Base.reverse), String}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{Core.Inference.PartialTypeVar}}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{Core.Inference.PartialTypeVar}, Int64}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{Core.Compiler.PartialTypeVar}}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{Core.Compiler.PartialTypeVar}, Int64}) precompile(Tuple{typeof(Base.show_delim_array), Base.GenericIOBuffer{Array{UInt8, 1}}, Array{Any, 1}, Char, Char, Char, Bool, Int64, Int64}) precompile(Tuple{typeof(Base.join), Base.GenericIOBuffer{Array{UInt8, 1}}, Array{Any, 1}, String, String}) precompile(Tuple{typeof(Base.join), Base.GenericIOBuffer{Array{UInt8, 1}}, Tuple{}, String, String}) @@ -569,7 +569,7 @@ precompile(Tuple{typeof(Base.Sort.searchsortedlast), Array{String, 1}, String, I precompile(Tuple{typeof(Base.Sort.searchsorted), Array{String, 1}, String, Int64, Int64, Base.Order.ForwardOrdering}) precompile(Tuple{typeof(Base.Sort.searchsorted), Array{String, 1}, String, Base.Order.ForwardOrdering}) precompile(Tuple{typeof(Base.throw_boundserror), Array{String, 1}, Tuple{Base.UnitRange{Int64}}}) -precompile(Tuple{typeof(Core.Inference.precise_container_type), String, Core.Inference.Const, Array{Any, 1}, Core.Inference.InferenceState}) +precompile(Tuple{typeof(Core.Compiler.precise_container_type), String, Core.Compiler.Const, Array{Any, 1}, Core.Compiler.InferenceState}) precompile(Tuple{typeof(Base._rsearchindex), String, String, Int64}) precompile(Tuple{typeof(Base.deleteat!), Array{Symbol, 1}, Base.UnitRange{Int64}}) precompile(Tuple{typeof(Base.copyto!), Array{String, 1}, Base.Generator{Array{Symbol, 1}, typeof(Base.string)}}) @@ -580,7 +580,7 @@ precompile(Tuple{typeof(Base.isstructtype), DataType}) precompile(Tuple{typeof(Base.start), Array{Int64, 1}}) precompile(Tuple{typeof(Base.to_tuple_type), Type{Tuple}}) precompile(Tuple{typeof(Base.append!), Array{Any, 1}, Array{Any, 1}}) -precompile(Tuple{typeof(Core.Inference.isbits), Tuple{DataType, Bool}}) +precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{DataType, Bool}}) precompile(Tuple{typeof(Base.is_default_method), Method}) precompile(Tuple{getfield(Base, Symbol("#kw##show")), Array{Any, 1}, typeof(Base.show), Base.GenericIOBuffer{Array{UInt8, 1}}, Method}) precompile(Tuple{typeof(Base.sort!), Array{String, 1}}) @@ -627,8 +627,8 @@ precompile(Tuple{typeof(Base.LineEdit.edit_backspace), Base.GenericIOBuffer{Arra precompile(Tuple{typeof(Base.LineEdit.edit_backspace), Base.LineEdit.PromptState}) precompile(Tuple{typeof(Base.Terminals.beep), Base.Terminals.TTYTerminal}) precompile(Tuple{typeof(Base.LineEdit.edit_move_down), Base.GenericIOBuffer{Array{UInt8, 1}}}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{Core.Inference.Const, Core.Inference.Const, Core.Inference.Const, DataType}}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{Core.Inference.Const, Core.Inference.Const, Core.Inference.Const, DataType}, Int64}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{Core.Compiler.Const, Core.Compiler.Const, Core.Compiler.Const, DataType}}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{Core.Compiler.Const, Core.Compiler.Const, Core.Compiler.Const, DataType}, Int64}) precompile(Tuple{typeof(Base.LineEdit.replace_line), Base.LineEdit.PrefixSearchState, String}) precompile(Tuple{typeof(Base.LineEdit.refresh_multi_line), Base.Terminals.TerminalBuffer, Base.LineEdit.PrefixSearchState}) precompile(Tuple{typeof(Base.copy), Base.GenericIOBuffer{Array{UInt8, 1}}}) @@ -656,8 +656,8 @@ precompile(Tuple{getfield(Base.LineEdit, Symbol("#kw##refresh_multi_line")), Arr precompile(Tuple{getfield(Base.LineEdit, Symbol("#kw##refresh_multi_line")), Array{Any, 1}, typeof(Base.LineEdit.refresh_multi_line), Base.Terminals.TerminalBuffer, Base.Terminals.TTYTerminal, Base.LineEdit.PrefixSearchState}) precompile(Tuple{typeof(Base.LineEdit.replace_line), Base.LineEdit.PrefixSearchState, Base.GenericIOBuffer{Array{UInt8, 1}}}) precompile(Tuple{typeof(Base.LineEdit.accept_result), Base.LineEdit.MIState, Base.LineEdit.PrefixHistoryPrompt{Base.REPL.REPLHistoryProvider}}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{Core.Inference.Const, Core.Inference.Const, DataType}}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{Core.Inference.Const, Core.Inference.Const, DataType}, Int64}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{Core.Compiler.Const, Core.Compiler.Const, DataType}}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{Core.Compiler.Const, Core.Compiler.Const, DataType}, Int64}) precompile(Tuple{typeof(Base.LineEdit.replace_line), Base.LineEdit.PromptState, Base.GenericIOBuffer{Array{UInt8, 1}}}) precompile(Tuple{typeof(Base.LineEdit.match_input), Base.Dict{Char, Any}, Base.LineEdit.MIState, Base.GenericIOBuffer{Array{UInt8, 1}}}) precompile(Tuple{typeof(Base.LineEdit.edit_move_left), Base.GenericIOBuffer{Array{UInt8, 1}}}) @@ -694,7 +694,7 @@ precompile(Tuple{typeof(Base.print), Base.TTY, Char, Char}) precompile(Tuple{typeof(Base.rehash!), Base.Dict{String, String}, Int64}) precompile(Tuple{typeof(Base.ht_keyindex2!), Base.Dict{String, String}, String}) precompile(Tuple{typeof(Base._setindex!), Base.Dict{String, String}, String, String, Int64}) -precompile(Tuple{typeof(Core.Inference.isbits), Base.Dict{String, String}}) +precompile(Tuple{typeof(Core.Compiler.isbits), Base.Dict{String, String}}) precompile(Tuple{typeof(Base.setindex!), Base.Dict{String, String}, String, String}) precompile(Tuple{typeof(Base.Docs.symbol_latex), String}) precompile(Tuple{typeof(Base.Docs.repl_search), Base.TTY, String}) @@ -705,7 +705,7 @@ precompile(Tuple{typeof(Base.mapreduce_impl), typeof(Base.identity), typeof(Base precompile(Tuple{typeof(Base._mapreduce), typeof(Base.identity), typeof(Base.:(+)), Base.IndexLinear, Array{Int64, 1}}) precompile(Tuple{typeof(Base.popfirst!), Array{Char, 1}}) precompile(Tuple{typeof(Base.Docs.avgdistance), Array{Int64, 1}}) -precompile(Tuple{typeof(Core.Inference.isbits), Tuple{Array{String, 1}}}) +precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{Array{String, 1}}}) precompile(Tuple{typeof(Base.Docs.accessible), Module}) precompile(Tuple{Type{Base.Generator{I, F} where F where I}, typeof(Base.names), Array{Any, 1}}) precompile(Tuple{typeof(Base._collect), Array{Any, 1}, Base.Generator{Array{Any, 1}, typeof(Base.names)}, Base.EltypeUnknown, Base.HasShape}) @@ -750,9 +750,9 @@ precompile(Tuple{typeof(Base.length), Tuple{DataType, DataType}}) precompile(Tuple{Type{BoundsError}, Array{Int64, 2}, Tuple{Base.UnitRange{Int64}, Int64}}) precompile(Tuple{typeof(Base.throw_boundserror), Array{Int64, 2}, Tuple{Base.UnitRange{Int64}, Int64}}) precompile(Tuple{getfield(Base.Cartesian, Symbol("#@nexprs")), Int64, Expr}) -precompile(Tuple{typeof(Core.Inference.builtin_tfunction), typeof(===), Array{Any, 1}, Core.Inference.InferenceState, Core.Inference.InferenceParams}) -precompile(Tuple{typeof(Core.Inference.typeinf_frame), Core.MethodInstance, Bool, Bool, Core.Inference.InferenceParams}) -precompile(Tuple{typeof(Core.Inference.typeinf), Core.Inference.InferenceState}) +precompile(Tuple{typeof(Core.Compiler.builtin_tfunction), typeof(===), Array{Any, 1}, Core.Compiler.InferenceState, Core.Compiler.Params}) +precompile(Tuple{typeof(Core.Compiler.typeinf_frame), Core.MethodInstance, Bool, Bool, Core.Compiler.Params}) +precompile(Tuple{typeof(Core.Compiler.typeinf), Core.Compiler.InferenceState}) precompile(Tuple{typeof(Base.Cartesian.inlineanonymous), Expr, Int64}) precompile(Tuple{typeof(Base.Cartesian.lreplace), Expr, Symbol, Int64}) precompile(Tuple{typeof(Base.copy), Expr}) @@ -787,35 +787,35 @@ precompile(Tuple{typeof(Base.throw_boundserror), Array{Int64, 2}, Tuple{Int64, B precompile(Tuple{typeof(Base._unsafe_setindex!), Base.IndexLinear, Array{Int64, 2}, Base.UnitRange{Int64}, Int64, Base.UnitRange{Int64}}) precompile(Tuple{typeof(Base.Docs.levenshtein), String, String}) precompile(Tuple{typeof(Base.Docs.fuzzyscore), String, String}) -precompile(Tuple{typeof(Core.Inference.copy_exprargs), Array{Any, 1}}) -precompile(Tuple{typeof(Core.Inference.copy), Expr}) -precompile(Tuple{typeof(Core.Inference.copyto!), Array{Any, 1}, Core.Inference.Generator{Array{Any, 1}, typeof(Core.Inference.copy_exprs)}}) -precompile(Tuple{typeof(Core.Inference._widen_all_consts!), Expr, Array{Bool, 1}}) -precompile(Tuple{typeof(Core.Inference.promote_type), Type{Float16}, Type{Int64}}) -precompile(Tuple{typeof(Core.Inference.mk_tuplecall), Array{Any, 1}, Core.Inference.InferenceState}) -precompile(Tuple{typeof(Core.Inference.annotate_slot_load!), Expr, Array{Any, 1}, Core.Inference.InferenceState, Array{Bool, 1}}) -precompile(Tuple{typeof(Core.Inference.record_slot_assign!), Core.Inference.InferenceState}) -precompile(Tuple{typeof(Core.Inference.type_annotate!), Core.Inference.InferenceState}) -precompile(Tuple{typeof(Core.Inference.inlining_pass!), Core.Inference.InferenceState}) -precompile(Tuple{typeof(Core.Inference.alloc_elim_pass!), Core.Inference.InferenceState}) -precompile(Tuple{typeof(Core.Inference.popmeta!), Array{Any, 1}, Symbol}) -precompile(Tuple{typeof(Core.Inference.widen_all_consts!), CodeInfo}) -precompile(Tuple{typeof(Core.Inference.stupdate!), Array{Any, 1}, Array{Any, 1}}) -precompile(Tuple{typeof(Core.Inference.push!), Core.Inference.BitSet, Int64}) -precompile(Tuple{typeof(Core.Inference.abstract_eval_call), Expr, Array{Any, 1}, Core.Inference.InferenceState}) -precompile(Tuple{typeof(Core.Inference.return_type_tfunc), Array{Any, 1}, Array{Any, 1}, Core.Inference.InferenceState}) -precompile(Tuple{typeof(Core.Inference.abstract_call), typeof(===), Tuple{}, Array{Any, 1}, Array{Any, 1}, Core.Inference.InferenceState}) -precompile(Tuple{typeof(Core.Inference.abstract_call), typeof(===), Array{Any, 1}, Array{Any, 1}, Array{Any, 1}, Core.Inference.InferenceState}) -precompile(Tuple{typeof(Core.Inference.type_too_complex), TypeVar, Int64}) -precompile(Tuple{typeof(Core.Inference.abstract_eval), Expr, Array{Any, 1}, Core.Inference.InferenceState}) -precompile(Tuple{typeof(Core.Inference._setint!), Core.Inference.BitSet, Int64, Bool}) -precompile(Tuple{typeof(Core.Inference.stupdate1!), Array{Any, 1}, Core.Inference.StateUpdate}) -precompile(Tuple{typeof(Core.Inference.optimize), Core.Inference.InferenceState}) -precompile(Tuple{typeof(Core.Inference.deleteat!), Core.Inference.BitArray{1}, Core.Inference.UnitRange{Int64}}) -precompile(Tuple{typeof(Core.Inference.resize!), Core.Inference.BitArray{1}, Int64}) -precompile(Tuple{typeof(Core.Inference.copy_chunks!), Array{UInt64, 1}, Int64, Array{UInt64, 1}, Int64, Int64}) -precompile(Tuple{typeof(Core.Inference.copy_chunks_rtol!), Array{UInt64, 1}, Int64, Int64, Int64}) -precompile(Tuple{typeof(Core.Inference.find_ssavalue_uses), Array{Any, 1}}) +precompile(Tuple{typeof(Core.Compiler.copy_exprargs), Array{Any, 1}}) +precompile(Tuple{typeof(Core.Compiler.copy), Expr}) +precompile(Tuple{typeof(Core.Compiler.copyto!), Array{Any, 1}, Core.Compiler.Generator{Array{Any, 1}, typeof(Core.Compiler.copy_exprs)}}) +precompile(Tuple{typeof(Core.Compiler._widen_all_consts!), Expr, Array{Bool, 1}}) +precompile(Tuple{typeof(Core.Compiler.promote_type), Type{Float16}, Type{Int64}}) +precompile(Tuple{typeof(Core.Compiler.mk_tuplecall), Array{Any, 1}, Core.Compiler.InferenceState}) +precompile(Tuple{typeof(Core.Compiler.annotate_slot_load!), Expr, Array{Any, 1}, Core.Compiler.InferenceState, Array{Bool, 1}}) +precompile(Tuple{typeof(Core.Compiler.record_slot_assign!), Core.Compiler.InferenceState}) +precompile(Tuple{typeof(Core.Compiler.type_annotate!), Core.Compiler.InferenceState}) +precompile(Tuple{typeof(Core.Compiler.inlining_pass!), Core.Compiler.InferenceState}) +precompile(Tuple{typeof(Core.Compiler.alloc_elim_pass!), Core.Compiler.InferenceState}) +precompile(Tuple{typeof(Core.Compiler.popmeta!), Array{Any, 1}, Symbol}) +precompile(Tuple{typeof(Core.Compiler.widen_all_consts!), CodeInfo}) +precompile(Tuple{typeof(Core.Compiler.stupdate!), Array{Any, 1}, Array{Any, 1}}) +precompile(Tuple{typeof(Core.Compiler.push!), Core.Compiler.BitSet, Int64}) +precompile(Tuple{typeof(Core.Compiler.abstract_eval_call), Expr, Array{Any, 1}, Core.Compiler.InferenceState}) +precompile(Tuple{typeof(Core.Compiler.return_type_tfunc), Array{Any, 1}, Array{Any, 1}, Core.Compiler.InferenceState}) +precompile(Tuple{typeof(Core.Compiler.abstract_call), typeof(===), Tuple{}, Array{Any, 1}, Array{Any, 1}, Core.Compiler.InferenceState}) +precompile(Tuple{typeof(Core.Compiler.abstract_call), typeof(===), Array{Any, 1}, Array{Any, 1}, Array{Any, 1}, Core.Compiler.InferenceState}) +precompile(Tuple{typeof(Core.Compiler.type_too_complex), TypeVar, Int64}) +precompile(Tuple{typeof(Core.Compiler.abstract_eval), Expr, Array{Any, 1}, Core.Compiler.InferenceState}) +precompile(Tuple{typeof(Core.Compiler._setint!), Core.Compiler.BitSet, Int64, Bool}) +precompile(Tuple{typeof(Core.Compiler.stupdate1!), Array{Any, 1}, Core.Compiler.StateUpdate}) +precompile(Tuple{typeof(Core.Compiler.optimize), Core.Compiler.InferenceState}) +precompile(Tuple{typeof(Core.Compiler.deleteat!), Core.Compiler.BitArray{1}, Core.Compiler.UnitRange{Int64}}) +precompile(Tuple{typeof(Core.Compiler.resize!), Core.Compiler.BitArray{1}, Int64}) +precompile(Tuple{typeof(Core.Compiler.copy_chunks!), Array{UInt64, 1}, Int64, Array{UInt64, 1}, Int64, Int64}) +precompile(Tuple{typeof(Core.Compiler.copy_chunks_rtol!), Array{UInt64, 1}, Int64, Int64, Int64}) +precompile(Tuple{typeof(Core.Compiler.find_ssavalue_uses), Array{Any, 1}}) precompile(Tuple{typeof(Base.sortperm), Array{Tuple{Float64, Int64}, 1}}) precompile(Tuple{Type{Base.Order.Perm{O, V} where V<:(AbstractArray{T, 1} where T) where O<:Base.Order.Ordering}, Base.Order.ForwardOrdering, Array{Tuple{Float64, Int64}, 1}}) precompile(Tuple{typeof(Base.sort!), Array{Int64, 1}, Base.Sort.QuickSortAlg, Base.Order.Perm{Base.Order.ForwardOrdering, Array{Tuple{Float64, Int64}, 1}}}) @@ -895,7 +895,7 @@ precompile(Tuple{getfield(Base.Markdown, Symbol("#kw##skipwhitespace")), Array{A precompile(Tuple{typeof(Base.ht_keyindex), Base.Dict{Symbol, Base.Markdown.Config}, Symbol}) precompile(Tuple{getfield(Base.Markdown, Symbol("#kw##parse")), Array{Any, 1}, typeof(Base.Markdown.parse), Base.GenericIOBuffer{Array{UInt8, 1}}}) precompile(Tuple{getfield(Base.Markdown, Symbol("#kw##startswith")), Array{Any, 1}, typeof(Base.Markdown.startswith), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Regex}) -precompile(Tuple{typeof(Core.Inference.isbits), Tuple{Int64, Base.Regex}}) +precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{Int64, Base.Regex}}) precompile(Tuple{typeof(Base.contains), String, String}) precompile(Tuple{typeof(Base.contains), Base.SubString{String}, String}) precompile(Tuple{typeof(Base.Markdown.pushitem!), Base.Markdown.List, Base.GenericIOBuffer{Array{UInt8, 1}}}) @@ -909,8 +909,8 @@ precompile(Tuple{typeof(Base.Markdown.admonition), Base.GenericIOBuffer{Array{UI precompile(Tuple{getfield(Base.Markdown, Symbol("#kw##linecontains")), Array{Any, 1}, typeof(Base.Markdown.linecontains), Base.GenericIOBuffer{Array{UInt8, 1}}, String}) precompile(Tuple{typeof(Base.ucfirst), Base.SubString{String}}) precompile(Tuple{typeof(Base.Markdown.blocktex), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{DataType, Core.Inference.Const, DataType}}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{DataType, Core.Inference.Const, DataType}, Int64}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{DataType, Core.Compiler.Const, DataType}}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{DataType, Core.Compiler.Const, DataType}, Int64}) precompile(Tuple{getfield(Base.Markdown, Symbol("#kw##parse_inline_wrapper")), Array{Any, 1}, typeof(Base.Markdown.parse_inline_wrapper), Base.GenericIOBuffer{Array{UInt8, 1}}, String}) precompile(Tuple{typeof(Base.Markdown.startswith), Base.GenericIOBuffer{Array{UInt8, 1}}, String}) precompile(Tuple{typeof(Base.Markdown.blockinterp), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD}) @@ -926,8 +926,8 @@ precompile(Tuple{typeof(Base.pop!), Array{Base.SubString{String}, 1}}) precompile(Tuple{typeof(Base.union!), Base.Set{Char}, String}) precompile(Tuple{typeof(Base.ht_keyindex), Base.Dict{Char, Nothing}, Char}) precompile(Tuple{typeof(Base.issubset), Base.SubString{String}, Base.Set{Char}}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{Core.Inference.Const, Core.Inference.Const, Core.Inference.Const, Core.Inference.Const, Core.Inference.Const}}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{Core.Inference.Const, Core.Inference.Const, Core.Inference.Const, Core.Inference.Const, Core.Inference.Const}, Int64}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{Core.Compiler.Const, Core.Compiler.Const, Core.Compiler.Const, Core.Compiler.Const, Core.Compiler.Const}}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{Core.Compiler.Const, Core.Compiler.Const, Core.Compiler.Const, Core.Compiler.Const, Core.Compiler.Const}, Int64}) precompile(Tuple{typeof(Base.throw_boundserror), Base.SubArray{UInt8, 1, Array{UInt8, 1}, Tuple{Base.UnitRange{Int64}}, true}, Tuple{Int64}}) precompile(Tuple{typeof(Base.peek), Base.GenericIOBuffer{Base.SubArray{UInt8, 1, Array{UInt8, 1}, Tuple{Base.UnitRange{Int64}}, true}}}) precompile(Tuple{typeof(Base.Markdown.parseinline), Base.GenericIOBuffer{Base.SubArray{UInt8, 1, Array{UInt8, 1}, Tuple{Base.UnitRange{Int64}}, true}}, Base.Markdown.MD, Array{Function, 1}}) @@ -941,7 +941,7 @@ precompile(Tuple{typeof(Base.Markdown.rowlength!), Array{Base.SubString{String}, precompile(Tuple{Type{Base.Markdown.Table}, Array{Any, 1}, Nothing}) precompile(Tuple{Type{Base.Markdown.Table}, Array{Any, 1}, Array{Symbol, 1}}) precompile(Tuple{typeof(Base.Markdown.horizontalrule), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD}) -precompile(Tuple{typeof(Core.Inference.isbits), Base.Markdown.HorizontalRule}) +precompile(Tuple{typeof(Core.Compiler.isbits), Base.Markdown.HorizontalRule}) precompile(Tuple{typeof(Base.Markdown.setextheader), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD}) precompile(Tuple{typeof(Base.Markdown.paragraph), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD}) precompile(Tuple{getfield(Base.Markdown, Symbol("#kw##parse")), Array{Any, 1}, typeof(Base.Markdown.parse), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD, Base.Markdown.Config}) @@ -977,7 +977,7 @@ precompile(Tuple{typeof(Base.write), Base.Terminals.TTYTerminal, Base.SubString{ precompile(Tuple{typeof(Base.print), Base.Terminals.TTYTerminal, Base.SubString{String}}) precompile(Tuple{typeof(Base.print), Base.Terminals.TTYTerminal, Base.SubString{String}, Char}) precompile(Tuple{typeof(Base.Markdown.term), Base.Terminals.TTYTerminal, Base.Markdown.Paragraph, Int64}) -precompile(Tuple{typeof(Core.Inference.eltype), Type{Array{T, 2} where T}}) +precompile(Tuple{typeof(Core.Compiler.eltype), Type{Array{T, 2} where T}}) precompile(Tuple{getfield(Base.Markdown, Symbol("#kw##wrapped_lines")), Array{Any, 1}, typeof(Base.Markdown.wrapped_lines), String}) precompile(Tuple{typeof(Base.start), Tuple{Symbol, String}}) precompile(Tuple{typeof(Base.Markdown.terminline), Base.GenericIOBuffer{Array{UInt8, 1}}, Array{Any, 1}}) @@ -999,7 +999,7 @@ precompile(Tuple{typeof(Base.setindex!), Base.Dict{Any, Any}, Bool, Symbol}) precompile(Tuple{typeof(Base._setindex!), Base.Dict{Any, Any}, Bool, Symbol, Int64}) precompile(Tuple{typeof(Base.merge!), Base.Dict{Any, Any}, Base.Dict{Any, Any}, Base.Dict{Any, Any}}) precompile(Tuple{Type{Symbol}, Symbol}) -precompile(Tuple{typeof(Core.Inference.mk_getfield), TypedSlot, Int64, Type{String}}) +precompile(Tuple{typeof(Core.Compiler.mk_getfield), TypedSlot, Int64, Type{String}}) precompile(Tuple{getfield(Core, Symbol("#kw#Type")), Array{Any, 1}, Type{Base.Cmd}, Base.Cmd}) precompile(Tuple{typeof(Base.uv_status_string), Base.PipeEndpoint}) precompile(Tuple{typeof(Base._fd), Base.PipeEndpoint}) @@ -1061,8 +1061,8 @@ precompile(Tuple{Type{Base.TCPSocket}}) precompile(Tuple{typeof(Base.getsockname), Base.TCPSocket}) precompile(Tuple{typeof(Base.unsafe_store!), Ptr{Int32}, Int64}) precompile(Tuple{Type{Base.Multimedia.TextDisplay}, Base.PipeEndpoint}) -precompile(Tuple{typeof(Core.Inference.isbits), OutOfMemoryError}) -precompile(Tuple{typeof(Core.Inference.isbits), Tuple{Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64}}) +precompile(Tuple{typeof(Core.Compiler.isbits), OutOfMemoryError}) +precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64}}) precompile(Tuple{typeof(Base.getaddrinfo), String}) precompile(Tuple{typeof(Base.uv_status_string), Base.PipeEndpoint}) precompile(Tuple{typeof(Base._fd), Base.PipeEndpoint}) @@ -1113,12 +1113,12 @@ precompile(Tuple{typeof(Base.accept), Base.TCPServer}) precompile(Tuple{typeof(Base.tryparse_internal), Type{Int16}, Base.SubString{String}, Int64, Int64, Int64, Bool}) precompile(Tuple{typeof(Base.parse), Type{Int16}, Base.SubString{String}}) precompile(Tuple{typeof(Base.write), Base.PipeEndpoint, String}) -precompile(Tuple{typeof(Core.Inference.isbits), Tuple{Tuple{String}, Tuple{String}, Tuple{String}, Tuple{String}, Tuple{String}, Tuple{String}}}) +precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{Tuple{String}, Tuple{String}, Tuple{String}, Tuple{String}, Tuple{String}, Tuple{String}}}) precompile(Tuple{typeof(Base.cmd_gen), Tuple{Tuple{String}, Tuple{String}, Tuple{String}, Tuple{String}, Tuple{String}, Tuple{String}}}) precompile(Tuple{typeof(Base.ndigits0z), UInt8}) precompile(Tuple{typeof(Base.dec), UInt8, Int64, Bool}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{Core.Inference.Const, typeof(Type), Core.Inference.Const}}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{Core.Inference.Const, typeof(Type), Core.Inference.Const}, Int64}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{Core.Compiler.Const, typeof(Type), Core.Compiler.Const}}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{Core.Compiler.Const, typeof(Type), Core.Compiler.Const}, Int64}) precompile(Tuple{typeof(Base.rehash!), Base.Dict{Int64, Nothing}, Int64}) precompile(Tuple{typeof(Base.ht_keyindex2!), Base.Dict{Int64, Nothing}, Int64}) precompile(Tuple{typeof(Base._setindex!), Base.Dict{Int64, Nothing}, Nothing, Int64, Int64}) @@ -1172,9 +1172,9 @@ precompile(Tuple{typeof(Base.close), Base.TCPSocket}) precompile(Tuple{typeof(Base.convert), Type{Base.AbstractChannel}, Base.Channel{Any}}) precompile(Tuple{typeof(Base.ndigits0z), UInt8}) precompile(Tuple{typeof(Base.dec), UInt8, Int64, Bool}) -precompile(Tuple{typeof(Core.Inference.mk_getfield), TypedSlot, Int64, Type{Integer}}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{Core.Inference.Const, DataType, Core.Inference.Const}}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{Core.Inference.Const, DataType, Core.Inference.Const}, Int64}) +precompile(Tuple{typeof(Core.Compiler.mk_getfield), TypedSlot, Int64, Type{Integer}}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{Core.Compiler.Const, DataType, Core.Compiler.Const}}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{Core.Compiler.Const, DataType, Core.Compiler.Const}, Int64}) precompile(Tuple{typeof(Base.rehash!), Base.Dict{Int64, Nothing}, Int64}) precompile(Tuple{typeof(Base.ht_keyindex2!), Base.Dict{Int64, Nothing}, Int64}) precompile(Tuple{typeof(Base._setindex!), Base.Dict{Int64, Nothing}, Nothing, Int64, Int64}) @@ -1187,8 +1187,8 @@ precompile(Tuple{typeof(Base.unsafe_write), Base.TCPSocket, Ptr{UInt8}, UInt64}) precompile(Tuple{typeof(Base.write), Base.TCPSocket, Int64, Int64, Int64, Int64}) precompile(Tuple{typeof(Base.unsafe_write), Base.TCPSocket, Base.RefValue{Int64}, Int64}) precompile(Tuple{typeof(Base.unsafe_write), Base.TCPSocket, Base.RefValue{UInt8}, Int64}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{Core.Inference.Const, DataType, Core.Inference.Const}}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{Core.Inference.Const, DataType, Core.Inference.Const}, Int64}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{Core.Compiler.Const, DataType, Core.Compiler.Const}}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{Core.Compiler.Const, DataType, Core.Compiler.Const}, Int64}) precompile(Tuple{typeof(Base.unsafe_write), Base.TCPSocket, Base.RefValue{Int32}, Int64}) precompile(Tuple{typeof(Base.Serializer.write_as_tag), Base.TCPSocket, Int32}) precompile(Tuple{typeof(Base.copyto!), Array{Any, 1}, Base.MethodList}) @@ -1197,7 +1197,7 @@ precompile(Tuple{typeof(Base.unique), Array{Symbol, 1}}) precompile(Tuple{typeof(Base.rehash!), Base.Dict{UInt64, UInt64}, Int64}) precompile(Tuple{typeof(Base.resize!), Array{UInt64, 1}, Int64}) precompile(Tuple{typeof(Base.ht_keyindex2!), Base.Dict{UInt64, UInt64}, UInt64}) -precompile(Tuple{typeof(Core.Inference.isbits), Symbol}) +precompile(Tuple{typeof(Core.Compiler.isbits), Symbol}) precompile(Tuple{typeof(Base.isassigned), Array{Symbol, 1}, Int64}) precompile(Tuple{typeof(Base.rehash!), Base.Dict{UInt64, Nothing}, Int64}) precompile(Tuple{typeof(Base.ht_keyindex2!), Base.Dict{UInt64, Nothing}, UInt64}) @@ -1342,7 +1342,7 @@ precompile(Tuple{typeof(Base.last), Array{Int64, 1}}) precompile(Tuple{typeof(Base.LineEdit.edit_delete), Base.GenericIOBuffer{Array{UInt8, 1}}}) precompile(Tuple{typeof(Base.print), String}) precompile(Tuple{typeof(Base.vcat), Array{Int64, 1}}) -precompile(Tuple{typeof(Core.Inference.eltype), Type{Array{Int64, 1}}}) +precompile(Tuple{typeof(Core.Compiler.eltype), Type{Array{Int64, 1}}}) precompile(Tuple{typeof(Base.vcat), Int64}) precompile(Tuple{typeof(Base.uvfinalize), Base.PipeEndpoint}) precompile(Tuple{typeof(Base.unpreserve_handle), Base.PipeEndpoint}) @@ -1371,8 +1371,8 @@ precompile(Tuple{Type{Base.Process}, Base.Cmd, Ptr{Cvoid}, Base.Pipe, Base.TTY, precompile(Tuple{Type{Base.Set{Tuple{String, Float64}}}, Tuple{Tuple{String, Float64}}}) precompile(Tuple{Type{Base.VersionNumber}, Int64, Int64, Int64, Tuple{String, Int64}, Tuple{}}) precompile(Tuple{Type{Base.VersionNumber}, Int64, Int64, Int64, Tuple{String}, Tuple{Int64}}) -precompile(Tuple{Type{Core.Inference.Generator{I, F} where F where I}, Type{Core.Inference.Const}, Tuple{Symbol, Expr}}) -precompile(Tuple{Type{Core.Inference.Generator{Tuple{Symbol, Expr}, Type{Core.Inference.Const}}}, Type{Core.Inference.Const}, Tuple{Symbol, Expr}}) +precompile(Tuple{Type{Core.Compiler.Generator{I, F} where F where I}, Type{Core.Compiler.Const}, Tuple{Symbol, Expr}}) +precompile(Tuple{Type{Core.Compiler.Generator{Tuple{Symbol, Expr}, Type{Core.Compiler.Const}}}, Type{Core.Compiler.Const}, Tuple{Symbol, Expr}}) precompile(Tuple{typeof(Base.cmd_gen), Tuple{Tuple{Base.Cmd}, Tuple{String}, Tuple{String}, Tuple{String}, Tuple{String}, Tuple{String}, Tuple{String}, Tuple{String, String}, Tuple{String}, Tuple{String}}}) precompile(Tuple{typeof(Base._collect), Array{Any, 1}, Base.Generator{Array{Any, 1}, typeof(Base.FastMath.make_fastmath)}, Base.EltypeUnknown, Base.HasShape}) precompile(Tuple{typeof(Base._collect), Array{Base.Process, 1}, Base.Generator{Array{Base.Process, 1}, typeof(Base.kill)}, Base.EltypeUnknown, Base.HasShape}) @@ -1521,18 +1521,18 @@ precompile(Tuple{typeof(Base.unsafe_write), Base.Pipe, Base.RefValue{Int32}, Int precompile(Tuple{typeof(Base.unsafe_write), Base.Pipe, Base.RefValue{Int64}, Int64}) precompile(Tuple{typeof(Base.unsafe_write), Base.Pipe, Base.RefValue{UInt64}, Int64}) precompile(Tuple{typeof(Base.write), Base.Pipe, UInt64}) -precompile(Tuple{typeof(Core.Inference.collect), Type{Any}, Core.Inference.Generator{Tuple{Symbol, Expr}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference._collect), Type{Any}, Core.Inference.Generator{Tuple{Symbol, Expr}, Type{Core.Inference.Const}}, Core.Inference.HasLength}) -precompile(Tuple{typeof(Core.Inference.convert), Type{Tuple{Symbol, Expr}}, Tuple{Symbol, Expr}}) -precompile(Tuple{typeof(Core.Inference.copyto!), Array{Any, 1}, Core.Inference.Generator{Tuple{Symbol, Expr}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.done), Core.Inference.Generator{Tuple{Symbol, Expr}, Type{Core.Inference.Const}}, Int64}) -precompile(Tuple{typeof(Core.Inference.done), Tuple{Symbol, Expr}, Int64}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{Symbol, Expr}, Int64}) -precompile(Tuple{typeof(Core.Inference.isbits), Array{Module, 1}}) -precompile(Tuple{typeof(Core.Inference.length), Core.Inference.Generator{Tuple{Symbol, Expr}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{Symbol, Expr}}) -precompile(Tuple{typeof(Core.Inference.next), Core.Inference.Generator{Tuple{Symbol, Expr}, Type{Core.Inference.Const}}, Int64}) -precompile(Tuple{typeof(Core.Inference.next), Tuple{Symbol, Expr}, Int64}) -precompile(Tuple{typeof(Core.Inference.start), Core.Inference.Generator{Tuple{Symbol, Expr}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.start), Tuple{Expr, Int64}}) -precompile(Tuple{typeof(Core.Inference.start), Tuple{Symbol, Expr}}) +precompile(Tuple{typeof(Core.Compiler.collect), Type{Any}, Core.Compiler.Generator{Tuple{Symbol, Expr}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler._collect), Type{Any}, Core.Compiler.Generator{Tuple{Symbol, Expr}, Type{Core.Compiler.Const}}, Core.Compiler.HasLength}) +precompile(Tuple{typeof(Core.Compiler.convert), Type{Tuple{Symbol, Expr}}, Tuple{Symbol, Expr}}) +precompile(Tuple{typeof(Core.Compiler.copyto!), Array{Any, 1}, Core.Compiler.Generator{Tuple{Symbol, Expr}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.done), Core.Compiler.Generator{Tuple{Symbol, Expr}, Type{Core.Compiler.Const}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.done), Tuple{Symbol, Expr}, Int64}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{Symbol, Expr}, Int64}) +precompile(Tuple{typeof(Core.Compiler.isbits), Array{Module, 1}}) +precompile(Tuple{typeof(Core.Compiler.length), Core.Compiler.Generator{Tuple{Symbol, Expr}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{Symbol, Expr}}) +precompile(Tuple{typeof(Core.Compiler.next), Core.Compiler.Generator{Tuple{Symbol, Expr}, Type{Core.Compiler.Const}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.next), Tuple{Symbol, Expr}, Int64}) +precompile(Tuple{typeof(Core.Compiler.start), Core.Compiler.Generator{Tuple{Symbol, Expr}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.start), Tuple{Expr, Int64}}) +precompile(Tuple{typeof(Core.Compiler.start), Tuple{Symbol, Expr}}) diff --git a/base/promotion.jl b/base/promotion.jl index 9c2aa4c9eb78f..cafe1084b41fe 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -346,8 +346,8 @@ minmax(x::Real, y::Real) = minmax(promote(x, y)...) # operations, so it is advised against overriding them _default_type(T::Type) = (@_inline_meta; T) -if isdefined(Core, :Inference) - const _return_type = Core.Inference.return_type +if isdefined(Core, :Compiler) + const _return_type = Core.Compiler.return_type else _return_type(@nospecialize(f), @nospecialize(t)) = Any end diff --git a/base/reducedim.jl b/base/reducedim.jl index 1ca8ff3d1168b..6c5e8c864bebd 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -85,7 +85,7 @@ function reducedim_initarray0(A::AbstractArray{T}, region, f, ops) where T if prod(map(length, reduced_indices(A, region))) != 0 reducedim_initarray0_empty(A, region, f, ops) # ops over empty slice of A else - R = f == identity ? T : Core.Inference.return_type(f, (T,)) + R = f == identity ? T : Core.Compiler.return_type(f, (T,)) similar(A, R, ri) end else diff --git a/base/reflection.jl b/base/reflection.jl index b9d18ac1fb2fd..97e6ba7eca6ff 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -611,7 +611,7 @@ function code_lowered(@nospecialize(f), @nospecialize(t = Tuple), expand_generat return map(method_instances(f, t)) do m if expand_generated && isgenerated(m) if isa(m, Core.MethodInstance) - return Core.Inference.get_staged(m) + return Core.Compiler.get_staged(m) else # isa(m, Method) error("Could not expand generator for `@generated` method ", m, ". ", "This can happen if the provided argument types (", t, ") are ", @@ -749,7 +749,7 @@ function method_instances(@nospecialize(f), @nospecialize(t), world::UInt = type results = Vector{Union{Method,Core.MethodInstance}}() for method_data in _methods_by_ftype(tt, -1, world) mtypes, msp, m = method_data - instance = Core.Inference.code_for_method(m, mtypes, msp, world, false) + instance = Core.Compiler.code_for_method(m, mtypes, msp, world, false) push!(results, ifelse(isa(instance, Core.MethodInstance), instance, m)) end return results @@ -875,11 +875,11 @@ function code_typed(@nospecialize(f), @nospecialize(types=Tuple); optimize=true) types = to_tuple_type(types) asts = [] world = ccall(:jl_get_world_counter, UInt, ()) - params = Core.Inference.InferenceParams(world) + params = Core.Compiler.Params(world) for x in _methods(f, types, -1, world) meth = func_for_method_checked(x[3], types) - (_, code, ty) = Core.Inference.typeinf_code(meth, x[1], x[2], optimize, optimize, params) - code === nothing && error("inference not successful") # Inference disabled? + (_, code, ty) = Core.Compiler.typeinf_code(meth, x[1], x[2], optimize, optimize, params) + code === nothing && error("inference not successful") # inference disabled? push!(asts, uncompressed_ast(meth, code) => ty) end return asts @@ -893,11 +893,11 @@ function return_types(@nospecialize(f), @nospecialize(types=Tuple)) types = to_tuple_type(types) rt = [] world = ccall(:jl_get_world_counter, UInt, ()) - params = Core.Inference.InferenceParams(world) + params = Core.Compiler.Params(world) for x in _methods(f, types, -1, world) meth = func_for_method_checked(x[3], types) - ty = Core.Inference.typeinf_type(meth, x[1], x[2], true, params) - ty === nothing && error("inference not successful") # Inference disabled? + ty = Core.Compiler.typeinf_type(meth, x[1], x[2], true, params) + ty === nothing && error("inference not successful") # inference disabled? push!(rt, ty) end return rt diff --git a/base/repl/REPLCompletions.jl b/base/repl/REPLCompletions.jl index f76116768dc59..2e487428f12a7 100644 --- a/base/repl/REPLCompletions.jl +++ b/base/repl/REPLCompletions.jl @@ -311,8 +311,8 @@ function get_type_call(expr::Expr) length(mt) == 1 || return (Any, false) m = first(mt) # Typeinference - params = Core.Inference.InferenceParams(world) - return_type = Core.Inference.typeinf_type(m[3], m[1], m[2], true, params) + params = Core.Compiler.Params(world) + return_type = Core.Compiler.typeinf_type(m[3], m[1], m[2], true, params) return_type === nothing && return (Any, false) return (return_type, true) end diff --git a/base/replutil.jl b/base/replutil.jl index 8c48deebeb756..6b0e9eef4f946 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -470,7 +470,7 @@ function show_method_candidates(io::IO, ex::MethodError, @nospecialize kwargs=() # pool MethodErrors for these two functions. if f === convert && !isempty(arg_types_param) at1 = arg_types_param[1] - if isa(at1,DataType) && (at1::DataType).name === Type.body.name && !Core.Inference.has_free_typevars(at1) + if isa(at1,DataType) && (at1::DataType).name === Type.body.name && !Core.Compiler.has_free_typevars(at1) push!(funcs, (at1.parameters[1], arg_types_param[2:end])) end end diff --git a/base/show.jl b/base/show.jl index 233ecfdacdc37..806acefa85cf7 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1358,7 +1358,7 @@ function show_tuple_as_call(io::IO, name::Symbol, sig::Type) isdefined(uw.name.module, uw.name.mt.name) && ft == typeof(getfield(uw.name.module, uw.name.mt.name)) print(io, uw.name.mt.name) - elseif isa(ft, DataType) && ft.name === Type.body.name && !Core.Inference.has_free_typevars(ft) + elseif isa(ft, DataType) && ft.name === Type.body.name && !Core.Compiler.has_free_typevars(ft) f = ft.parameters[1] print(io, f) else diff --git a/base/sysimg.jl b/base/sysimg.jl index 066457c4a0052..c986891029cee 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -42,7 +42,7 @@ function include(path::AbstractString) end const _included_files = Array{Tuple{Module,String},1}() function _include1(mod::Module, path) - Core.Inference.push!(_included_files, (mod, ccall(:jl_prepend_cwd, Any, (Any,), path))) + Core.Compiler.push!(_included_files, (mod, ccall(:jl_prepend_cwd, Any, (Any,), path))) Core.include(mod, path) end let SOURCE_PATH = "" @@ -75,8 +75,8 @@ convert(::Type{T}, arg::T) where {T<:VecElement} = arg # init core docsystem import Core: @doc, @__doc__, @doc_str, WrappedException -if isdefined(Core, :Inference) - import Core.Inference.CoreDocs +if isdefined(Core, :Compiler) + import Core.Compiler.CoreDocs Core.atdoc!(CoreDocs.docm) end @@ -195,7 +195,7 @@ include("reshapedarray.jl") include("bitarray.jl") include("bitset.jl") -if !isdefined(Core, :Inference) +if !isdefined(Core, :Compiler) include("docs/core.jl") Core.atdoc!(CoreDocs.docm) end @@ -487,7 +487,7 @@ include("docs/basedocs.jl") include("markdown/Markdown.jl") include("docs/Docs.jl") using .Docs, .Markdown -isdefined(Core, :Inference) && Docs.loaddocs(Core.Inference.CoreDocs.DOCS) +isdefined(Core, :Compiler) && Docs.loaddocs(Core.Compiler.CoreDocs.DOCS) function __init__() # for the few uses of Crand in Base: diff --git a/base/tuple.jl b/base/tuple.jl index 15f51e2975072..c8ae221c76305 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -211,7 +211,7 @@ fill_to_length(t::Tuple{}, val, ::Val{2}) = (val, val) # constructing from an iterator # only define these in Base, to avoid overwriting the constructors -# NOTE: this means this constructor must be avoided in Inference! +# NOTE: this means this constructor must be avoided in Core.Compiler! if module_name(@__MODULE__) === :Base (::Type{T})(x::Tuple) where {T<:Tuple} = convert(T, x) # still use `convert` for tuples diff --git a/contrib/build_sysimg.jl b/contrib/build_sysimg.jl index ed1a1524e0cac..9e63e3378e712 100644 --- a/contrib/build_sysimg.jl +++ b/contrib/build_sysimg.jl @@ -69,16 +69,16 @@ function build_sysimg(sysimg_path=nothing, cpu_target="native", userimg_path=not cp(userimg_path, "userimg.jl") end try - # Start by building inference.{ji,o} - inference_path = joinpath(dirname(sysimg_path), "inference") - info("Building inference.o") - info("$julia -C $cpu_target --output-ji $inference_path.ji --output-o $inference_path.o coreimg.jl") - run(`$julia -C $cpu_target --output-ji $inference_path.ji --output-o $inference_path.o coreimg.jl`) + # Start by building basecompiler.{ji,o} + basecompiler_path = joinpath(dirname(sysimg_path), "basecompiler") + info("Building basecompiler.o") + info("$julia -C $cpu_target --output-ji $basecompiler_path.ji --output-o $basecompiler_path.o compiler/compiler.jl") + run(`$julia -C $cpu_target --output-ji $basecompiler_path.ji --output-o $basecompiler_path.o compiler/compiler.jl`) # Bootstrap off of that to create sys.{ji,o} info("Building sys.o") - info("$julia -C $cpu_target --output-ji $sysimg_path.ji --output-o $sysimg_path.o -J $inference_path.ji --startup-file=no sysimg.jl") - run(`$julia -C $cpu_target --output-ji $sysimg_path.ji --output-o $sysimg_path.o -J $inference_path.ji --startup-file=no sysimg.jl`) + info("$julia -C $cpu_target --output-ji $sysimg_path.ji --output-o $sysimg_path.o -J $basecompiler_path.ji --startup-file=no sysimg.jl") + run(`$julia -C $cpu_target --output-ji $sysimg_path.ji --output-o $sysimg_path.o -J $basecompiler_path.ji --startup-file=no sysimg.jl`) if cc !== nothing link_sysimg(sysimg_path, cc, debug) diff --git a/doc/src/devdocs/debuggingtips.md b/doc/src/devdocs/debuggingtips.md index f00666c5fa09f..a5f4ce9f74806 100644 --- a/doc/src/devdocs/debuggingtips.md +++ b/doc/src/devdocs/debuggingtips.md @@ -11,7 +11,7 @@ Within `gdb`, any `jl_value_t*` object `obj` can be displayed using The object will be displayed in the `julia` session, not in the gdb session. This is a useful way to discover the types and values of objects being manipulated by Julia's C code. -Similarly, if you're debugging some of Julia's internals (e.g., `inference.jl`), you can print +Similarly, if you're debugging some of Julia's internals (e.g., `compiler.jl`), you can print `obj` using ```julia diff --git a/doc/src/devdocs/eval.md b/doc/src/devdocs/eval.md index 93c69dc5e5db6..3922ef6235ada 100644 --- a/doc/src/devdocs/eval.md +++ b/doc/src/devdocs/eval.md @@ -83,7 +83,7 @@ although it can also be invoked directly by a call to [`macroexpand()`](@ref)/`j ## [Type Inference](@id dev-type-inference) -Type inference is implemented in Julia by [`typeinf()` in `inference.jl`](https://github.com/JuliaLang/julia/blob/master/base/inference.jl). +Type inference is implemented in Julia by [`typeinf()` in `compiler/typeinf.jl`](https://github.com/JuliaLang/julia/blob/master/base/compiler/typeinf.jl). Type inference is the process of examining a Julia function and determining bounds for the types of each of its variables, as well as bounds on the type of the return value from the function. This enables many future optimizations, such as unboxing of known immutable values, and compile-time diff --git a/doc/src/devdocs/inference.md b/doc/src/devdocs/inference.md index 1cc4b29dab667..28f624bbbd864 100644 --- a/doc/src/devdocs/inference.md +++ b/doc/src/devdocs/inference.md @@ -9,12 +9,12 @@ posts ([1](https://juliacomputing.com/blog/2016/04/04/inference-convergence.html), [2](https://juliacomputing.com/blog/2017/05/15/inference-converage2.html)). -## Debugging inference.jl +## Debugging compiler.jl -You can start a Julia session, edit `inference.jl` (for example to -insert `print` statements), and then replace `Core.Inference` in your -running session by navigating to `base/` and executing -`include("coreimg.jl")`. This trick typically leads to much faster +You can start a Julia session, edit `compiler/*.jl` (for example to +insert `print` statements), and then replace `Core.Compiler` in your +running session by navigating to `base/compiler` and executing +`include("compiler.jl")`. This trick typically leads to much faster development than if you rebuild Julia for each change. A convenient entry point into inference is `typeinf_code`. Here's a @@ -27,16 +27,16 @@ mths = methods(convert, atypes) # worth checking that there is only one m = first(mths) # Create variables needed to call `typeinf_code` -params = Core.Inference.InferenceParams(typemax(UInt)) # parameter is the world age, +params = Core.Compiler.Params(typemax(UInt)) # parameter is the world age, # typemax(UInt) -> most recent sparams = Core.svec() # this particular method doesn't have type-parameters optimize = true # run all inference optimizations cached = false # force inference to happen (do not use cached results) -Core.Inference.typeinf_code(m, atypes, sparams, optimize, cached, params) +Core.Compiler.typeinf_code(m, atypes, sparams, optimize, cached, params) ``` If your debugging adventures require a `MethodInstance`, you can look it up by -calling `Core.Inference.code_for_method` using many of the variables above. +calling `Core.Compiler.code_for_method` using many of the variables above. A `CodeInfo` object may be obtained with ```julia # Returns the CodeInfo object for `convert(Int, ::UInt)`: @@ -84,7 +84,7 @@ input and output types were inferred in advance) is assigned a fixed cost (currently 20 cycles). In contrast, a `:call` expression, for functions other than intrinsics/builtins, indicates that the call will require dynamic dispatch, in which case we assign a cost set by -`InferenceParams.inline_nonleaf_penalty` (currently set at 1000). Note +`Params.inline_nonleaf_penalty` (currently set at 1000). Note that this is not a "first-principles" estimate of the raw cost of dynamic dispatch, but a mere heuristic indicating that dynamic dispatch is extremely expensive. @@ -93,11 +93,11 @@ Each statement gets analyzed for its total cost in a function called `statement_cost`. You can run this yourself by following this example: ```julia -params = Core.Inference.InferenceParams(typemax(UInt)) +params = Core.Compiler.Params(typemax(UInt)) # Get the CodeInfo object ci = (@code_typed fill(3, (5, 5)))[1] # we'll try this on the code for `fill(3, (5, 5))` # Calculate cost of each statement -cost(stmt) = Core.Inference.statement_cost(stmt, ci, Base, params) +cost(stmt) = Core.Compiler.statement_cost(stmt, ci, Base, params) cst = map(cost, ci.code) ``` diff --git a/doc/src/manual/metaprogramming.md b/doc/src/manual/metaprogramming.md index 16bff6d77d5ab..b607bfbcd2ed4 100644 --- a/doc/src/manual/metaprogramming.md +++ b/doc/src/manual/metaprogramming.md @@ -1248,7 +1248,7 @@ run during inference, it must respect all of the limitations of that code. Some operations that should not be attempted include: 1. Caching of native pointers. -2. Interacting with the contents or methods of Core.Inference in any way. +2. Interacting with the contents or methods of Core.Compiler in any way. 3. Observing any mutable state. * Inference on the generated function may be run at *any* time, including while your code is attempting diff --git a/src/common_symbols2.inc b/src/common_symbols2.inc index e41e64d8d73c3..d0ec352a7b8f6 100644 --- a/src/common_symbols2.inc +++ b/src/common_symbols2.inc @@ -97,7 +97,7 @@ jl_symbol("char.jl"), jl_symbol("val"), jl_symbol("value"), jl_symbol("id"), -jl_symbol("inference.jl"), +jl_symbol("compiler.jl"), jl_symbol("io"), jl_symbol("kwfunc"), jl_symbol("Symbol"), diff --git a/stdlib/Distributed/src/precompile.jl b/stdlib/Distributed/src/precompile.jl index 9f0bdca57eced..f5c7f64d22dad 100644 --- a/stdlib/Distributed/src/precompile.jl +++ b/stdlib/Distributed/src/precompile.jl @@ -1,7 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license precompile(Tuple{typeof(Base.empty!), Base.Dict{Int64, Union{Distributed.Worker, Distributed.LocalProcess}}}) -precompile(Tuple{typeof(Core.Inference.isbits), Distributed.DefaultClusterManager}) +precompile(Tuple{typeof(Core.Compiler.isbits), Distributed.DefaultClusterManager}) precompile(Tuple{typeof(Distributed.init_worker), String, Distributed.DefaultClusterManager}) precompile(Tuple{typeof(Distributed.local_remotecall_thunk), typeof(Distributed.set_valid_processes), Tuple{Array{Int64, 1}}, Array{Any, 1}}) precompile(Tuple{typeof(Distributed.remote_do), typeof(Distributed.set_valid_processes), Distributed.Worker, Array{Int64, 1}}) @@ -54,24 +54,24 @@ precompile(Tuple{typeof(Distributed.worker_id_from_socket), Base.TCPSocket}) precompile(Tuple{Type{Distributed.ClusterSerializer{Base.TCPSocket}}, Base.TCPSocket}) precompile(Tuple{typeof(Distributed.send_msg_), Distributed.Worker, Distributed.MsgHeader, Distributed.ResultMsg, Bool}) precompile(Tuple{typeof(Distributed.send_msg_now), Distributed.Worker, Distributed.MsgHeader, Distributed.ResultMsg}) -precompile(Tuple{Type{Core.Inference.Generator{I, F} where F where I}, Type{Core.Inference.Const}, Tuple{Int64, typeof(Distributed.rmprocs)}}) -precompile(Tuple{Type{Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Inference.Const}}}, Type{Core.Inference.Const}, Tuple{Int64, typeof(Distributed.rmprocs)}}) -precompile(Tuple{typeof(Core.Inference.convert), Type{Tuple{Int64, typeof(Distributed.rmprocs)}}, Tuple{Int64, typeof(Distributed.rmprocs)}}) -precompile(Tuple{typeof(Core.Inference.collect), Type{Any}, Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference._collect), Type{Any}, Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Inference.Const}}, Core.Inference.HasLength}) -precompile(Tuple{typeof(Core.Inference.length), Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{Int64, typeof(Distributed.rmprocs)}}) -precompile(Tuple{typeof(Core.Inference.copyto!), Array{Any, 1}, Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.start), Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.start), Tuple{Int64, typeof(Distributed.rmprocs)}}) -precompile(Tuple{typeof(Core.Inference.done), Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Inference.Const}}, Int64}) -precompile(Tuple{typeof(Core.Inference.done), Tuple{Int64, typeof(Distributed.rmprocs)}, Int64}) -precompile(Tuple{typeof(Core.Inference.next), Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Inference.Const}}, Int64}) -precompile(Tuple{typeof(Core.Inference.next), Tuple{Int64, typeof(Distributed.rmprocs)}, Int64}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{Int64, typeof(Distributed.rmprocs)}, Int64}) -precompile(Tuple{typeof(Core.Inference.start), Tuple{typeof(Distributed.rmprocs), Int64}}) -precompile(Tuple{typeof(Core.Inference.indexed_next), Tuple{typeof(Distributed.rmprocs), Int64}, Int64, Int64}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{typeof(Distributed.rmprocs), Int64}, Int64}) +precompile(Tuple{Type{Core.Compiler.Generator{I, F} where F where I}, Type{Core.Compiler.Const}, Tuple{Int64, typeof(Distributed.rmprocs)}}) +precompile(Tuple{Type{Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Compiler.Const}}}, Type{Core.Compiler.Const}, Tuple{Int64, typeof(Distributed.rmprocs)}}) +precompile(Tuple{typeof(Core.Compiler.convert), Type{Tuple{Int64, typeof(Distributed.rmprocs)}}, Tuple{Int64, typeof(Distributed.rmprocs)}}) +precompile(Tuple{typeof(Core.Compiler.collect), Type{Any}, Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler._collect), Type{Any}, Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Compiler.Const}}, Core.Compiler.HasLength}) +precompile(Tuple{typeof(Core.Compiler.length), Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{Int64, typeof(Distributed.rmprocs)}}) +precompile(Tuple{typeof(Core.Compiler.copyto!), Array{Any, 1}, Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.start), Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.start), Tuple{Int64, typeof(Distributed.rmprocs)}}) +precompile(Tuple{typeof(Core.Compiler.done), Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Compiler.Const}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.done), Tuple{Int64, typeof(Distributed.rmprocs)}, Int64}) +precompile(Tuple{typeof(Core.Compiler.next), Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Compiler.Const}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.next), Tuple{Int64, typeof(Distributed.rmprocs)}, Int64}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{Int64, typeof(Distributed.rmprocs)}, Int64}) +precompile(Tuple{typeof(Core.Compiler.start), Tuple{typeof(Distributed.rmprocs), Int64}}) +precompile(Tuple{typeof(Core.Compiler.indexed_next), Tuple{typeof(Distributed.rmprocs), Int64}, Int64, Int64}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{typeof(Distributed.rmprocs), Int64}, Int64}) precompile(Tuple{typeof(Distributed.register_worker_streams), Distributed.Worker}) precompile(Tuple{typeof(Distributed.register_worker_streams), Distributed.LocalProcess}) precompile(Tuple{Type{Distributed.ClusterSerializer{Base.TCPSocket}}, Base.TCPSocket}) @@ -81,15 +81,15 @@ precompile(Tuple{typeof(Base.convert), Type{Distributed.ClusterManager}, Distrib precompile(Tuple{typeof(Base.convert), Type{Distributed.WorkerConfig}, Distributed.WorkerConfig}) precompile(Tuple{typeof(Base.get), Base.Dict{Any, Any}, Distributed.RRID, Bool}) precompile(Tuple{typeof(Base.ht_keyindex), Base.Dict{Any, Any}, Distributed.RRID}) -precompile(Tuple{typeof(Core.Inference.isbits), Tuple{Int64, typeof(Distributed.rmprocs)}}) -precompile(Tuple{Type{Core.Inference.Generator{I, F} where F where I}, Type{QuoteNode}, Tuple{Int64, typeof(Distributed.rmprocs)}}) -precompile(Tuple{Type{Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}}, Type{QuoteNode}, Tuple{Int64, typeof(Distributed.rmprocs)}}) -precompile(Tuple{typeof(Core.Inference._collect), Type{Any}, Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}, Core.Inference.HasLength}) -precompile(Tuple{typeof(Core.Inference.length), Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}}) -precompile(Tuple{typeof(Core.Inference.copyto!), Array{Any, 1}, Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}}) -precompile(Tuple{typeof(Core.Inference.start), Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}}) -precompile(Tuple{typeof(Core.Inference.done), Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}, Int64}) -precompile(Tuple{typeof(Core.Inference.next), Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{Int64, typeof(Distributed.rmprocs)}}) +precompile(Tuple{Type{Core.Compiler.Generator{I, F} where F where I}, Type{QuoteNode}, Tuple{Int64, typeof(Distributed.rmprocs)}}) +precompile(Tuple{Type{Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}}, Type{QuoteNode}, Tuple{Int64, typeof(Distributed.rmprocs)}}) +precompile(Tuple{typeof(Core.Compiler._collect), Type{Any}, Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}, Core.Compiler.HasLength}) +precompile(Tuple{typeof(Core.Compiler.length), Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}}) +precompile(Tuple{typeof(Core.Compiler.copyto!), Array{Any, 1}, Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}}) +precompile(Tuple{typeof(Core.Compiler.start), Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}}) +precompile(Tuple{typeof(Core.Compiler.done), Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.next), Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}, Int64}) precompile(Tuple{typeof(Distributed.local_remotecall_thunk), typeof(Distributed.rmprocs), Tuple{Int64}, Array{Any, 1}}) precompile(Tuple{typeof(Distributed.remote_do), typeof(Distributed.rmprocs), Distributed.Worker, Int64}) precompile(Tuple{typeof(Distributed.remote_do), typeof(Distributed.rmprocs), Distributed.LocalProcess, Int64}) @@ -140,39 +140,39 @@ precompile(Tuple{typeof(Base.Serializer.serialize_cycle_header), Distributed.Clu precompile(Tuple{typeof(Base.Serializer.serialize_any), Distributed.ClusterSerializer{Base.TCPSocket}, Type{Union{Tuple{Any, Int64}, Tuple{Tuple{}, Any, Bool}}}}) precompile(Tuple{typeof(Distributed.send_msg_), Distributed.Worker, Distributed.MsgHeader, Distributed.ResultMsg, Bool}) precompile(Tuple{typeof(Distributed.send_msg_now), Distributed.Worker, Distributed.MsgHeader, Distributed.ResultMsg}) -precompile(Tuple{Type{Core.Inference.Generator{I, F} where F where I}, Type{Core.Inference.Const}, Tuple{Int64, typeof(Distributed.rmprocs)}}) -precompile(Tuple{Type{Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Inference.Const}}}, Type{Core.Inference.Const}, Tuple{Int64, typeof(Distributed.rmprocs)}}) -precompile(Tuple{typeof(Core.Inference.convert), Type{Tuple{Int64, typeof(Distributed.rmprocs)}}, Tuple{Int64, typeof(Distributed.rmprocs)}}) -precompile(Tuple{typeof(Core.Inference.collect), Type{Any}, Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference._collect), Type{Any}, Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Inference.Const}}, Core.Inference.HasLength}) -precompile(Tuple{typeof(Core.Inference.length), Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.length), Tuple{Int64, typeof(Distributed.rmprocs)}}) -precompile(Tuple{typeof(Core.Inference.copyto!), Array{Any, 1}, Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.start), Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Inference.Const}}}) -precompile(Tuple{typeof(Core.Inference.start), Tuple{Int64, typeof(Distributed.rmprocs)}}) -precompile(Tuple{typeof(Core.Inference.done), Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Inference.Const}}, Int64}) -precompile(Tuple{typeof(Core.Inference.done), Tuple{Int64, typeof(Distributed.rmprocs)}, Int64}) -precompile(Tuple{typeof(Core.Inference.next), Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Inference.Const}}, Int64}) -precompile(Tuple{typeof(Core.Inference.next), Tuple{Int64, typeof(Distributed.rmprocs)}, Int64}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{Int64, typeof(Distributed.rmprocs)}, Int64}) -precompile(Tuple{typeof(Core.Inference.start), Tuple{typeof(Distributed.rmprocs), Int64}}) -precompile(Tuple{typeof(Core.Inference.indexed_next), Tuple{typeof(Distributed.rmprocs), Int64}, Int64, Int64}) -precompile(Tuple{typeof(Core.Inference.getindex), Tuple{typeof(Distributed.rmprocs), Int64}, Int64}) +precompile(Tuple{Type{Core.Compiler.Generator{I, F} where F where I}, Type{Core.Compiler.Const}, Tuple{Int64, typeof(Distributed.rmprocs)}}) +precompile(Tuple{Type{Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Compiler.Const}}}, Type{Core.Compiler.Const}, Tuple{Int64, typeof(Distributed.rmprocs)}}) +precompile(Tuple{typeof(Core.Compiler.convert), Type{Tuple{Int64, typeof(Distributed.rmprocs)}}, Tuple{Int64, typeof(Distributed.rmprocs)}}) +precompile(Tuple{typeof(Core.Compiler.collect), Type{Any}, Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler._collect), Type{Any}, Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Compiler.Const}}, Core.Compiler.HasLength}) +precompile(Tuple{typeof(Core.Compiler.length), Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.length), Tuple{Int64, typeof(Distributed.rmprocs)}}) +precompile(Tuple{typeof(Core.Compiler.copyto!), Array{Any, 1}, Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.start), Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Compiler.Const}}}) +precompile(Tuple{typeof(Core.Compiler.start), Tuple{Int64, typeof(Distributed.rmprocs)}}) +precompile(Tuple{typeof(Core.Compiler.done), Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Compiler.Const}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.done), Tuple{Int64, typeof(Distributed.rmprocs)}, Int64}) +precompile(Tuple{typeof(Core.Compiler.next), Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{Core.Compiler.Const}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.next), Tuple{Int64, typeof(Distributed.rmprocs)}, Int64}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{Int64, typeof(Distributed.rmprocs)}, Int64}) +precompile(Tuple{typeof(Core.Compiler.start), Tuple{typeof(Distributed.rmprocs), Int64}}) +precompile(Tuple{typeof(Core.Compiler.indexed_next), Tuple{typeof(Distributed.rmprocs), Int64}, Int64, Int64}) +precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{typeof(Distributed.rmprocs), Int64}, Int64}) precompile(Tuple{typeof(Base.Serializer.deserialize), Distributed.ClusterSerializer{Base.TCPSocket}}) precompile(Tuple{typeof(Base.Serializer.deserialize_cycle), Distributed.ClusterSerializer{Base.TCPSocket}, Expr}) precompile(Tuple{typeof(Base.Serializer.handle_deserialize), Distributed.ClusterSerializer{Base.TCPSocket}, Int32}) precompile(Tuple{typeof(Base.Serializer.deserialize_array), Distributed.ClusterSerializer{Base.TCPSocket}}) precompile(Tuple{typeof(Base.Serializer.deserialize_datatype), Distributed.ClusterSerializer{Base.TCPSocket}}) precompile(Tuple{typeof(Base.Serializer.deserialize_expr), Distributed.ClusterSerializer{Base.TCPSocket}, Int64}) -precompile(Tuple{typeof(Core.Inference.isbits), Tuple{Int64, typeof(Distributed.rmprocs)}}) -precompile(Tuple{Type{Core.Inference.Generator{I, F} where F where I}, Type{QuoteNode}, Tuple{Int64, typeof(Distributed.rmprocs)}}) -precompile(Tuple{Type{Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}}, Type{QuoteNode}, Tuple{Int64, typeof(Distributed.rmprocs)}}) -precompile(Tuple{typeof(Core.Inference._collect), Type{Any}, Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}, Core.Inference.HasLength}) -precompile(Tuple{typeof(Core.Inference.length), Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}}) -precompile(Tuple{typeof(Core.Inference.copyto!), Array{Any, 1}, Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}}) -precompile(Tuple{typeof(Core.Inference.start), Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}}) -precompile(Tuple{typeof(Core.Inference.done), Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}, Int64}) -precompile(Tuple{typeof(Core.Inference.next), Core.Inference.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{Int64, typeof(Distributed.rmprocs)}}) +precompile(Tuple{Type{Core.Compiler.Generator{I, F} where F where I}, Type{QuoteNode}, Tuple{Int64, typeof(Distributed.rmprocs)}}) +precompile(Tuple{Type{Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}}, Type{QuoteNode}, Tuple{Int64, typeof(Distributed.rmprocs)}}) +precompile(Tuple{typeof(Core.Compiler._collect), Type{Any}, Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}, Core.Compiler.HasLength}) +precompile(Tuple{typeof(Core.Compiler.length), Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}}) +precompile(Tuple{typeof(Core.Compiler.copyto!), Array{Any, 1}, Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}}) +precompile(Tuple{typeof(Core.Compiler.start), Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}}) +precompile(Tuple{typeof(Core.Compiler.done), Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}, Int64}) +precompile(Tuple{typeof(Core.Compiler.next), Core.Compiler.Generator{Tuple{Int64, typeof(Distributed.rmprocs)}, Type{QuoteNode}}, Int64}) precompile(Tuple{typeof(Distributed.local_remotecall_thunk), typeof(Distributed.rmprocs), Tuple{Int64}, Array{Any, 1}}) precompile(Tuple{typeof(Distributed.remote_do), typeof(Distributed.rmprocs), Distributed.Worker, Int64}) precompile(Tuple{typeof(Distributed.remote_do), typeof(Distributed.rmprocs), Distributed.LocalProcess, Int64}) diff --git a/test/ambiguous.jl b/test/ambiguous.jl index 1654f88360215..c3ba4b34e58c0 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -269,10 +269,10 @@ end # TODO: review this list and remove everything between test_broken and test let need_to_handle_undef_sparam = Set{Method}(detect_unbound_args(Core; recursive=true)) - pop!(need_to_handle_undef_sparam, which(Core.Inference.eltype, Tuple{Type{Tuple{Any}}})) + pop!(need_to_handle_undef_sparam, which(Core.Compiler.eltype, Tuple{Type{Tuple{Any}}})) @test_broken need_to_handle_undef_sparam == Set() - pop!(need_to_handle_undef_sparam, which(Core.Inference.cat, Tuple{Any, AbstractArray})) - pop!(need_to_handle_undef_sparam, first(methods(Core.Inference.same_names))) + pop!(need_to_handle_undef_sparam, which(Core.Compiler.cat, Tuple{Any, AbstractArray})) + pop!(need_to_handle_undef_sparam, first(methods(Core.Compiler.same_names))) @test need_to_handle_undef_sparam == Set() end let need_to_handle_undef_sparam = diff --git a/test/choosetests.jl b/test/choosetests.jl index 2c9ec6a42d640..92d7d30c2c130 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -33,7 +33,7 @@ in the `choices` argument: """ -> function choosetests(choices = []) testnames = [ - "linalg", "subarray", "core", "inference", "worlds", + "linalg", "subarray", "core", "compiler", "worlds", "keywordargs", "numbers", "subtype", "char", "strings", "triplequote", "unicode", "intrinsics", "dict", "hashing", "iobuffer", "staged", "offsetarray", @@ -53,7 +53,7 @@ function choosetests(choices = []) "enums", "cmdlineargs", "i18n", "int", "checked", "bitset", "floatfuncs", "compile", "inline", "boundscheck", "error", "ambiguous", "cartesian", "asmvariant", "osutils", - "channels", "iostream", "specificity", "codegen", "codevalidation", + "channels", "iostream", "specificity", "codegen", "reinterpretarray", "syntax", "logging", "missing", "asyncmap" ] @@ -128,6 +128,16 @@ function choosetests(choices = []) prepend!(tests, linalgtests) end + compilertests = ["compiler/compiler", "compiler/validation"] + + if "compiler" in skip_tests + filter!(x -> (x != "compiler" && !(x in compilertests)), tests) + elseif "compiler" in tests + # specifically selected case + filter!(x -> x != "compiler", tests) + prepend!(tests, compilertests) + end + net_required_for = ["socket", "stdlib", "libgit2"] net_on = true try diff --git a/test/inference.jl b/test/compiler/compiler.jl similarity index 94% rename from test/inference.jl rename to test/compiler/compiler.jl index ffdede50d5b90..ea3a3a6c4da43 100644 --- a/test/inference.jl +++ b/test/compiler/compiler.jl @@ -1,21 +1,21 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -# tests for Core.Inference correctness and precision -import Core.Inference: Const, Conditional, ⊑ -const isleaftype = Core.Inference._isleaftype +# tests for Core.Compiler correctness and precision +import Core.Compiler: Const, Conditional, ⊑ +const isleaftype = Core.Compiler._isleaftype using Random # demonstrate some of the type-size limits -@test Core.Inference.limit_type_size(Ref{Complex{T} where T}, Ref, Ref, 0) == Ref -@test Core.Inference.limit_type_size(Ref{Complex{T} where T}, Ref{Complex{T} where T}, Ref, 0) == Ref{Complex{T} where T} +@test Core.Compiler.limit_type_size(Ref{Complex{T} where T}, Ref, Ref, 0) == Ref +@test Core.Compiler.limit_type_size(Ref{Complex{T} where T}, Ref{Complex{T} where T}, Ref, 0) == Ref{Complex{T} where T} let comparison = Tuple{X, X} where X<:Tuple sig = Tuple{X, X} where X<:comparison ref = Tuple{X, X} where X - @test Core.Inference.limit_type_size(sig, comparison, comparison, 10) == comparison - @test Core.Inference.limit_type_size(sig, ref, comparison, 10) == comparison - @test Core.Inference.limit_type_size(Tuple{sig}, Tuple{ref}, comparison, 10) == Tuple{comparison} - @test Core.Inference.limit_type_size(sig, ref, Tuple{comparison}, 10) == sig + @test Core.Compiler.limit_type_size(sig, comparison, comparison, 10) == comparison + @test Core.Compiler.limit_type_size(sig, ref, comparison, 10) == comparison + @test Core.Compiler.limit_type_size(Tuple{sig}, Tuple{ref}, comparison, 10) == Tuple{comparison} + @test Core.Compiler.limit_type_size(sig, ref, Tuple{comparison}, 10) == sig end @@ -121,9 +121,9 @@ barTuple2() = fooTuple{tuple(:y)}() @test Base.return_types(barTuple1,Tuple{})[1] == Base.return_types(barTuple2,Tuple{})[1] == fooTuple{(:y,)} # issue #6050 -@test Core.Inference.getfield_tfunc( +@test Core.Compiler.getfield_tfunc( Dict{Int64,Tuple{UnitRange{Int64},UnitRange{Int64}}}, - Core.Inference.Const(:vals)) == Array{Tuple{UnitRange{Int64},UnitRange{Int64}},1} + Core.Compiler.Const(:vals)) == Array{Tuple{UnitRange{Int64},UnitRange{Int64}},1} # issue #12476 function f12476(a) @@ -396,7 +396,7 @@ f18450() = ifelse(true, Tuple{Vararg{Int}}, Tuple{Vararg}) @test f18450() == Tuple{Vararg{Int}} # issue #18569 -@test !Core.Inference.isconstType(Type{Tuple}) +@test !Core.Compiler.isconstType(Type{Tuple}) # ensure pure attribute applies correctly to all signatures of fpure Base.@pure function fpure(a=rand(); b=rand()) @@ -493,7 +493,7 @@ for codetype in Any[ @code_typed(g18679()), @code_typed(h18679()), @code_typed(g19348((1, 2.0)))] - # make sure none of the slottypes are left as Core.Inference.Const objects + # make sure none of the slottypes are left as Core.Compiler.Const objects code = codetype[1] @test all(x->isa(x, Type), code.slottypes) local notconst(@nospecialize(other)) = true @@ -557,7 +557,7 @@ end # Issue 19641 foo19641() = let a = 1.0 - Core.Inference.return_type(x -> x + a, Tuple{Float64}) + Core.Compiler.return_type(x -> x + a, Tuple{Float64}) end @inferred foo19641() @@ -698,12 +698,12 @@ f20267(x::T20267{T}, y::T) where (T) = f20267(Any[1][1], x.inds) # issue #20615 let A = 1:2, z = zip(A, A, A, A, A, A, A, A, A, A, A, A) - @test z isa Core.Inference.limit_type_depth(typeof(z), 0) + @test z isa Core.Compiler.limit_type_depth(typeof(z), 0) @test start(z) == (1, (1, (1, (1, (1, (1, (1, (1, (1, (1, (1, 1))))))))))) end # introduce TypeVars in Unions in invariant position let T = Val{Val{Val{Union{Int8,Int16,Int32,Int64,UInt8,UInt16,UInt32,UInt64}}}} - @test T <: Core.Inference.limit_type_depth(T, 0) + @test T <: Core.Compiler.limit_type_depth(T, 0) end # issue #20704 @@ -752,19 +752,19 @@ test_no_apply(::Any) = true # issue #20033 # check return_type_tfunc for calls where no method matches -bcast_eltype_20033(f, A) = Core.Inference.return_type(f, Tuple{eltype(A)}) +bcast_eltype_20033(f, A) = Core.Compiler.return_type(f, Tuple{eltype(A)}) err20033(x::Float64...) = prod(x) @test bcast_eltype_20033(err20033, [1]) === Union{} @test Base.return_types(bcast_eltype_20033, (typeof(err20033), Vector{Int},)) == Any[Type{Union{}}] # return_type on builtins -@test Core.Inference.return_type(tuple, Tuple{Int,Int8,Int}) === Tuple{Int,Int8,Int} +@test Core.Compiler.return_type(tuple, Tuple{Int,Int8,Int}) === Tuple{Int,Int8,Int} # issue #21088 -@test Core.Inference.return_type(typeof, Tuple{Int}) == Type{Int} +@test Core.Compiler.return_type(typeof, Tuple{Int}) == Type{Int} # Inference of constant svecs @eval fsvecinf() = $(QuoteNode(Core.svec(Tuple{Int,Int}, Int)))[1] -@test Core.Inference.return_type(fsvecinf, Tuple{}) == Type{Tuple{Int,Int}} +@test Core.Compiler.return_type(fsvecinf, Tuple{}) == Type{Tuple{Int,Int}} # nfields tfunc on `DataType` let f = ()->Val{nfields(DataType[Int][1])} @@ -856,14 +856,14 @@ for A in (1,) end # issue #21848 -@test Core.Inference.limit_type_depth(Ref{Complex{T} where T}, 0) == Ref +@test Core.Compiler.limit_type_depth(Ref{Complex{T} where T}, 0) == Ref let T = Tuple{Tuple{Int64, Nothing}, Tuple{Tuple{Int64, Nothing}, Tuple{Int64, Tuple{Tuple{Int64, Nothing}, Tuple{Tuple{Int64, Nothing}, Tuple{Int64, Tuple{Tuple{Int64, Nothing}, Tuple{Tuple, Tuple}}}}}}}} - @test Core.Inference.limit_type_depth(T, 0) >: T - @test Core.Inference.limit_type_depth(T, 1) >: T - @test Core.Inference.limit_type_depth(T, 2) >: T + @test Core.Compiler.limit_type_depth(T, 0) >: T + @test Core.Compiler.limit_type_depth(T, 1) >: T + @test Core.Compiler.limit_type_depth(T, 2) >: T end # Issue #20902, check that this doesn't error. @@ -926,7 +926,7 @@ let f(x) = isdefined(x, :NonExistentField) ? 1 : "" @test Base.return_types(f, (ComplexF32,)) == Any[String] @test Union{Int,String} <: Base.return_types(f, (AbstractArray,))[1] end -import Core.Inference: isdefined_tfunc +import Core.Compiler: isdefined_tfunc @test isdefined_tfunc(ComplexF32, Const(())) === Union{} @test isdefined_tfunc(ComplexF32, Const(1)) === Const(true) @test isdefined_tfunc(ComplexF32, Const(2)) === Const(true) @@ -973,10 +973,10 @@ end # issue #22875 typeargs = (Type{Int},) -@test Base.Core.Inference.return_type((args...) -> one(args...), typeargs) === Int +@test Base.Core.Compiler.return_type((args...) -> one(args...), typeargs) === Int typeargs = (Type{Int},Type{Int},Type{Int},Type{Int},Type{Int},Type{Int}) -@test Base.Core.Inference.return_type(promote_type, typeargs) === Type{Int} +@test Base.Core.Compiler.return_type(promote_type, typeargs) === Type{Int} # demonstrate that inference must converge # while doing constant propagation @@ -1066,7 +1066,7 @@ function test_const_return(@nospecialize(f), @nospecialize(t), @nospecialize(val continue elseif isa(ex, Expr) ex = ex::Expr - if Core.Inference.is_meta_expr(ex) + if Core.Compiler.is_meta_expr(ex) continue elseif ex.head === :return # multiple returns @@ -1098,7 +1098,7 @@ function find_call(code, func, narg) if farg === func return true end - elseif Core.Inference.is_meta_expr(ex) + elseif Core.Compiler.is_meta_expr(ex) continue end find_call(ex.args, func, narg) && return true @@ -1135,8 +1135,8 @@ isdefined_f3(x) = isdefined(x, 3) @test @inferred(isdefined_f3(())) == false @test find_call(first(code_typed(isdefined_f3, Tuple{Tuple{Vararg{Int}}})[1]).code, isdefined, 3) -let isa_tfunc = Core.Inference.t_ffunc_val[ - findfirst(x->x===isa, Core.Inference.t_ffunc_key)][3] +let isa_tfunc = Core.Compiler.T_FFUNC_VAL[ + findfirst(x->x===isa, Core.Compiler.T_FFUNC_KEY)][3] @test isa_tfunc(Array, Const(AbstractArray)) === Const(true) @test isa_tfunc(Array, Type{AbstractArray}) === Const(true) @test isa_tfunc(Array, Type{AbstractArray{Int}}) == Bool @@ -1175,8 +1175,8 @@ let isa_tfunc = Core.Inference.t_ffunc_val[ @test isa_tfunc(Union{Int64, Float64}, Type{AbstractArray}) === Const(false) end -let subtype_tfunc = Core.Inference.t_ffunc_val[ - findfirst(x->x===(<:), Core.Inference.t_ffunc_key)][3] +let subtype_tfunc = Core.Compiler.T_FFUNC_VAL[ + findfirst(x->x===(<:), Core.Compiler.T_FFUNC_KEY)][3] @test subtype_tfunc(Type{<:Array}, Const(AbstractArray)) === Const(true) @test subtype_tfunc(Type{<:Array}, Type{AbstractArray}) === Const(true) @test subtype_tfunc(Type{<:Array}, Type{AbstractArray{Int}}) == Bool @@ -1235,7 +1235,7 @@ g23024(TT::Tuple{DataType}) = f23024(TT[1], v23024) @test Base.return_types(g23024, (Tuple{DataType},)) == Any[Int] @test g23024((UInt8,)) === 2 -@test !Core.Inference.isconstType(Type{typeof(Union{})}) # could be Core.TypeofBottom or Type{Union{}} at runtime +@test !Core.Compiler.isconstType(Type{typeof(Union{})}) # could be Core.TypeofBottom or Type{Union{}} at runtime @test Base.return_types(supertype, (Type{typeof(Union{})},)) == Any[Any] # issue #23685 @@ -1257,7 +1257,7 @@ end struct T23786{D<:Tuple{Vararg{Vector{T} where T}}, N} end let t = Tuple{Type{T23786{D, N} where N where D<:Tuple{Vararg{Array{T, 1} where T, N} where N}}} - @test Core.Inference.limit_type_depth(t, 4) >: t + @test Core.Compiler.limit_type_depth(t, 4) >: t end # issue #13183 @@ -1268,7 +1268,7 @@ gg13183(x::X...) where {X} = (_false13183 ? gg13183(x, x) : 0) # test the external OptimizationState constructor let linfo = get_linfo(Base.convert, Tuple{Type{Int64}, Int32}), world = typemax(UInt), - opt = Core.Inference.OptimizationState(linfo, Core.Inference.InferenceParams(world)) + opt = Core.Compiler.OptimizationState(linfo, Core.Compiler.Params(world)) # make sure the state of the properties look reasonable @test opt.src !== linfo.def.source @test length(opt.src.slotflags) == length(opt.src.slotnames) == length(opt.src.slottypes) @@ -1276,7 +1276,7 @@ let linfo = get_linfo(Base.convert, Tuple{Type{Int64}, Int32}), @test !opt.src.inferred @test opt.mod === Base @test opt.max_valid === typemax(UInt) - @test opt.min_valid === Core.Inference.min_world(opt.linfo) > 2 + @test opt.min_valid === Core.Compiler.min_world(opt.linfo) > 2 @test opt.nargs == 3 end @@ -1304,9 +1304,9 @@ f_pure_add() = (1 + 1 == 2) ? true : "FAIL" @test @inferred f_pure_add() # inference of `T.mutable` -@test Core.Inference.getfield_tfunc(Const(Int), Const(:mutable)) == Const(false) -@test Core.Inference.getfield_tfunc(Const(Vector{Int}), Const(:mutable)) == Const(true) -@test Core.Inference.getfield_tfunc(DataType, Const(:mutable)) == Bool +@test Core.Compiler.getfield_tfunc(Const(Int), Const(:mutable)) == Const(false) +@test Core.Compiler.getfield_tfunc(Const(Vector{Int}), Const(:mutable)) == Const(true) +@test Core.Compiler.getfield_tfunc(DataType, Const(:mutable)) == Bool struct Foo_22708 x::Ptr{Foo_22708} @@ -1331,7 +1331,7 @@ function f24852_kernel_cinfo(x, y) sig, spvals, method = Base._methods_by_ftype(Tuple{typeof(f24852_kernel),x,y}, -1, typemax(UInt))[1] code_info = Base.uncompressed_ast(method) body = Expr(:block, code_info.code...) - Base.Core.Inference.substitute!(body, 0, Any[], sig, Any[spvals...], 0, :propagate) + Base.Core.Compiler.substitute!(body, 0, Any[], sig, Any[spvals...], 0, :propagate) return method, code_info end @@ -1342,7 +1342,7 @@ end function f24852_gen_cinfo_inflated(X, Y, f, x, y) method, code_info = f24852_kernel_cinfo(x, y) - code_info.signature_for_inference_heuristics = Core.Inference.svec(f, (x, y), typemax(UInt)) + code_info.signature_for_inference_heuristics = Core.Compiler.svec(f, (x, y), typemax(UInt)) return code_info end diff --git a/test/codevalidation.jl b/test/compiler/validation.jl similarity index 53% rename from test/codevalidation.jl rename to test/compiler/validation.jl index cb4d947e92d86..962e2d75b61dc 100644 --- a/test/codevalidation.jl +++ b/test/compiler/validation.jl @@ -14,127 +14,127 @@ end msig = Tuple{typeof(f22938),Int,Int,Int,Int} world = typemax(UInt) _, msp, m = Base._methods_by_ftype(msig, -1, world)[] -mi = Core.Inference.code_for_method(m, msig, msp, world, false) -c0 = Core.Inference.retrieve_code_info(mi) +mi = Core.Compiler.code_for_method(m, msig, msp, world, false) +c0 = Core.Compiler.retrieve_code_info(mi) -@test isempty(Core.Inference.validate_code(mi)) -@test isempty(Core.Inference.validate_code(c0)) +@test isempty(Core.Compiler.validate_code(mi)) +@test isempty(Core.Compiler.validate_code(c0)) @testset "INVALID_EXPR_HEAD" begin - c = Core.Inference.copy_code_info(c0) + c = Core.Compiler.copy_code_info(c0) insert!(c.code, 4, Expr(:invalid, 1)) - errors = Core.Inference.validate_code(c) + errors = Core.Compiler.validate_code(c) @test length(errors) == 1 - @test errors[1].kind === Core.Inference.INVALID_EXPR_HEAD + @test errors[1].kind === Core.Compiler.INVALID_EXPR_HEAD end @testset "INVALID_LVALUE" begin - c = Core.Inference.copy_code_info(c0) + c = Core.Compiler.copy_code_info(c0) insert!(c.code, 4, Expr(:(=), LabelNode(1), 1)) insert!(c.code, 2, Expr(:(=), :x, 1)) insert!(c.code, 10, Expr(:(=), 3, 1)) - errors = Core.Inference.validate_code(c) + errors = Core.Compiler.validate_code(c) @test length(errors) == 3 - @test all(e.kind === Core.Inference.INVALID_LVALUE for e in errors) + @test all(e.kind === Core.Compiler.INVALID_LVALUE for e in errors) end @testset "INVALID_RVALUE" begin - c = Core.Inference.copy_code_info(c0) + c = Core.Compiler.copy_code_info(c0) insert!(c.code, 2, Expr(:(=), SlotNumber(2), GotoNode(1))) insert!(c.code, 4, Expr(:(=), SlotNumber(2), LabelNode(2))) insert!(c.code, 10, Expr(:(=), SlotNumber(2), LineNumberNode(2))) for h in (:gotoifnot, :line, :const, :meta) push!(c.code, Expr(:(=), SlotNumber(2), Expr(h))) end - errors = Core.Inference.validate_code(c) + errors = Core.Compiler.validate_code(c) @test length(errors) == 7 - @test count(e.kind === Core.Inference.INVALID_RVALUE for e in errors) == 7 + @test count(e.kind === Core.Compiler.INVALID_RVALUE for e in errors) == 7 end @testset "INVALID_CALL_ARG" begin - c = Core.Inference.copy_code_info(c0) + c = Core.Compiler.copy_code_info(c0) insert!(c.code, 2, Expr(:(=), SlotNumber(2), Expr(:call, GlobalRef(Base,:+), SlotNumber(2), GotoNode(1)))) insert!(c.code, 4, Expr(:call, GlobalRef(Base,:-), Expr(:call, GlobalRef(Base,:sin), LabelNode(2)), 3)) insert!(c.code, 10, Expr(:call, LineNumberNode(2))) for h in (:gotoifnot, :line, :const, :meta) push!(c.code, Expr(:call, GlobalRef(@__MODULE__,:f), Expr(h))) end - errors = Core.Inference.validate_code(c) + errors = Core.Compiler.validate_code(c) @test length(errors) == 7 - @test count(e.kind === Core.Inference.INVALID_CALL_ARG for e in errors) == 7 + @test count(e.kind === Core.Compiler.INVALID_CALL_ARG for e in errors) == 7 end @testset "EMPTY_SLOTNAMES" begin - c = Core.Inference.copy_code_info(c0) + c = Core.Compiler.copy_code_info(c0) empty!(c.slotnames) - errors = Core.Inference.validate_code(c) + errors = Core.Compiler.validate_code(c) @test length(errors) == 2 - @test any(e.kind === Core.Inference.EMPTY_SLOTNAMES for e in errors) - @test any(e.kind === Core.Inference.SLOTFLAGS_MISMATCH for e in errors) + @test any(e.kind === Core.Compiler.EMPTY_SLOTNAMES for e in errors) + @test any(e.kind === Core.Compiler.SLOTFLAGS_MISMATCH for e in errors) end @testset "SLOTFLAGS_MISMATCH" begin - c = Core.Inference.copy_code_info(c0) + c = Core.Compiler.copy_code_info(c0) push!(c.slotnames, :dummy) - errors = Core.Inference.validate_code(c) + errors = Core.Compiler.validate_code(c) @test length(errors) == 1 - @test errors[1].kind === Core.Inference.SLOTFLAGS_MISMATCH + @test errors[1].kind === Core.Compiler.SLOTFLAGS_MISMATCH end @testset "SLOTTYPES_MISMATCH" begin c = @code_typed(f22938(1,2,3,4))[1] pop!(c.slottypes) - errors = Core.Inference.validate_code(c) + errors = Core.Compiler.validate_code(c) @test length(errors) == 1 - @test errors[1].kind === Core.Inference.SLOTTYPES_MISMATCH + @test errors[1].kind === Core.Compiler.SLOTTYPES_MISMATCH end @testset "SLOTTYPES_MISMATCH_UNINFERRED" begin - c = Core.Inference.copy_code_info(c0) + c = Core.Compiler.copy_code_info(c0) c.slottypes = 1 - errors = Core.Inference.validate_code(c) + errors = Core.Compiler.validate_code(c) @test length(errors) == 1 - @test errors[1].kind === Core.Inference.SLOTTYPES_MISMATCH_UNINFERRED + @test errors[1].kind === Core.Compiler.SLOTTYPES_MISMATCH_UNINFERRED end @testset "SSAVALUETYPES_MISMATCH" begin c = @code_typed(f22938(1,2,3,4))[1] empty!(c.ssavaluetypes) - errors = Core.Inference.validate_code(c) + errors = Core.Compiler.validate_code(c) @test length(errors) == 1 - @test errors[1].kind === Core.Inference.SSAVALUETYPES_MISMATCH + @test errors[1].kind === Core.Compiler.SSAVALUETYPES_MISMATCH end @testset "SSAVALUETYPES_MISMATCH_UNINFERRED" begin - c = Core.Inference.copy_code_info(c0) + c = Core.Compiler.copy_code_info(c0) c.ssavaluetypes -= 1 - errors = Core.Inference.validate_code(c) + errors = Core.Compiler.validate_code(c) @test length(errors) == 1 - @test errors[1].kind === Core.Inference.SSAVALUETYPES_MISMATCH_UNINFERRED + @test errors[1].kind === Core.Compiler.SSAVALUETYPES_MISMATCH_UNINFERRED end @testset "SIGNATURE_NARGS_MISMATCH" begin old_sig = mi.def.sig mi.def.sig = Tuple{1,2} - errors = Core.Inference.validate_code(mi) + errors = Core.Compiler.validate_code(mi) mi.def.sig = old_sig @test length(errors) == 1 - @test errors[1].kind === Core.Inference.SIGNATURE_NARGS_MISMATCH + @test errors[1].kind === Core.Compiler.SIGNATURE_NARGS_MISMATCH end @testset "NON_TOP_LEVEL_METHOD" begin - c = Core.Inference.copy_code_info(c0) + c = Core.Compiler.copy_code_info(c0) push!(c.code, Expr(:method, :dummy)) - errors = Core.Inference.validate_code(c) + errors = Core.Compiler.validate_code(c) @test length(errors) == 1 - @test errors[1].kind === Core.Inference.NON_TOP_LEVEL_METHOD + @test errors[1].kind === Core.Compiler.NON_TOP_LEVEL_METHOD end @testset "SLOTNAMES_NARGS_MISMATCH" begin mi.def.nargs += 20 - errors = Core.Inference.validate_code(mi) + errors = Core.Compiler.validate_code(mi) mi.def.nargs -= 20 @test length(errors) == 2 - @test count(e.kind === Core.Inference.SLOTNAMES_NARGS_MISMATCH for e in errors) == 1 - @test count(e.kind === Core.Inference.SIGNATURE_NARGS_MISMATCH for e in errors) == 1 + @test count(e.kind === Core.Compiler.SLOTNAMES_NARGS_MISMATCH for e in errors) == 1 + @test count(e.kind === Core.Compiler.SIGNATURE_NARGS_MISMATCH for e in errors) == 1 end diff --git a/test/keywordargs.jl b/test/keywordargs.jl index 6958b8d20cdc4..47ffc65e4d0dd 100644 --- a/test/keywordargs.jl +++ b/test/keywordargs.jl @@ -235,11 +235,11 @@ end end end # pr #18396, kwargs before Base is defined -@eval Core.Inference begin +@eval Core.Compiler begin f18396(;kwargs...) = g18396(;kwargs...) g18396(;x=1,y=2) = x+y end -@test Core.Inference.f18396() == 3 +@test Core.Compiler.f18396() == 3 @testset "issue #7045, `invoke` with keyword args" begin f7045(x::Float64; y=true) = y ? 1 : invoke(f7045,Tuple{Real},x,y=y) f7045(x::Real; y=true) = y ? 2 : 3 diff --git a/test/misc.jl b/test/misc.jl index c97e051c3470d..eafcbd35003a7 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -183,7 +183,7 @@ v11801, t11801 = @timed sin(1) # interactive utilities import Base.summarysize -@test summarysize(Core) > (summarysize(Core.Inference) + Base.summarysize(Core.Intrinsics)) > Core.sizeof(Core) +@test summarysize(Core) > (summarysize(Core.Compiler) + Base.summarysize(Core.Intrinsics)) > Core.sizeof(Core) @test summarysize(Base) > 100_000 * sizeof(Ptr) let R = Ref{Any}(nothing), depth = 10^6 diff --git a/test/reflection.jl b/test/reflection.jl index e22c553c03771..9367b90c25763 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -666,11 +666,11 @@ let code_typed(f18888, Tuple{}; optimize=false) @test m.specializations !== nothing # uncached, but creates the specializations entry - code = Core.Inference.code_for_method(m, Tuple{ft}, Core.svec(), world, true) + code = Core.Compiler.code_for_method(m, Tuple{ft}, Core.svec(), world, true) @test !isdefined(code, :inferred) code_typed(f18888, Tuple{}; optimize=true) - code = Core.Inference.code_for_method(m, Tuple{ft}, Core.svec(), world, true) + code = Core.Compiler.code_for_method(m, Tuple{ft}, Core.svec(), world, true) @test isdefined(code, :inferred) end @@ -768,8 +768,8 @@ x22979 = (1, 2.0, 3.0 + im) T22979 = Tuple{typeof(f22979),typeof.(x22979)...} world = typemax(UInt) mtypes, msp, m = Base._methods_by_ftype(T22979, -1, world)[] -instance = Core.Inference.code_for_method(m, mtypes, msp, world, false) -cinfo_generated = Core.Inference.get_staged(instance) +instance = Core.Compiler.code_for_method(m, mtypes, msp, world, false) +cinfo_generated = Core.Compiler.get_staged(instance) @test_throws ErrorException Base.uncompressed_ast(m) test_similar_codeinfo(@code_lowered(f22979(x22979...)), cinfo_generated) diff --git a/test/staged.jl b/test/staged.jl index dad910a1b3a5e..4e6379eddcfa6 100644 --- a/test/staged.jl +++ b/test/staged.jl @@ -243,7 +243,7 @@ f22440kernel(::Type{T}) where {T<:AbstractFloat} = zero(T) sig, spvals, method = Base._methods_by_ftype(Tuple{typeof(f22440kernel),y}, -1, typemax(UInt))[1] code_info = Base.uncompressed_ast(method) body = Expr(:block, code_info.code...) - Base.Core.Inference.substitute!(body, 0, Any[], sig, Any[spvals...], 0, :propagate) + Base.Core.Compiler.substitute!(body, 0, Any[], sig, Any[spvals...], 0, :propagate) return code_info end diff --git a/test/worlds.jl b/test/worlds.jl index b67b419a4c656..23f036e921d63 100644 --- a/test/worlds.jl +++ b/test/worlds.jl @@ -12,13 +12,13 @@ begin f265a(x::Any) = 1 @test g265a() == 1 @test Base.return_types(g265a, ()) == Any[Int] - @test Core.Inference.return_type(g265a, ()) == Int + @test Core.Compiler.return_type(g265a, ()) == Int f265a(x::Any) = 2.0 @test g265a() == 2.0 @test Base.return_types(g265a, ()) == Any[Float64] - @test Core.Inference.return_type(g265a, ()) == Float64 + @test Core.Compiler.return_type(g265a, ()) == Float64 end # test signature widening @@ -29,13 +29,13 @@ begin end @test g265b(1) == 1 @test Base.return_types(g265b, (Int,)) == Any[Int] - @test Core.Inference.return_type(g265b, (Int,)) == Int + @test Core.Compiler.return_type(g265b, (Int,)) == Int f265b(x::Any) = 2.0 @test g265b(1) == 1 @test g265b(2) == 2.0 @test Base.return_types(g265b, (Int,)) == Any[Union{Int, Float64}] - @test Core.Inference.return_type(g265b, (Int,)) == Union{Int, Float64} + @test Core.Compiler.return_type(g265b, (Int,)) == Union{Int, Float64} end # test signature narrowing @@ -44,13 +44,13 @@ begin f265c(x::Any) = 1 @test g265c() == 1 @test Base.return_types(g265c, ()) == Any[Int] - @test Core.Inference.return_type(g265c, ()) == Int + @test Core.Compiler.return_type(g265c, ()) == Int f265c(x::Int) = 2.0 @test g265c() == 2.0 @test Base.return_types(g265c, ()) == Any[Float64] - @test Core.Inference.return_type(g265c, ()) == Float64 + @test Core.Compiler.return_type(g265c, ()) == Float64 end # test constructor narrowing @@ -78,7 +78,7 @@ end @test_throws MethodError B265_(2) @test_throws MethodError B265_(3) @test Base.return_types(B265_, (Int,)) == Any[B265{Int}] -@test Core.Inference.return_type(B265_, (Int,)) == B265{Int} +@test Core.Compiler.return_type(B265_, (Int,)) == B265{Int} # add new constructors B265(x::Float64, dummy::Nothing) = B265{Float64}(x, dummy) @@ -90,7 +90,7 @@ B265(x::Any, dummy::Nothing) = B265{UInt8}(x, dummy) @test (B265_(3)::B265{UInt8}).field1 === 0x03 @test Base.return_types(B265_, (Int,)) == Any[Union{B265{Float64}, B265{Int}, B265{UInt8}}] -@test Core.Inference.return_type(B265_, (Int,)) == Union{B265{Float64}, B265{Int}, B265{UInt8}} +@test Core.Compiler.return_type(B265_, (Int,)) == Union{B265{Float64}, B265{Int}, B265{UInt8}} # test oldworld call / inference @@ -120,15 +120,15 @@ f265(::Int) = 1 @test put_n_take!(tls_world_age, ()) == wc265 @test g265() == Int[1, 1, 1] -@test Core.Inference.return_type(f265, (Any,)) == Union{Float64, Int} -@test Core.Inference.return_type(f265, (Int,)) == Int -@test Core.Inference.return_type(f265, (Float64,)) == Float64 +@test Core.Compiler.return_type(f265, (Any,)) == Union{Float64, Int} +@test Core.Compiler.return_type(f265, (Int,)) == Int +@test Core.Compiler.return_type(f265, (Float64,)) == Float64 @test put_n_take!(g265, ()) == Float64[1.0, 1.0, 1.0] -@test put_n_take!(Core.Inference.return_type, (f265, (Any,))) == Float64 -@test put_n_take!(Core.Inference.return_type, (f265, (Int,))) == Float64 -@test put_n_take!(Core.Inference.return_type, (f265, (Float64,))) == Float64 -@test put_n_take!(Core.Inference.return_type, (f265, (Float64,))) == Float64 +@test put_n_take!(Core.Compiler.return_type, (f265, (Any,))) == Float64 +@test put_n_take!(Core.Compiler.return_type, (f265, (Int,))) == Float64 +@test put_n_take!(Core.Compiler.return_type, (f265, (Float64,))) == Float64 +@test put_n_take!(Core.Compiler.return_type, (f265, (Float64,))) == Float64 # test that reflection ignores worlds @test Base.return_types(f265, (Any,)) == Any[Int, Float64]