Skip to content

Commit dd56353

Browse files
authored
use our own struct for the local method table instead of TypeMapEntry (#313)
* use our own struct for the local method table instead of TypeMapEntry * also cache calls that end up getting compiled in local method table * add a typeassert
1 parent d1fbf8d commit dd56353

File tree

5 files changed

+61
-44
lines changed

5 files changed

+61
-44
lines changed

src/JuliaInterpreter.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module JuliaInterpreter
22

33
using Base.Meta
44
import Base: +, -, convert, isless
5-
using Core: CodeInfo, TypeMapEntry, SimpleVector, LineInfoNode, GotoNode, Slot,
5+
using Core: CodeInfo, SimpleVector, LineInfoNode, GotoNode, Slot,
66
GeneratedFunctionStub, MethodInstance, NewvarNode, TypeName
77

88
using UUIDs

src/construct.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ function prepare_call(@nospecialize(f), allargs; enter_generated = false)
262262
# The generator threw an error. Let's generate the same error by calling it.
263263
f(allargs[2:end]...)
264264
end
265-
isa(ret, Compiled) && return ret
265+
isa(ret, Compiled) && return ret, argtypes
266266
# Typical return
267267
framecode, lenv = ret
268268
if is_generated(method) && enter_generated
@@ -550,7 +550,7 @@ See [`enter_call`](@ref) for a similar approach not based on expressions.
550550
function enter_call_expr(expr; enter_generated = false)
551551
clear_caches()
552552
r = determine_method_for_expr(expr; enter_generated = enter_generated)
553-
if isa(r, Tuple)
553+
if r !== nothing && !isa(r[1], Compiled)
554554
return prepare_frame(r[1:end-1]...)
555555
end
556556
nothing
@@ -604,7 +604,7 @@ function enter_call(@nospecialize(finfo), @nospecialize(args...); kwargs...)
604604
error(f, " is a builtin or intrinsic")
605605
end
606606
r = prepare_call(f, allargs; enter_generated=enter_generated)
607-
if isa(r, Tuple)
607+
if r !== nothing && !isa(r[1], Compiled)
608608
return prepare_frame(r[1:end-1]...)
609609
end
610610
return nothing

src/localmethtable.jl

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,22 @@ function get_call_framecode(fargs::Vector{Any}, parentframe::FrameCode, idx::Int
1111
nargs = length(fargs) # includes f as the first "argument"
1212
# Determine whether we can look up the appropriate framecode in the local method table
1313
if isassigned(parentframe.methodtables, idx) # if this is the first call, this may not yet be set
14-
tme = tme1 = parentframe.methodtables[idx]::TypeMapEntry
15-
local tmeprev
14+
# The case where `methodtables[idx]` is a `Compiled` has already been handled in `bypass_builtins`
15+
d_meth = d_meth1 = parentframe.methodtables[idx]::DispatchableMethod
16+
local d_methprev
1617
depth = 1
1718
while true
1819
# TODO: consider using world age bounds to handle cache invalidation
1920
# Determine whether the argument types match the signature
20-
sig = tme.sig.parameters::SimpleVector
21+
sig = d_meth.sig.parameters::SimpleVector
2122
if length(sig) == nargs
2223
# If this is generated, match only if `enter_generated` also matches
23-
mi = tme.func::FrameInstance
24-
matches = !is_generated(scopeof(mi.framecode)) || enter_generated == mi.enter_generated
24+
fi = d_meth.frameinstance
25+
if fi isa FrameInstance
26+
matches = !is_generated(scopeof(fi.framecode)) || enter_generated == fi.enter_generated
27+
else
28+
matches = !enter_generated
29+
end
2530
if matches
2631
for i = 1:nargs
2732
if !isa(fargs[i], sig[i])
@@ -34,54 +39,59 @@ function get_call_framecode(fargs::Vector{Any}, parentframe::FrameCode, idx::Int
3439
# Rearrange the list to place this method first
3540
# (if we're in a loop, we'll likely match this one again on the next iteration)
3641
if depth > 1
37-
parentframe.methodtables[idx] = tme
38-
tmeprev.next = tme.next
39-
tme.next = tme1
42+
parentframe.methodtables[idx] = d_meth
43+
d_methprev.next = d_meth.next
44+
d_meth.next = d_meth1
45+
end
46+
if fi isa Compiled
47+
return Compiled(), nothing
48+
else
49+
fi = fi::FrameInstance
50+
return fi.framecode, fi.sparam_vals
4051
end
41-
return mi.framecode, mi.sparam_vals
4252
end
4353
end
4454
depth += 1
45-
tmeprev = tme
46-
tme = tme.next
47-
tme === nothing && break
48-
tme = tme::TypeMapEntry
55+
d_methprev = d_meth
56+
d_meth = d_meth.next
57+
d_meth === nothing && break
58+
d_meth = d_meth::DispatchableMethod
4959
end
5060
end
5161
# We haven't yet encountered this argtype combination and need to look it up by dispatch
5262
fargs[1] = f = to_function(fargs[1])
5363
ret = prepare_call(f, fargs; enter_generated=enter_generated)
5464
ret === nothing && return f(fargs[2:end]...), nothing
55-
isa(ret, Compiled) && return ret, nothing
56-
framecode, args, env, argtypes = ret
57-
# Store the results of the method lookup in the local method table
58-
mi = FrameInstance(framecode, env, is_generated(scopeof(framecode)) & enter_generated)
59-
# it's sort of odd to call this a TypeMapEntry, then set most of the fields incorrectly
60-
# but since we're just using it as a linked list, it's probably ok
61-
tme = ccall(:jl_new_struct_uninit, Any, (Any,), TypeMapEntry)::TypeMapEntry
62-
tme.func = mi
63-
tme.simplesig = nothing
64-
tme.sig = argtypes
65-
tme.isleafsig = true
66-
tme.issimplesig = false
67-
method = framecode.scope::Method
68-
tme.va = method.isva
65+
is_compiled = isa(ret[1], Compiled)
66+
local framecode
67+
if is_compiled
68+
d_meth = DispatchableMethod(nothing, Compiled(), ret[2])
69+
else
70+
framecode, args, env, argtypes = ret
71+
# Store the results of the method lookup in the local method table
72+
fi = FrameInstance(framecode, env, is_generated(scopeof(framecode)) && enter_generated)
73+
d_meth = DispatchableMethod(nothing, fi, argtypes)
74+
end
6975
if isassigned(parentframe.methodtables, idx)
70-
tme.next = parentframe.methodtables[idx]
71-
# Drop the oldest tme, if necessary
72-
tmetmp = tme.next
76+
d_meth.next = parentframe.methodtables[idx]
77+
# Drop the oldest d_meth, if necessary
78+
d_methtmp = d_meth.next
7379
depth = 2
74-
while isdefined(tmetmp, :next) && tmetmp.next !== nothing
80+
while d_methtmp.next !== nothing
7581
depth += 1
76-
tmetmp = tmetmp.next
82+
d_methtmp = d_methtmp.next
7783
depth >= max_methods && break
7884
end
7985
if depth >= max_methods
80-
tmetmp.next = nothing
86+
d_methtmp.next = nothing
8187
end
8288
else
83-
tme.next = nothing
89+
d_meth.next = nothing
90+
end
91+
parentframe.methodtables[idx] = d_meth
92+
if is_compiled
93+
return Compiled(), nothing
94+
else
95+
return framecode, env
8496
end
85-
parentframe.methodtables[idx] = tme
86-
return framecode, env
8797
end

src/optimize.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ function optimize!(code::CodeInfo, scope)
210210
code.ssavaluetypes = length(new_code)
211211

212212
# Insert the foreigncall wrappers at the updated idxs
213-
methodtables = Vector{Union{Compiled,TypeMapEntry}}(undef, length(code.code))
213+
methodtables = Vector{Union{Compiled,DispatchableMethod}}(undef, length(code.code))
214214
for idx in foreigncalls_idx
215215
methodtables[ssalookup[idx]] = Compiled()
216216
end

src/types.jl

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ function breakpointchar(bps::BreakpointState)
5353
return bps.condition === falsecondition ? ' ' : 'd' # no breakpoint : disabled
5454
end
5555

56+
abstract type AbstractFrameInstance end
57+
mutable struct DispatchableMethod
58+
next::Union{Nothing,DispatchableMethod} # linked-list representation
59+
frameinstance::Union{Compiled, AbstractFrameInstance} # really a Union{Compiled, FrameInstance} but we have a cyclic dependency
60+
sig::Type # for speed of matching, this is a *concrete* signature. `sig <: frameinstance.framecode.scope.sig`
61+
end
62+
5663
"""
5764
`FrameCode` holds static information about a method or toplevel code.
5865
One `FrameCode` can be shared by many calling `Frame`s.
@@ -68,7 +75,7 @@ Important fields:
6875
struct FrameCode
6976
scope::Union{Method,Module}
7077
src::CodeInfo
71-
methodtables::Vector{Union{Compiled,TypeMapEntry}} # line-by-line method tables for generic-function :call Exprs
78+
methodtables::Vector{Union{Compiled,DispatchableMethod}} # line-by-line method tables for generic-function :call Exprs
7279
breakpoints::Vector{BreakpointState}
7380
slotnamelists::Dict{Symbol,Vector{Int}}
7481
used::BitSet
@@ -81,7 +88,7 @@ function FrameCode(scope, src::CodeInfo; generator=false, optimize=true)
8188
src, methodtables = optimize!(copy_codeinfo(src), scope)
8289
else
8390
src = replace_coretypes!(copy_codeinfo(src))
84-
methodtables = Vector{Union{Compiled,TypeMapEntry}}(undef, length(src.code))
91+
methodtables = Vector{Union{Compiled,DispatchableMethod}}(undef, length(src.code))
8592
end
8693
breakpoints = Vector{BreakpointState}(undef, length(src.code))
8794
for (i, pc_expr) in enumerate(src.code)
@@ -124,7 +131,7 @@ Fields:
124131
- `framecode`: the [`FrameCode`](@ref) for the method.
125132
- `sparam_vals`: the static parameter values for the method.
126133
"""
127-
struct FrameInstance
134+
struct FrameInstance <: AbstractFrameInstance
128135
framecode::FrameCode
129136
sparam_vals::SimpleVector
130137
enter_generated::Bool

0 commit comments

Comments
 (0)