Skip to content

Commit a82a28f

Browse files
authored
newinterp: add external cache mode (#54074)
When working on debugging inference behavior or performance tuning, having direct access to inspect the global cache can be really handy. Right now, `@newinterp` utilizes the `Core.Compiler.InternalCodeCache` for its global caching, which doesn’t allow for such inspections. This commit introduces the `@newinterp NewInterp cache_externally=true` option. This enhancement allows `NewInterp` to store `CodeInstance` in an external cache whose type is `IdDict{MethodInstance, CodeInstance}`.
1 parent e5821b3 commit a82a28f

File tree

2 files changed

+49
-5
lines changed

2 files changed

+49
-5
lines changed

test/compiler/AbstractInterpreter.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,3 +514,20 @@ let src = code_typed((Int,); interp=CustomDataInterp()) do x
514514
@test count(isinvoke(:cos), src.code) == 1
515515
@test count(isinvoke(:+), src.code) == 0
516516
end
517+
518+
# ephemeral cache mode
519+
@newinterp DebugInterp #=ephemeral_cache=#true
520+
func_ext_cache1(a) = func_ext_cache2(a) * cos(a)
521+
func_ext_cache2(a) = sin(a)
522+
let interp = DebugInterp()
523+
@test Base.infer_return_type(func_ext_cache1, (Float64,); interp) === Float64
524+
@test isdefined(interp, :code_cache)
525+
found = false
526+
for (mi, codeinst) in interp.code_cache.dict
527+
if mi.def.name === :func_ext_cache2
528+
found = true
529+
break
530+
end
531+
end
532+
@test found
533+
end

test/compiler/newinterp.jl

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,62 @@
33
# TODO set up a version who defines new interpreter with persistent cache?
44

55
"""
6-
@newinterp NewInterpreter
6+
@newinterp NewInterpreter [ephemeral_cache::Bool=false]
77
88
Defines new `NewInterpreter <: AbstractInterpreter` whose cache is separated
99
from the native code cache, satisfying the minimum interface requirements.
10+
11+
When the `ephemeral_cache=true` option is specified, `NewInterpreter` will hold
12+
`CodeInstance` in an ephemeral non-integrated cache, rather than in the integrated
13+
`Core.Compiler.InternalCodeCache`.
14+
Keep in mind that ephemeral cache lacks support for invalidation and doesn't persist across
15+
sessions. However it is an usual Julia object of the type `code_cache::IdDict{MethodInstance,CodeInstance}`,
16+
making it easier for debugging and inspecting the compiler behavior.
1017
"""
11-
macro newinterp(InterpName)
12-
cache_token = QuoteNode(gensym(string(InterpName, "Cache")))
18+
macro newinterp(InterpName, ephemeral_cache::Bool=false)
19+
cache_token = QuoteNode(gensym(string(InterpName, "CacheToken")))
20+
InterpCacheName = esc(Symbol(string(InterpName, "Cache")))
1321
InterpName = esc(InterpName)
1422
C = Core
1523
CC = Core.Compiler
1624
quote
25+
$(ephemeral_cache && quote
26+
struct $InterpCacheName
27+
dict::IdDict{$C.MethodInstance,$C.CodeInstance}
28+
end
29+
$InterpCacheName() = $InterpCacheName(IdDict{$C.MethodInstance,$C.CodeInstance}())
30+
end)
1731
struct $InterpName <: $CC.AbstractInterpreter
1832
meta # additional information
1933
world::UInt
2034
inf_params::$CC.InferenceParams
2135
opt_params::$CC.OptimizationParams
2236
inf_cache::Vector{$CC.InferenceResult}
37+
$(ephemeral_cache && :(code_cache::$InterpCacheName))
2338
function $InterpName(meta = nothing;
2439
world::UInt = Base.get_world_counter(),
2540
inf_params::$CC.InferenceParams = $CC.InferenceParams(),
2641
opt_params::$CC.OptimizationParams = $CC.OptimizationParams(),
27-
inf_cache::Vector{$CC.InferenceResult} = $CC.InferenceResult[])
28-
return new(meta, world, inf_params, opt_params, inf_cache)
42+
inf_cache::Vector{$CC.InferenceResult} = $CC.InferenceResult[],
43+
$(ephemeral_cache ?
44+
Expr(:kw, :(code_cache::$InterpCacheName), :($InterpCacheName())) :
45+
Expr(:kw, :_, :nothing)))
46+
return $(ephemeral_cache ?
47+
:(new(meta, world, inf_params, opt_params, inf_cache, code_cache)) :
48+
:(new(meta, world, inf_params, opt_params, inf_cache)))
2949
end
3050
end
3151
$CC.InferenceParams(interp::$InterpName) = interp.inf_params
3252
$CC.OptimizationParams(interp::$InterpName) = interp.opt_params
3353
$CC.get_inference_world(interp::$InterpName) = interp.world
3454
$CC.get_inference_cache(interp::$InterpName) = interp.inf_cache
3555
$CC.cache_owner(::$InterpName) = $cache_token
56+
$(ephemeral_cache && quote
57+
$CC.code_cache(interp::$InterpName) = $CC.WorldView(interp.code_cache, $CC.WorldRange(interp.world))
58+
$CC.get(wvc::$CC.WorldView{$InterpCacheName}, mi::$C.MethodInstance, default) = get(wvc.cache.dict, mi, default)
59+
$CC.getindex(wvc::$CC.WorldView{$InterpCacheName}, mi::$C.MethodInstance) = getindex(wvc.cache.dict, mi)
60+
$CC.haskey(wvc::$CC.WorldView{$InterpCacheName}, mi::$C.MethodInstance) = haskey(wvc.cache.dict, mi)
61+
$CC.setindex!(wvc::$CC.WorldView{$InterpCacheName}, ci::$C.CodeInstance, mi::$C.MethodInstance) = setindex!(wvc.cache.dict, ci, mi)
62+
end)
3663
end
3764
end

0 commit comments

Comments
 (0)