Skip to content

Commit 94085d0

Browse files
authored
Adapt handling of world ages to upstream changes (#403)
1 parent 7e0a6ff commit 94085d0

File tree

1 file changed

+90
-10
lines changed

1 file changed

+90
-10
lines changed

src/cache.jl

Lines changed: 90 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,98 @@ Normally, you shouldn't have to use this function, as it's used by `FunctionSpec
3838
"""
3939
get_world
4040

41-
# generate functions currently do not know which world they are invoked for, so we fall
42-
# back to using the current world. this may be wrong when the generator is invoked in a
43-
# different world (TODO: when does this happen?)
44-
#
45-
# XXX: this should be fixed by JuliaLang/julia#48611
41+
if VERSION >= v"1.10.0-DEV.873"
4642

47-
function get_world_generator(self, ::Type{Type{ft}}, ::Type{Type{tt}}) where {ft, tt}
43+
# on 1.10 (JuliaLang/julia#48611) the generated function knows which world it was invoked in
44+
45+
function _generated_ex(world, source, ex)
46+
stub = Core.GeneratedFunctionStub(identity, Core.svec(:get_world, :ft, :tt), Core.svec())
47+
stub(world, source, ex)
48+
end
49+
50+
function get_world_generator(world::UInt, source, self, ft::Type, tt::Type)
4851
@nospecialize
52+
@assert Core.Compiler.isType(ft) && Core.Compiler.isType(tt)
53+
ft = ft.parameters[1]
54+
tt = tt.parameters[1]
4955

5056
# look up the method
57+
method_error = :(throw(MethodError(ft, tt, $world)))
58+
Base.isdispatchtuple(tt) || return _generated_ex(world, source, :(error("$tt is not a dispatch tuple")))
59+
sig = Tuple{ft, tt.parameters...}
60+
min_world = Ref{UInt}(typemin(UInt))
61+
max_world = Ref{UInt}(typemax(UInt))
62+
has_ambig = Ptr{Int32}(C_NULL) # don't care about ambiguous results
63+
mthds = if VERSION >= v"1.7.0-DEV.1297"
64+
Base._methods_by_ftype(sig, #=mt=# nothing, #=lim=# -1,
65+
world, #=ambig=# false,
66+
min_world, max_world, has_ambig)
67+
# XXX: use the correct method table to support overlaying kernels
68+
else
69+
Base._methods_by_ftype(sig, #=lim=# -1,
70+
world, #=ambig=# false,
71+
min_world, max_world, has_ambig)
72+
end
73+
mthds === nothing && return _generated_ex(world, source, method_error)
74+
length(mthds) == 1 || return _generated_ex(world, source, method_error)
75+
76+
# look up the method and code instance
77+
mtypes, msp, m = mthds[1]
78+
mi = ccall(:jl_specializations_get_linfo, Ref{MethodInstance}, (Any, Any, Any), m, mtypes, msp)
79+
ci = retrieve_code_info(mi, world)::CodeInfo
80+
81+
# prepare a new code info
82+
new_ci = copy(ci)
83+
empty!(new_ci.code)
84+
empty!(new_ci.codelocs)
85+
resize!(new_ci.linetable, 1) # see note below
86+
empty!(new_ci.ssaflags)
87+
new_ci.ssavaluetypes = 0
88+
new_ci.min_world = min_world[]
89+
new_ci.max_world = max_world[]
90+
new_ci.edges = MethodInstance[mi]
91+
# XXX: setting this edge does not give us proper method invalidation, see
92+
# JuliaLang/julia#34962 which demonstrates we also need to "call" the kernel.
93+
# invoking `code_llvm` also does the necessary codegen, as does calling the
94+
# underlying C methods -- which GPUCompiler does, so everything Just Works.
95+
96+
# prepare the slots
97+
new_ci.slotnames = Symbol[Symbol("#self#"), :ft, :tt]
98+
new_ci.slotflags = UInt8[0x00 for i = 1:3]
99+
100+
# return the world
101+
push!(new_ci.code, ReturnNode(world))
102+
push!(new_ci.ssaflags, 0x00) # Julia's native compilation pipeline (and its verifier) expects `ssaflags` to be the same length as `code`
103+
push!(new_ci.codelocs, 1) # see note below
104+
new_ci.ssavaluetypes += 1
105+
106+
# NOTE: we keep the first entry of the original linetable, and use it for location info
107+
# on the call to check_cache. we can't not have a codeloc (using 0 causes
108+
# corruption of the back trace), and reusing the target function's info
109+
# has as advantage that we see the name of the kernel in the backtraces.
110+
111+
return new_ci
112+
end
113+
114+
@eval function get_world(ft, tt)
115+
$(Expr(:meta, :generated_only))
116+
$(Expr(:meta, :generated, get_world_generator))
117+
end
118+
119+
else
120+
121+
# on older versions of Julia we fall back to looking up the current world. this may be wrong
122+
# when the generator is invoked in a different world (TODO: when does this happen?)
123+
124+
function get_world_generator(self, ft::Type, tt::Type)
125+
@nospecialize
126+
@assert Core.Compiler.isType(ft) && Core.Compiler.isType(tt)
127+
ft = ft.parameters[1]
128+
tt = tt.parameters[1]
129+
130+
# look up the method
131+
method_error = :(throw(MethodError(ft, tt)))
132+
Base.isdispatchtuple(tt) || return(:(error("$tt is not a dispatch tuple")))
51133
sig = Tuple{ft, tt.parameters...}
52134
min_world = Ref{UInt}(typemin(UInt))
53135
max_world = Ref{UInt}(typemax(UInt))
@@ -63,11 +145,7 @@ function get_world_generator(self, ::Type{Type{ft}}, ::Type{Type{tt}}) where {ft
63145
min_world, max_world, has_ambig)
64146
end
65147
# XXX: using world=-1 is wrong, but the current world isn't exposed to this generator
66-
67-
# check the validity of the method matches
68-
method_error = :(throw(MethodError(ft, tt)))
69148
mthds === nothing && return method_error
70-
Base.isdispatchtuple(tt) || return(:(error("$tt is not a dispatch tuple")))
71149
length(mthds) == 1 || return method_error
72150

73151
# look up the method and code instance
@@ -127,6 +205,8 @@ end
127205
true)))
128206
end
129207

208+
end
209+
130210
const cache_lock = ReentrantLock()
131211

132212
"""

0 commit comments

Comments
 (0)