Skip to content

Commit 43dd067

Browse files
vchuravymaleadt
andauthored
Support Julia's internal cache (#545)
Co-authored-by: Tim Besard <tim.besard@gmail.com>
1 parent 7229e2b commit 43dd067

File tree

6 files changed

+122
-32
lines changed

6 files changed

+122
-32
lines changed

.github/workflows/ci.yml

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
strategy:
1919
fail-fast: false
2020
matrix:
21-
version: ['1.8', '1.9', '1.10'] # 'nightly'
21+
version: ['1.8', '1.9', '1.10', '^1.11.0-alpha1'] # 'nightly'
2222
os: [ubuntu-latest, macOS-latest, windows-latest]
2323
arch: [x64]
2424
llvm_args: ['']
@@ -36,6 +36,18 @@ jobs:
3636
os: 'windows-latest'
3737
arch: 'x64'
3838
llvm_args: '--opaque-pointers'
39+
- version: '^1.11.0-alpha1'
40+
os: 'ubuntu-latest'
41+
arch: 'x64'
42+
llvm_args: '--opaque-pointers'
43+
- version: '^1.11.0-alpha1'
44+
os: 'macOS-latest'
45+
arch: 'x64'
46+
llvm_args: '--opaque-pointers'
47+
- version: '^1.11.0-alpha1'
48+
os: 'windows-latest'
49+
arch: 'x64'
50+
llvm_args: '--opaque-pointers'
3951
#- version: 'nightly'
4052
# os: 'ubuntu-latest'
4153
# arch: 'x64'
@@ -96,9 +108,9 @@ jobs:
96108
include:
97109
- version: '1.11'
98110
pipeline: 'julia-release-1-dot-11'
99-
build: 'x86_64-linux-gnu'
111+
build: 'x86_64-linux-gnuassert'
100112
branch: 'release-1.11'
101-
#- version: '1.12'
113+
#- version: 'master'
102114
# pipeline: 'julia-master'
103115
# build: 'x86_64-linux-gnuassert'
104116
# branch: 'master'

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
test/Manifest.toml
22
Manifest.toml
3+
Manifest-*.toml

src/interface.jl

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,17 @@ runtime_module(@nospecialize(job::CompilerJob)) = error("Not implemented")
176176
isintrinsic(@nospecialize(job::CompilerJob), fn::String) = false
177177

178178
# provide a specific interpreter to use.
179+
if VERSION >= v"1.11.0-DEV.1552"
180+
get_interpreter(@nospecialize(job::CompilerJob)) =
181+
GPUInterpreter(job.world; method_table=method_table(job),
182+
token=ci_cache_token(job), inf_params=inference_params(job),
183+
opt_params=optimization_params(job))
184+
else
179185
get_interpreter(@nospecialize(job::CompilerJob)) =
180186
GPUInterpreter(job.world; method_table=method_table(job),
181187
code_cache=ci_cache(job), inf_params=inference_params(job),
182188
opt_params=optimization_params(job))
189+
end
183190

184191
# does this target support throwing Julia exceptions with jl_throw?
185192
# if not, calls to throw will be replaced with calls to the GPU runtime
@@ -207,7 +214,26 @@ needs_byval(@nospecialize(job::CompilerJob)) = true
207214
# whether pointer is a valid call target
208215
valid_function_pointer(@nospecialize(job::CompilerJob), ptr::Ptr{Cvoid}) = false
209216

210-
# the codeinfo cache to use
217+
# Care is required for anything that impacts:
218+
# - method_table
219+
# - inference_params
220+
# - optimization_params
221+
# By default that is just always_inline
222+
# the cache token is compared with jl_egal
223+
struct GPUCompilerCacheToken
224+
target_type::Type
225+
always_inline::Bool
226+
method_table::Core.MethodTable
227+
end
228+
229+
ci_cache_token(@nospecialize(job::CompilerJob)) =
230+
GPUCompilerCacheToken(typeof(job.config.target), job.config.always_inline, method_table(job))
231+
232+
# the codeinfo cache to use -- should only be used for the constructor
233+
if VERSION >= v"1.11.0-DEV.1552"
234+
# Soft deprecated user should use `CC.code_cache(get_interpreter(job))`
235+
ci_cache(@nospecialize(job::CompilerJob)) = CC.code_cache(get_interpreter(job))
236+
else
211237
function ci_cache(@nospecialize(job::CompilerJob))
212238
lock(GLOBAL_CI_CACHES_LOCK) do
213239
cache = get!(GLOBAL_CI_CACHES, job.config) do
@@ -216,6 +242,7 @@ function ci_cache(@nospecialize(job::CompilerJob))
216242
return cache
217243
end
218244
end
245+
end
219246

220247
# the method table to use
221248
method_table(@nospecialize(job::CompilerJob)) = GLOBAL_METHOD_TABLE

src/jlgen.jl

Lines changed: 77 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66
# `tls_world_age` should be used to look up the current world age. in most cases, this is
77
# what you should use to invoke the compiler with.
88

9-
tls_world_age() = ccall(:jl_get_tls_world_age, UInt, ())
10-
9+
if isdefined(Base, :tls_world_age)
10+
import Base: tls_world_age
11+
else
12+
tls_world_age() = ccall(:jl_get_tls_world_age, UInt, ())
13+
end
1114

1215
## looking up method instances
1316

14-
export methodinstance
17+
export methodinstance, generic_methodinstance
1518

1619
@inline function signature_type_by_tt(ft::Type, tt::Type)
1720
u = Base.unwrap_unionall(tt)::DataType
@@ -49,21 +52,38 @@ end
4952
Look up the method instance that corresponds to invoking the function with type `ft` with
5053
argument typed `tt`. If the `world` argument is specified, the look-up is static and will
5154
always return the same result. If the `world` argument is not specified, the look-up is
52-
dynamic and the returned method instance will depende on the current world age.
55+
dynamic and the returned method instance will depende on the current world age. If no method
56+
is found, a `MethodError` is thrown.
57+
58+
This function is highly optimized, and results do not need to be cached additionally.
5359
54-
This call is highly optimized, and does not need to be cached additionally.
60+
Only use this function with concrete signatures, i.e., using the types of values you would
61+
pass at run time. For non-concrete signatures, use `generic_methodinstance` instead.
5562
56-
If the method is not found, a `MethodError` is thrown.
5763
"""
5864
methodinstance
5965

66+
function generic_methodinstance(@nospecialize(ft::Type), @nospecialize(tt::Type),
67+
world::Integer=tls_world_age())
68+
sig = signature_type_by_tt(ft, tt)
69+
70+
match, _ = CC._findsup(sig, nothing, world)
71+
match === nothing && throw(MethodError(ft, tt, world))
72+
73+
mi = CC.specialize_method(match)
74+
75+
return mi::MethodInstance
76+
end
77+
6078
# on 1.11 (JuliaLang/julia#52572, merged as part of JuliaLang/julia#52233) we can use
6179
# Julia's cached method lookup to simply look up method instances at run time.
6280
if VERSION >= v"1.11.0-DEV.1552"
6381

6482
# XXX: version of Base.method_instance that uses a function type
65-
@inline function methodinstance(@nospecialize(ft::Type), @nospecialize(tt::Type), world::Integer=tls_world_age())
83+
@inline function methodinstance(@nospecialize(ft::Type), @nospecialize(tt::Type),
84+
world::Integer=tls_world_age())
6685
sig = signature_type_by_tt(ft, tt)
86+
@assert Base.isdispatchtuple(sig) # JuliaLang/julia#52233
6787

6888
mi = ccall(:jl_method_lookup_by_tt, Any,
6989
(Any, Csize_t, Any),
@@ -79,19 +99,10 @@ if VERSION >= v"1.11.0-DEV.1552"
7999
return mi
80100
end
81101

82-
# on older versions of Julia, the run-time lookup is much slower, so we'll need to cache it
102+
# on older versions of Julia, we always need to use the generic lookup
83103
else
84104

85-
function methodinstance(ft::Type, tt::Type, world::Integer)
86-
sig = signature_type_by_tt(ft, tt)
87-
88-
match, _ = CC._findsup(sig, nothing, world)
89-
match === nothing && throw(MethodError(ft, tt, world))
90-
91-
mi = CC.specialize_method(match)
92-
93-
return mi::MethodInstance
94-
end
105+
const methodinstance = generic_methodinstance
95106

96107
# on 1.10 (JuliaLang/julia#48611) generated functions know which world to generate code for.
97108
# we can use this to cache and automatically invalidate method instance look-ups.
@@ -153,18 +164,15 @@ end
153164
$(Expr(:meta, :generated, methodinstance_generator))
154165
end
155166

156-
# on really old versions, we can't cache the run-time lookup
157-
else
158-
159-
methodinstance(f, tt) = methodinstance(f, tt, tls_world_age())
160-
161167
end
162168

163169
end
164170

165171

166172
## code instance cache
173+
const HAS_INTEGRATED_CACHE = VERSION >= v"1.11.0-DEV.1552"
167174

175+
if !HAS_INTEGRATED_CACHE
168176
struct CodeCache
169177
dict::IdDict{MethodInstance,Vector{CodeInstance}}
170178

@@ -292,6 +300,8 @@ function (callback::CodeCacheCallback)(replaced::MethodInstance, max_world::UInt
292300
end
293301

294302
end
303+
end # !HAS_INTEGRATED_CACHE
304+
295305

296306
## method overrides
297307

@@ -323,13 +333,47 @@ struct GPUInterpreter <: CC.AbstractInterpreter
323333
world::UInt
324334
method_table::GPUMethodTableView
325335

336+
@static if HAS_INTEGRATED_CACHE
337+
token::Any
338+
else
326339
code_cache::CodeCache
340+
end
327341
inf_cache::Vector{CC.InferenceResult}
328342

329343
inf_params::CC.InferenceParams
330344
opt_params::CC.OptimizationParams
331345
end
332346

347+
@static if HAS_INTEGRATED_CACHE
348+
function GPUInterpreter(world::UInt=Base.get_world_counter();
349+
method_table::MTType,
350+
token::Any,
351+
inf_params::CC.InferenceParams,
352+
opt_params::CC.OptimizationParams)
353+
@assert world <= Base.get_world_counter()
354+
355+
method_table = get_method_table_view(world, method_table)
356+
inf_cache = Vector{CC.InferenceResult}()
357+
358+
return GPUInterpreter(world, method_table,
359+
token, inf_cache,
360+
inf_params, opt_params)
361+
end
362+
363+
function GPUInterpreter(interp::GPUInterpreter;
364+
world::UInt=interp.world,
365+
method_table::GPUMethodTableView=interp.method_table,
366+
token::Any=interp.token,
367+
inf_cache::Vector{CC.InferenceResult}=interp.inf_cache,
368+
inf_params::CC.InferenceParams=interp.inf_params,
369+
opt_params::CC.OptimizationParams=interp.opt_params)
370+
return GPUInterpreter(world, method_table,
371+
token, inf_cache,
372+
inf_params, opt_params)
373+
end
374+
375+
else
376+
333377
function GPUInterpreter(world::UInt=Base.get_world_counter();
334378
method_table::MTType,
335379
code_cache::CodeCache,
@@ -356,12 +400,17 @@ function GPUInterpreter(interp::GPUInterpreter;
356400
code_cache, inf_cache,
357401
inf_params, opt_params)
358402
end
403+
end # HAS_INTEGRATED_CACHE
359404

360405
CC.InferenceParams(interp::GPUInterpreter) = interp.inf_params
361406
CC.OptimizationParams(interp::GPUInterpreter) = interp.opt_params
362407
#=CC.=#get_inference_world(interp::GPUInterpreter) = interp.world
363408
CC.get_inference_cache(interp::GPUInterpreter) = interp.inf_cache
364-
CC.code_cache(interp::GPUInterpreter) = WorldView(interp.code_cache, interp.world)
409+
if HAS_INTEGRATED_CACHE
410+
CC.cache_owner(interp::GPUInterpreter) = interp.token
411+
else
412+
CC.code_cache(interp::GPUInterpreter) = WorldView(interp.code_cache, interp.world)
413+
end
365414

366415
# No need to do any locking since we're not putting our results into the runtime cache
367416
CC.lock_mi_inference(interp::GPUInterpreter, mi::MethodInstance) = nothing
@@ -413,9 +462,10 @@ end
413462

414463

415464
## world view of the cache
416-
417465
using Core.Compiler: WorldView
418466

467+
if !HAS_INTEGRATED_CACHE
468+
419469
function CC.haskey(wvc::WorldView{CodeCache}, mi::MethodInstance)
420470
CC.get(wvc, mi, nothing) !== nothing
421471
end
@@ -454,6 +504,7 @@ function CC.setindex!(wvc::WorldView{CodeCache}, ci::CodeInstance, mi::MethodIns
454504
CC.setindex!(wvc.cache, ci, mi)
455505
end
456506

507+
end # HAS_INTEGRATED_CACHE
457508

458509
## codegen/inference integration
459510

@@ -526,8 +577,8 @@ end
526577

527578
function compile_method_instance(@nospecialize(job::CompilerJob))
528579
# populate the cache
529-
cache = ci_cache(job)
530580
interp = get_interpreter(job)
581+
cache = CC.code_cache(interp)
531582
if ci_cache_lookup(cache, job.source, job.world, job.world) === nothing
532583
ci_cache_populate(interp, cache, job.source, job.world, job.world)
533584
end

src/rtlib.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ end
5555

5656
function emit_function!(mod, config::CompilerConfig, f, method)
5757
tt = Base.to_tuple_type(method.types)
58-
source = methodinstance(f, tt)
58+
source = generic_methodinstance(f, tt)
5959
new_mod, meta = codegen(:llvm, CompilerJob(source, config);
6060
optimize=false, libraries=false, validate=false)
6161
ft = function_type(meta.entry)

test/Project.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
[deps]
22
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
3-
Cthulhu = "f68482b8-f384-11e8-15f7-abe071a5a75f"
43
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
54
LLVM = "929cbde3-209d-540e-8aea-75f648917ca0"
65
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"

0 commit comments

Comments
 (0)