Skip to content

Commit

Permalink
AbstractInterpreter: remove `method_table(::AbstractInterpreter, ::…
Browse files Browse the repository at this point in the history
…InferenceState)` interface (#44389)

In #44240 we removed the `CachedMethodTable` support as it turned out to
be ineffective under the current compiler infrastructure.
Because of this, there is no strong reason to keep a method table per `InferenceState`.
This commit simply removes the `method_table(::AbstractInterpreter, ::InferenceState)`
interface and should make it clearer which interface should be overloaded to
implement a contextual dispatch.
  • Loading branch information
aviatesk authored Mar 3, 2022
1 parent ceaee81 commit 8800a80
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 28 deletions.
14 changes: 7 additions & 7 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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) &&
Expand Down Expand Up @@ -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) &&
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down
31 changes: 11 additions & 20 deletions base/compiler/inferencestate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions base/compiler/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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))

"""
Expand Down
2 changes: 1 addition & 1 deletion test/choosetests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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"])
Expand Down
47 changes: 47 additions & 0 deletions test/compiler/AbstractInterpreter.jl
Original file line number Diff line number Diff line change
@@ -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]

0 comments on commit 8800a80

Please sign in to comment.