1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
|
# 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
slottypes::Vector{Any}
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}
cycle_backedges::Vector{Tuple{InferenceState, LineNum}} # call-graph backedges connecting from callee to caller
callers_in_cycle::Vector{InferenceState}
parent::Union{Nothing, InferenceState}
# TODO: move these to InferenceResult / Params?
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,
cached::Bool, params::Params)
linfo = result.linfo
code = src.code::Array{Any,1}
toplevel = !isa(linfo.def, Method)
sp = spvals_from_meth_instance(linfo::MethodInstance)
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)
nargs = length(argtypes)
s_argtypes = VarTable(undef, nslots)
slottypes = Vector{Any}(undef, nslots)
for i in 1:nslots
at = (i > nargs) ? Bottom : argtypes[i]
s_argtypes[i] = VarState(at, i > nargs)
slottypes[i] = at
end
s_types[1] = s_argtypes
ssavalue_uses = find_ssavalue_uses(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, slottypes, inmodule, 0,
src, min_valid, max_valid,
nargs, s_types, s_edges,
Union{}, W, 1, n,
cur_hand, handler_at, n_handlers,
ssavalue_uses,
Vector{Tuple{InferenceState,LineNum}}(), # cycle_backedges
Vector{InferenceState}(), # callers_in_cycle
#=parent=#nothing,
cached, false, false, false)
result.result = frame
cached && push!(params.cache, result)
return frame
end
end
function InferenceState(result::InferenceResult, 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, cached, params)
end
function spvals_from_meth_instance(linfo::MethodInstance)
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
if _any(t->isa(t,TypeVar), sp)
sp = collect(Any, sp)
end
end
if !isa(sp, SimpleVector)
for i = 1:length(sp)
v = sp[i]
if v isa TypeVar
ub = v.ub
while ub isa TypeVar
ub = ub.ub
end
if has_free_typevars(ub)
ub = Any
end
lb = v.lb
while lb isa TypeVar
lb = lb.lb
end
if has_free_typevars(lb)
lb = Bottom
end
sp[i] = TypeVar(v.name, lb, ub)
end
end
sp = svec(sp...)
end
return sp
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_cycle_backedge!(frame::InferenceState, caller::InferenceState, currpc::Int)
update_valid_age!(frame, caller)
backedge = (caller, currpc)
contains_is(frame.cycle_backedges, backedge) || push!(frame.cycle_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::Core.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), nargs::Int, vargs::Vector{Any})
return (isa(arg, Slot) && slot_id(arg) == nargs && !isempty(vargs))
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
|