diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 39cabe33ff043..53cc5fdeddf09 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -47,7 +47,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), end argtypes = arginfo.argtypes - matches = find_matching_methods(argtypes, atype, method_table(interp, sv), InferenceParams(interp).MAX_UNION_SPLITTING, max_methods) + matches = find_matching_methods(argtypes, atype, method_table(interp), InferenceParams(interp).MAX_UNION_SPLITTING, max_methods) if isa(matches, FailedMethodMatch) add_remark!(interp, sv, matches.reason) tristate_merge!(sv, Effects()) @@ -637,7 +637,7 @@ end function pure_eval_eligible(interp::AbstractInterpreter, @nospecialize(f), applicable::Vector{Any}, arginfo::ArgInfo, sv::InferenceState) - return !isoverlayed(method_table(interp, sv)) && + return !isoverlayed(method_table(interp)) && f !== nothing && length(applicable) == 1 && is_method_pure(applicable[1]::MethodMatch) && @@ -674,7 +674,7 @@ end function concrete_eval_eligible(interp::AbstractInterpreter, @nospecialize(f), result::MethodCallResult, arginfo::ArgInfo, sv::InferenceState) - return !isoverlayed(method_table(interp, sv)) && + return !isoverlayed(method_table(interp)) && f !== nothing && result.edge !== nothing && is_total_or_error(result.edge_effects) && @@ -2110,14 +2110,14 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) frame.dont_work_on_me = true # mark that this function is currently on the stack W = frame.ip states = frame.stmt_types - n = frame.nstmts + nstmts = frame.nstmts nargs = frame.nargs def = frame.linfo.def isva = isa(def, Method) && def.isva nslots = nargs - isva slottypes = frame.slottypes ssavaluetypes = frame.src.ssavaluetypes::Vector{Any} - while frame.pc´´ <= n + while frame.pc´´ <= nstmts # make progress on the active ip set local pc::Int = frame.pc´´ while true # inner loop optimizes the common case where it can run straight from pc to pc + 1 @@ -2189,7 +2189,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) end end elseif isa(stmt, ReturnNode) - pc´ = n + 1 + pc´ = nstmts + 1 bestguess = frame.bestguess rt = abstract_eval_value(interp, stmt.val, changes, frame) rt = widenreturn(rt, bestguess, nslots, slottypes, changes) @@ -2310,7 +2310,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) ssavaluetypes[pc] = Any end - pc´ > n && break # can't proceed with the fast-path fall-through + pc´ > nstmts && break # can't proceed with the fast-path fall-through newstate = stupdate!(states[pc´], changes) if isa(stmt, GotoNode) && frame.pc´´ < pc´ # if we are processing a goto node anyways, diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 39c4c9a2ceb66..928811dd63a3b 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -62,11 +62,6 @@ mutable struct InferenceState # Inferred purity flags ipo_effects::Effects - # The place to look up methods while working on this function. - # In particular, we cache method lookup results for the same function to - # fast path repeated queries. - method_table::InternalMethodTable - # The interpreter that created this inference state. Not looked at by # NativeInterpreter. But other interpreters may use this to detect cycles interp::AbstractInterpreter @@ -85,9 +80,9 @@ mutable struct InferenceState src.ssavaluetypes = Any[ NOT_FOUND for i = 1:nssavalues ] stmt_info = Any[ nothing for i = 1:length(code) ] - n = length(code) - s_types = Union{Nothing, VarTable}[ nothing for i = 1:n ] - s_edges = Union{Nothing, Vector{Any}}[ nothing for i = 1:n ] + nstmts = length(code) + s_types = Union{Nothing, VarTable}[ nothing for i = 1:nstmts ] + s_edges = Union{Nothing, Vector{Any}}[ nothing for i = 1:nstmts ] # initial types nslots = length(src.slotflags) @@ -129,19 +124,17 @@ mutable struct InferenceState @assert cache === :no || cache === :local || cache === :global frame = new( params, result, linfo, - sp, slottypes, mod, 0, - IdSet{InferenceState}(), IdSet{InferenceState}(), + sp, slottypes, mod, #=currpc=#0, + #=pclimitations=#IdSet{InferenceState}(), #=limitations=#IdSet{InferenceState}(), src, get_world_counter(interp), valid_worlds, nargs, s_types, s_edges, stmt_info, - Union{}, ip, 1, n, handler_at, - ssavalue_uses, - Vector{Tuple{InferenceState,LineNum}}(), # cycle_backedges - Vector{InferenceState}(), # callers_in_cycle + #=bestguess=#Union{}, ip, #=pc´´=#1, nstmts, handler_at, ssavalue_uses, + #=cycle_backedges=#Vector{Tuple{InferenceState,LineNum}}(), + #=callers_in_cycle=#Vector{InferenceState}(), #=parent=#nothing, - cache === :global, false, false, - Effects(consistent, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, - inbounds_taints_consistency), - method_table(interp), + #=cached=#cache === :global, + #=inferred=#false, #=dont_work_on_me=#false, + #=ipo_effects=#Effects(consistent, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, inbounds_taints_consistency), interp) result.result = frame cache !== :no && push!(get_inference_cache(interp), result) @@ -267,8 +260,6 @@ function iterate(unw::InfStackUnwind, (infstate, cyclei)::Tuple{InferenceState, (infstate::InferenceState, (infstate, cyclei)) end -method_table(interp::AbstractInterpreter, sv::InferenceState) = sv.method_table - function InferenceState(result::InferenceResult, cache::Symbol, interp::AbstractInterpreter) # prepare an InferenceState object for inferring lambda src = retrieve_code_info(result.linfo) diff --git a/base/compiler/types.jl b/base/compiler/types.jl index 1fd33d47f0408..6d4a650470237 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -314,6 +314,13 @@ may_compress(::AbstractInterpreter) = true may_discard_trees(::AbstractInterpreter) = true verbose_stmt_info(::AbstractInterpreter) = false +""" + method_table(interp::AbstractInterpreter) -> MethodTableView + +Returns a method table this `interp` uses for method lookup. +External `AbstractInterpreter` can optionally return `OverlayMethodTable` here +to incorporate customized dispatches for the overridden methods. +""" method_table(interp::AbstractInterpreter) = InternalMethodTable(get_world_counter(interp)) """ diff --git a/test/choosetests.jl b/test/choosetests.jl index f86f665bc2217..099dfa18a71c5 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -142,7 +142,7 @@ function choosetests(choices = []) filtertests!(tests, "subarray") filtertests!(tests, "compiler", ["compiler/inference", "compiler/validation", "compiler/ssair", "compiler/irpasses", "compiler/codegen", - "compiler/inline", "compiler/contextual", + "compiler/inline", "compiler/contextual", "compiler/AbstractInterpreter", "compiler/EscapeAnalysis/local", "compiler/EscapeAnalysis/interprocedural"]) filtertests!(tests, "compiler/EscapeAnalysis", [ "compiler/EscapeAnalysis/local", "compiler/EscapeAnalysis/interprocedural"]) diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl new file mode 100644 index 0000000000000..6ef2adf7177fa --- /dev/null +++ b/test/compiler/AbstractInterpreter.jl @@ -0,0 +1,47 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +const CC = Core.Compiler +import Core: MethodInstance, CodeInstance +import .CC: WorldRange, WorldView + +# define new `AbstractInterpreter` that satisfies the minimum interface requirements +# while managing its cache independently +macro newinterp(name) + cachename = gensym(string(name, "Cache")) + name = esc(name) + quote + struct $cachename + dict::IdDict{MethodInstance,CodeInstance} + end + struct $name <: CC.AbstractInterpreter + interp::CC.NativeInterpreter + cache::$cachename + $name(world = Base.get_world_counter(); + interp = CC.NativeInterpreter(world), + cache = $cachename(IdDict{MethodInstance,CodeInstance}()) + ) = new(interp, cache) + end + CC.InferenceParams(interp::$name) = CC.InferenceParams(interp.interp) + CC.OptimizationParams(interp::$name) = CC.OptimizationParams(interp.interp) + CC.get_world_counter(interp::$name) = CC.get_world_counter(interp.interp) + CC.get_inference_cache(interp::$name) = CC.get_inference_cache(interp.interp) + CC.code_cache(interp::$name) = WorldView(interp.cache, WorldRange(CC.get_world_counter(interp))) + CC.get(wvc::WorldView{<:$cachename}, mi::MethodInstance, default) = get(wvc.cache.dict, mi, default) + CC.getindex(wvc::WorldView{<:$cachename}, mi::MethodInstance) = getindex(wvc.cache.dict, mi) + CC.haskey(wvc::WorldView{<:$cachename}, mi::MethodInstance) = haskey(wvc.cache.dict, mi) + CC.setindex!(wvc::WorldView{<:$cachename}, ci::CodeInstance, mi::MethodInstance) = setindex!(wvc.cache.dict, ci, mi) + end +end + +# `OverlayMethodTable` +# -------------------- +import Base.Experimental: @MethodTable, @overlay + +@newinterp MTOverlayInterp +@MethodTable(OverlayedMT) +CC.method_table(interp::MTOverlayInterp) = CC.OverlayMethodTable(CC.get_world_counter(interp), OverlayedMT) + +@overlay OverlayedMT sin(x::Float64) = 1 +@test Base.return_types((Int,), MTOverlayInterp()) do x + sin(x) +end == Any[Int]