From f192ead1b898ee9afc6c8912d2ceb2edaaefc588 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sun, 28 Jun 2020 20:33:46 -0400 Subject: [PATCH] StmtInfo WIP --- base/compiler/abstractinterpretation.jl | 344 +++++++++++++----------- base/compiler/compiler.jl | 1 + base/compiler/inferencestate.jl | 1 + base/compiler/ssair/driver.jl | 2 +- base/compiler/ssair/ir.jl | 10 +- base/compiler/ssair/legacy.jl | 2 +- base/compiler/stmtinfo.jl | 7 + base/compiler/tfuncs.jl | 2 +- src/codegen.cpp | 1 + src/ircode.c | 6 +- src/jltypes.c | 8 +- src/julia.h | 1 + src/method.c | 1 + 13 files changed, 213 insertions(+), 173 deletions(-) create mode 100644 base/compiler/stmtinfo.jl diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index b766c043dc514..114cdbd1cb29a 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -37,9 +37,9 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), max_methods::Int = InferenceParams(interp).MAX_METHODS) atype_params = unwrap_unionall(atype).parameters ft = unwrap_unionall(atype_params[1]) # TODO: ccall jl_method_table_for here - isa(ft, DataType) || return Any # the function being called is unknown. can't properly handle this backedge right now + isa(ft, DataType) || return Any, nothing # 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 + isdefined(ftname, :mt) || return Any, nothing # 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) @@ -49,7 +49,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), if !isa(tname, DataType) # can't track the backedge to the ctor right now # for things like Union - return Any + return Any, nothing end end min_valid = UInt[typemin(UInt)] @@ -58,20 +58,24 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), if splitunions splitsigs = switchtupleunion(atype) applicable = Any[] + infos = MethodMatchInfo[] for sig_n in splitsigs xapplicable = matching_methods(sig_n, sv.matching_methods_cache, max_methods, get_world_counter(interp), min_valid, max_valid) - xapplicable === false && return Any + xapplicable === false && return Any, nothing + push!(infos, MethodMatchInfo(xapplicable)) append!(applicable, xapplicable) end + info = UnionSplitInfo(infos) else applicable = matching_methods(atype, sv.matching_methods_cache, max_methods, get_world_counter(interp), 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 + return Any, nothing end + info = MethodMatchInfo(applicable) end update_valid_age!(min_valid[1], max_valid[1], sv) applicable = applicable::Array{Any,1} @@ -87,7 +91,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), if f !== nothing && napplicable == 1 && is_method_pure(applicable[1][3], applicable[1][1], applicable[1][2]) val = pure_eval_call(f, argtypes) if val !== false - return val + return val, info end end @@ -176,7 +180,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), end end #print("=> ", rettype, "\n") - return rettype + return rettype, info end @@ -559,7 +563,7 @@ function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @n else return Any[Vararg{Any}] end - stateordonet = abstract_call_known(interp, iteratef, nothing, Any[itft, itertype], vtypes, sv) + stateordonet, _ = abstract_call_known(interp, iteratef, nothing, Any[itft, itertype], vtypes, sv) # Return Bottom if this is not an iterator. # WARNING: Changes to the iteration protocol must be reflected here, # this is not just an optimization. @@ -578,7 +582,7 @@ function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @n valtype = stateordonet.parameters[1] statetype = stateordonet.parameters[2] push!(ret, valtype) - stateordonet = abstract_call_known(interp, iteratef, nothing, Any[Const(iteratef), itertype, statetype], vtypes, sv) + stateordonet, _ = abstract_call_known(interp, iteratef, nothing, Any[Const(iteratef), itertype, statetype], vtypes, sv) stateordonet = widenconst(stateordonet) end if stateordonet === Nothing @@ -595,7 +599,7 @@ function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @n end valtype = tmerge(valtype, nounion.parameters[1]) statetype = tmerge(statetype, nounion.parameters[2]) - stateordonet = abstract_call_known(interp, iteratef, nothing, Any[Const(iteratef), itertype, statetype], vtypes, sv) + stateordonet, _ = abstract_call_known(interp, iteratef, nothing, Any[Const(iteratef), itertype, statetype], vtypes, sv) stateordonet = widenconst(stateordonet) end push!(ret, Vararg{valtype}) @@ -646,7 +650,7 @@ function abstract_apply(interp::AbstractInterpreter, @nospecialize(itft), @nospe break end end - rt = abstract_call(interp, nothing, ct, vtypes, sv, max_methods) + rt, _ = abstract_call(interp, nothing, ct, vtypes, sv, max_methods) res = tmerge(res, rt) if res === Any break @@ -702,118 +706,161 @@ function argtype_tail(argtypes::Vector{Any}, i::Int) return argtypes[i:n] end -# call where the function is known exactly -function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), fargs::Union{Nothing,Vector{Any}}, argtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState, max_methods::Int = InferenceParams(interp).MAX_METHODS) +function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, fargs::Union{Nothing,Vector{Any}}, + argtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState, max_methods::Int) la = length(argtypes) - - if isa(f, Builtin) - if f === _apply - ft = argtype_by_index(argtypes, 2) - ft === Bottom && return Bottom - return abstract_apply(interp, nothing, ft, argtype_tail(argtypes, 3), vtypes, sv, max_methods) - elseif f === _apply_iterate - itft = argtype_by_index(argtypes, 2) - ft = argtype_by_index(argtypes, 3) - (itft === Bottom || ft === Bottom) && return Bottom - return abstract_apply(interp, itft, ft, argtype_tail(argtypes, 4), vtypes, sv, max_methods) - elseif f === ifelse && fargs isa Vector{Any} && la == 4 && argtypes[2] isa Conditional - # try to simulate this as a real conditional (`cnd ? x : y`), so that the penalty for using `ifelse` instead isn't too high - cnd = argtypes[2]::Conditional - tx = argtypes[3] - ty = argtypes[4] - a = ssa_def_slot(fargs[3], sv) - b = ssa_def_slot(fargs[4], sv) - if isa(a, Slot) && slot_id(cnd.var) == slot_id(a) - tx = typeintersect(tx, cnd.vtype) - end - if isa(b, Slot) && slot_id(cnd.var) == slot_id(b) - ty = typeintersect(ty, cnd.elsetype) - end - return tmerge(tx, ty) - end - rt = builtin_tfunction(interp, f, argtypes[2:end], sv) - if f === getfield && isa(fargs, Vector{Any}) && la == 3 && isa(argtypes[3], Const) && isa(argtypes[3].val, Int) && argtypes[2] ⊑ Tuple - cti = precise_container_type(interp, nothing, argtypes[2], vtypes, sv) - idx = argtypes[3].val - if 1 <= idx <= length(cti) - rt = unwrapva(cti[idx]) - end - elseif (rt === Bool || (isa(rt, Const) && isa(rt.val, Bool))) && isa(fargs, Vector{Any}) - # perform very limited back-propagation of type information for `is` and `isa` - if f === isa - a = ssa_def_slot(fargs[2], sv) - if isa(a, Slot) - aty = widenconst(argtypes[2]) - if rt === Const(false) - return Conditional(a, Union{}, aty) - elseif rt === Const(true) - return Conditional(a, aty, Union{}) - end - 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) - elty = typesubtract(aty, tty_lb) - return Conditional(a, ifty, elty) - end - end - end - elseif f === (===) - a = ssa_def_slot(fargs[2], sv) - b = ssa_def_slot(fargs[3], sv) - aty = argtypes[2] - bty = argtypes[3] - # if doing a comparison to a singleton, consider returning a `Conditional` instead - if isa(aty, Const) && isa(b, Slot) - if rt === Const(false) - aty = Union{} - elseif rt === Const(true) - bty = Union{} - elseif bty isa Type && isdefined(typeof(aty.val), :instance) # can only widen a if it is a singleton - bty = typesubtract(bty, typeof(aty.val)) - end - return Conditional(b, aty, bty) + if f === _apply + ft = argtype_by_index(argtypes, 2) + ft === Bottom && return Bottom + return abstract_apply(interp, nothing, ft, argtype_tail(argtypes, 3), vtypes, sv, max_methods) + elseif f === _apply_iterate + itft = argtype_by_index(argtypes, 2) + ft = argtype_by_index(argtypes, 3) + (itft === Bottom || ft === Bottom) && return Bottom + return abstract_apply(interp, itft, ft, argtype_tail(argtypes, 4), vtypes, sv, max_methods) + elseif f === ifelse && fargs isa Vector{Any} && la == 4 && argtypes[2] isa Conditional + # try to simulate this as a real conditional (`cnd ? x : y`), so that the penalty for using `ifelse` instead isn't too high + cnd = argtypes[2]::Conditional + tx = argtypes[3] + ty = argtypes[4] + a = ssa_def_slot(fargs[3], sv) + b = ssa_def_slot(fargs[4], sv) + if isa(a, Slot) && slot_id(cnd.var) == slot_id(a) + tx = typeintersect(tx, cnd.vtype) + end + if isa(b, Slot) && slot_id(cnd.var) == slot_id(b) + ty = typeintersect(ty, cnd.elsetype) + end + return tmerge(tx, ty) + end + rt = builtin_tfunction(interp, f, argtypes[2:end], sv) + if f === getfield && isa(fargs, Vector{Any}) && la == 3 && isa(argtypes[3], Const) && isa(argtypes[3].val, Int) && argtypes[2] ⊑ Tuple + cti = precise_container_type(interp, nothing, argtypes[2], vtypes, sv) + idx = argtypes[3].val + if 1 <= idx <= length(cti) + rt = unwrapva(cti[idx]) + end + elseif (rt === Bool || (isa(rt, Const) && isa(rt.val, Bool))) && isa(fargs, Vector{Any}) + # perform very limited back-propagation of type information for `is` and `isa` + if f === isa + a = ssa_def_slot(fargs[2], sv) + if isa(a, Slot) + aty = widenconst(argtypes[2]) + if rt === Const(false) + return Conditional(a, Union{}, aty) + elseif rt === Const(true) + return Conditional(a, aty, Union{}) end - if isa(bty, Const) && isa(a, Slot) - if rt === Const(false) - bty = Union{} - elseif rt === Const(true) - aty = Union{} - elseif aty isa Type && isdefined(typeof(bty.val), :instance) # same for b - aty = typesubtract(aty, typeof(bty.val)) + 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) + elty = typesubtract(aty, tty_lb) + return Conditional(a, ifty, elty) end - return Conditional(a, bty, aty) end - if isa(b, Slot) - return Conditional(b, bty, bty) + end + elseif f === (===) + a = ssa_def_slot(fargs[2], sv) + b = ssa_def_slot(fargs[3], sv) + aty = argtypes[2] + bty = argtypes[3] + # if doing a comparison to a singleton, consider returning a `Conditional` instead + if isa(aty, Const) && isa(b, Slot) + if rt === Const(false) + aty = Union{} + elseif rt === Const(true) + bty = Union{} + elseif bty isa Type && isdefined(typeof(aty.val), :instance) # can only widen a if it is a singleton + bty = typesubtract(bty, typeof(aty.val)) end - if isa(a, Slot) - return Conditional(a, aty, aty) + return Conditional(b, aty, bty) + end + if isa(bty, Const) && isa(a, Slot) + if rt === Const(false) + bty = Union{} + elseif rt === Const(true) + aty = Union{} + elseif aty isa Type && isdefined(typeof(bty.val), :instance) # same for b + aty = typesubtract(aty, typeof(bty.val)) end - elseif f === Core.Compiler.not_int - aty = argtypes[2] - if isa(aty, Conditional) - ifty = aty.elsetype - elty = aty.vtype - if rt === Const(false) - ifty = Union{} - elseif rt === Const(true) - elty = Union{} - end - return Conditional(aty.var, ifty, elty) + return Conditional(a, bty, aty) + end + if isa(b, Slot) + return Conditional(b, bty, bty) + end + if isa(a, Slot) + return Conditional(a, aty, aty) + end + elseif f === Core.Compiler.not_int + aty = argtypes[2] + if isa(aty, Conditional) + ifty = aty.elsetype + elty = aty.vtype + if rt === Const(false) + ifty = Union{} + elseif rt === Const(true) + elty = Union{} end + return Conditional(aty.var, ifty, elty) end end - return isa(rt, TypeVar) ? rt.ub : rt + end + return isa(rt, TypeVar) ? rt.ub : rt +end + +function abstract_call_unionall(argtypes::Vector{Any}) + 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 + if has_free_typevars(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 + body = UnionAll(tv, body) + end + ret = canconst ? AbstractEvalConstant(body) : Type{body} + return ret + end + return Any +end + +# call where the function is known exactly +function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), + fargs::Union{Nothing,Vector{Any}}, argtypes::Vector{Any}, + vtypes::VarTable, sv::InferenceState, + max_methods::Int = InferenceParams(interp).MAX_METHODS) + + la = length(argtypes) + + if isa(f, Builtin) + return abstract_call_builtin(interp, f, fargs, argtypes, vtypes, sv, max_methods), nothing elseif f === Core.kwfunc if la == 2 ft = widenconst(argtypes[2]) if isa(ft, DataType) && isdefined(ft.name, :mt) && isdefined(ft.name.mt, :kwsorter) - return Const(ft.name.mt.kwsorter) + return Const(ft.name.mt.kwsorter), nothing end end - return Any + return Any, nothing elseif f === TypeVar # Manually look through the definition of TypeVar to # make sure to be able to get `PartialTypeVar`s out. @@ -827,62 +874,33 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), farg elseif la == 3 ub_var = argtypes[3] end - return typevar_tfunc(n, lb_var, ub_var) + return typevar_tfunc(n, lb_var, ub_var), nothing elseif f === UnionAll - if la == 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 - if has_free_typevars(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 - body = UnionAll(tv, body) - end - ret = canconst ? AbstractEvalConstant(body) : Type{body} - return ret - end - return Any + return abstract_call_unionall(argtypes), nothing elseif f === Tuple && la == 2 && !isconcretetype(widenconst(argtypes[2])) - return Tuple + return Tuple, nothing elseif is_return_type(f) rt_rt = return_type_tfunc(interp, argtypes, vtypes, sv) if rt_rt !== nothing - return rt_rt + return rt_rt, nothing end - return Type + return Type, nothing elseif la == 2 && istopfunction(f, :!) # handle Conditional propagation through !Bool aty = argtypes[2] if isa(aty, Conditional) abstract_call_gf_by_type(interp, f, Any[Const(f), Bool], Tuple{typeof(f), Bool}, sv) # make sure we've inferred `!(::Bool)` - return Conditional(aty.var, aty.elsetype, aty.vtype) + return Conditional(aty.var, aty.elsetype, aty.vtype), nothing end elseif la == 3 && istopfunction(f, :!==) # mark !== as exactly a negated call to === - rty = abstract_call_known(interp, (===), fargs, argtypes, vtypes, sv) + rty, _ = abstract_call_known(interp, (===), fargs, argtypes, vtypes, sv) if isa(rty, Conditional) - return Conditional(rty.var, rty.elsetype, rty.vtype) # swap if-else + return Conditional(rty.var, rty.elsetype, rty.vtype), nothing # swap if-else elseif isa(rty, Const) - return Const(rty.val === false) + return Const(rty.val === false), nothing end - return rty + return rty, nothing elseif la == 3 && istopfunction(f, :(>:)) # mark issupertype as a exact alias for issubtype # swap T1 and T2 arguments and call <: @@ -892,26 +910,26 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), farg fargs = nothing end argtypes = Any[typeof(<:), argtypes[3], argtypes[2]] - rty = abstract_call_known(interp, <:, fargs, argtypes, vtypes, sv) - return rty + rty, _ = abstract_call_known(interp, <:, fargs, argtypes, vtypes, sv) + return rty, nothing elseif la == 2 && isa(argtypes[2], Const) && isa(argtypes[2].val, SimpleVector) && istopfunction(f, :length) # mark length(::SimpleVector) as @pure - return Const(length(argtypes[2].val)) + return Const(length(argtypes[2].val)), nothing elseif la == 3 && isa(argtypes[2], Const) && isa(argtypes[3], Const) && isa(argtypes[2].val, SimpleVector) && isa(argtypes[3].val, Int) && istopfunction(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)) + return Const(getindex(svecval, idx)), nothing end elseif la == 2 && istopfunction(f, :typename) - return typename_static(argtypes[2]) + return typename_static(argtypes[2]), nothing elseif max_methods > 1 && istopfunction(f, :copyto!) max_methods = 1 elseif la == 3 && istopfunction(f, :typejoin) val = pure_eval_call(f, argtypes) - return val === false ? Type : val + return val === false ? Type : val, nothing end atype = argtypes_to_type(argtypes) @@ -933,7 +951,7 @@ function abstract_call(interp::AbstractInterpreter, fargs::Union{Nothing,Vector{ # non-constant function, but the number of arguments is known # and the ft is not a Builtin or IntrinsicFunction if typeintersect(widenconst(ft), Builtin) != Union{} - return Any + return Any, nothing end return abstract_call_gf_by_type(interp, nothing, argtypes, argtypes_to_type(argtypes), sv, max_methods) end @@ -1027,10 +1045,10 @@ function abstract_eval_value(interp::AbstractInterpreter, @nospecialize(e), vtyp end function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), vtypes::VarTable, sv::InferenceState) + local info = nothing if !isa(e, Expr) - return abstract_eval_special_value(interp, e, vtypes, sv) + return abstract_eval_special_value(interp, e, vtypes, sv), info end - e = e::Expr if e.head === :call ea = e.args @@ -1039,11 +1057,11 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), @inbounds for i = 1:n ai = abstract_eval_value(interp, ea[i], vtypes, sv) if ai === Bottom - return Bottom + return Bottom, nothing end argtypes[i] = ai end - t = abstract_call(interp, ea, argtypes, vtypes, sv) + t, info = abstract_call(interp, ea, argtypes, vtypes, sv) elseif e.head === :new t = instanceof_tfunc(abstract_eval_value(interp, e.args[1], vtypes, sv))[1] if isconcretetype(t) && !t.mutable @@ -1144,14 +1162,14 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), end end else - return abstract_eval_value_expr(interp, e, vtypes, sv) + return abstract_eval_value_expr(interp, e, vtypes, sv), nothing end @assert !isa(t, TypeVar) if isa(t, DataType) && isdefined(t, :instance) # replace singleton types with their equivalent Const object t = Const(t.instance) end - return t + return t, info end function abstract_eval_global(M::Module, s::Symbol) @@ -1282,7 +1300,8 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) end else if hd === :(=) - t = abstract_eval_statement(interp, stmt.args[2], changes, frame) + t, info = abstract_eval_statement(interp, stmt.args[2], changes, frame) + frame.src.stmtinfo[pc] = info t === Bottom && break frame.src.ssavaluetypes[pc] = t lhs = stmt.args[1] @@ -1297,7 +1316,8 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) elseif hd === :inbounds || hd === :meta || hd === :loopinfo || hd == :code_coverage_effect # these do not generate code else - t = abstract_eval_statement(interp, stmt, changes, frame) + t, info = abstract_eval_statement(interp, stmt, changes, frame) + frame.src.stmtinfo[pc] = info t === Bottom && break if !isempty(frame.ssavalue_uses[pc]) record_ssa_assign(pc, t, frame) diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl index 48a2d1e30adf3..e496963d13cf4 100644 --- a/base/compiler/compiler.jl +++ b/base/compiler/compiler.jl @@ -106,6 +106,7 @@ include("compiler/typeutils.jl") include("compiler/typelimits.jl") include("compiler/typelattice.jl") include("compiler/tfuncs.jl") +include("compiler/stmtinfo.jl") include("compiler/abstractinterpretation.jl") include("compiler/typeinfer.jl") diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index deee51793806d..c840cdaeada9b 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -61,6 +61,7 @@ mutable struct InferenceState nssavalues = src.ssavaluetypes::Int src.ssavaluetypes = Any[ NOT_FOUND for i = 1:nssavalues ] + src.stmtinfo = Any[ nothing for i = 1:length(code) ] n = length(code) s_edges = Any[ nothing for i = 1:n ] diff --git a/base/compiler/ssair/driver.jl b/base/compiler/ssair/driver.jl index b6fd4a788a5ce..387b06fc3374b 100644 --- a/base/compiler/ssair/driver.jl +++ b/base/compiler/ssair/driver.jl @@ -101,7 +101,7 @@ function convert_to_ircode(ci::CodeInfo, code::Vector{Any}, coverage::Bool, narg strip_trailing_junk!(ci, code, flags) cfg = compute_basic_blocks(code) types = Any[] - stmts = InstructionStream(code, types, ci.codelocs, flags) + stmts = InstructionStream(code, types, ci.stmtinfo, ci.codelocs, flags) ir = IRCode(stmts, cfg, collect(LineInfoNode, ci.linetable), sv.slottypes, meta, sv.sptypes) return ir end diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index 9204590fb882b..a9705fdc2fc30 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -160,15 +160,18 @@ end struct InstructionStream inst::Vector{Any} type::Vector{Any} + info::Vector{Any} line::Vector{Int32} flag::Vector{UInt8} end function InstructionStream(len::Int) insts = Array{Any}(undef, len) types = Array{Any}(undef, len) + info = Array{Any}(undef, len) + fill!(info, nothing) lines = fill(Int32(0), len) flags = fill(0x00, len) - return InstructionStream(insts, types, lines, flags) + return InstructionStream(insts, types, info, lines, flags) end InstructionStream() = InstructionStream(0) length(is::InstructionStream) = length(is.inst) @@ -177,6 +180,7 @@ function add!(is::InstructionStream) ninst = length(is) + 1 resize!(is.inst, ninst) resize!(is.type, ninst) + resize!(is.info, ninst) resize!(is.line, ninst) resize!(is.flag, ninst) return ninst @@ -192,16 +196,17 @@ function resize!(stmts::InstructionStream, len) old_length = length(stmts) resize!(stmts.inst, len) resize!(stmts.type, len) + resize!(stmts.info, len) resize!(stmts.line, len) resize!(stmts.flag, len) for i in (old_length + 1):len stmts.line[i] = 0 stmts.flag[i] = 0x00 + stmts.info[i] = nothing end return stmts end - struct Instruction data::InstructionStream idx::Int @@ -221,6 +226,7 @@ end function setindex!(is::InstructionStream, newval::Instruction, idx::Int) is.inst[idx] = newval[:inst] is.type[idx] = newval[:type] + #is.info[idx] = newval[:info] is.line[idx] = newval[:line] is.flag[idx] = newval[:flag] return is diff --git a/base/compiler/ssair/legacy.jl b/base/compiler/ssair/legacy.jl index c36cb73fac828..b866ac0f531be 100644 --- a/base/compiler/ssair/legacy.jl +++ b/base/compiler/ssair/legacy.jl @@ -32,7 +32,7 @@ function inflate_ir(ci::CodeInfo, sptypes::Vector{Any}, argtypes::Vector{Any}) ssavaluetypes = ci.ssavaluetypes nstmts = length(code) ssavaluetypes = ci.ssavaluetypes isa Vector{Any} ? copy(ci.ssavaluetypes) : Any[ Any for i = 1:(ci.ssavaluetypes::Int) ] - stmts = InstructionStream(code, ssavaluetypes, copy(ci.codelocs), copy(ci.ssaflags)) + stmts = InstructionStream(code, ssavaluetypes, copy(ci.stmtinfo), copy(ci.codelocs), copy(ci.ssaflags)) ir = IRCode(stmts, cfg, collect(LineInfoNode, ci.linetable), argtypes, Any[], sptypes) return ir end diff --git a/base/compiler/stmtinfo.jl b/base/compiler/stmtinfo.jl new file mode 100644 index 0000000000000..24fa2fb06280a --- /dev/null +++ b/base/compiler/stmtinfo.jl @@ -0,0 +1,7 @@ +struct MethodMatchInfo + applicable::Vector{Any} +end + +struct UnionSplitInfo + matches::Vector{MethodMatchInfo} +end diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index fb4d8549295af..da6e81ec1dacc 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -1488,7 +1488,7 @@ function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, v if contains_is(argtypes_vec, Union{}) return Const(Union{}) end - rt = abstract_call(interp, nothing, argtypes_vec, vtypes, sv, -1) + rt , _ = abstract_call(interp, nothing, argtypes_vec, vtypes, sv, -1) if isa(rt, Const) # output was computed to be constant return Const(typeof(rt.val)) diff --git a/src/codegen.cpp b/src/codegen.cpp index 5fecee611e82c..a9cb879a0fd67 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6186,6 +6186,7 @@ static std::pair, jl_llvm_functions_t> }; std::vector linetable; { + assert(jl_is_array(src->linetable)); size_t nlocs = jl_array_len(src->linetable); std::map, DISubprogram*> subprograms; linetable.resize(nlocs + 1); diff --git a/src/ircode.c b/src/ircode.c index a3f602dd2f2e1..b65275e546dda 100644 --- a/src/ircode.c +++ b/src/ircode.c @@ -708,13 +708,13 @@ JL_DLLEXPORT jl_array_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code) // by the various jl_ir_ accessors. Make sure to adjust those if you change // the data layout. - for (i = 0; i < 6; i++) { + for (i = 0; i < 7; i++) { int copy = 1; if (i == 1) { // skip codelocs assert(jl_field_offset(jl_code_info_type, i) == offsetof(jl_code_info_t, codelocs)); continue; } - if (i == 4) { // don't copy contents of method_for_inference_limit_heuristics field + if (i == 5) { // don't copy contents of method_for_inference_limit_heuristics field assert(jl_field_offset(jl_code_info_type, i) == offsetof(jl_code_info_t, method_for_inference_limit_heuristics)); copy = 0; } @@ -787,7 +787,7 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t code->slotflags = jl_alloc_array_1d(jl_array_uint8_type, nslots); ios_readall(s.s, (char*)jl_array_data(code->slotflags), nslots); - for (i = 0; i < 6; i++) { + for (i = 0; i < 7; i++) { if (i == 1) // skip codelocs continue; assert(jl_field_isptr(jl_code_info_type, i)); diff --git a/src/jltypes.c b/src/jltypes.c index c7d5922a1d850..f9a84ae67b361 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2232,11 +2232,12 @@ void jl_init_types(void) JL_GC_DISABLED jl_code_info_type = jl_new_datatype(jl_symbol("CodeInfo"), core, jl_any_type, jl_emptysvec, - jl_perm_symsvec(18, + jl_perm_symsvec(19, "code", "codelocs", "ssavaluetypes", "ssaflags", + "stmtinfo", "method_for_inference_limit_heuristics", "linetable", "slotnames", @@ -2251,11 +2252,12 @@ void jl_init_types(void) JL_GC_DISABLED "inlineable", "propagate_inbounds", "pure"), - jl_svec(18, + jl_svec(19, jl_array_any_type, jl_any_type, jl_any_type, jl_array_uint8_type, + jl_array_any_type, jl_any_type, jl_any_type, jl_array_symbol_type, @@ -2270,7 +2272,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_bool_type, jl_bool_type, jl_bool_type), - 0, 1, 18); + 0, 1, 19); jl_method_type = jl_new_datatype(jl_symbol("Method"), core, diff --git a/src/julia.h b/src/julia.h index 1536ed465f4d5..fdde2ace6c3c0 100644 --- a/src/julia.h +++ b/src/julia.h @@ -252,6 +252,7 @@ typedef struct _jl_code_info_t { // 4-6 = // 7 = has out-of-band info // miscellaneous data: + jl_array_t *stmtinfo; // side-channel information from inference to optimize jl_value_t *method_for_inference_limit_heuristics; // optional method used during inference jl_value_t *linetable; // Table of locations [TODO: make this volatile like slotnames] jl_array_t *slotnames; // names of local variables diff --git a/src/method.c b/src/method.c index 4d9ab188c960d..5440f51f1bb6c 100644 --- a/src/method.c +++ b/src/method.c @@ -329,6 +329,7 @@ JL_DLLEXPORT jl_code_info_t *jl_new_code_info_uninit(void) src->codelocs = jl_nothing; src->ssavaluetypes = NULL; src->ssaflags = NULL; + src->stmtinfo = NULL; src->method_for_inference_limit_heuristics = jl_nothing; src->linetable = jl_nothing; src->slotflags = NULL;