Skip to content

Commit 93876c9

Browse files
authored
allow external absint to hold custom data in codeinst.inferred (#53300)
It has been possible for external abstract interpreters to keep custom data in `codeinst.inferred` (together /w overloading `inlining_policy`). After #52233, when such external absint uses `InternalCodeCache`, this data is passed to `jl_ir_flag_inferred`, leading to segfaults in assertion builds. This commit resolves the issue by omitting `jl_ir_flag_inferred` checks when the `cache_owner` is external. Nonetheless, a better resolution might be necessary. It suggests that the current design of `code_owner` and `InternalCodeCache` for the external cache system is somewhat flawed. A conceivable approach could involve: - Adding a layer similar to `inlining_policy` in `CC.get(::WorldView{InternalCodeCache})` to enable safe redirection of custom data to the native interpreter's implementation. - Prohibiting custom data in the `inferred` field and directing such data to be kept in `analysis_results`.
1 parent 88587ab commit 93876c9

File tree

4 files changed

+116
-9
lines changed

4 files changed

+116
-9
lines changed

base/compiler/types.jl

+3-6
Original file line numberDiff line numberDiff line change
@@ -373,20 +373,17 @@ end
373373
function NativeInterpreter(world::UInt = get_world_counter();
374374
inf_params::InferenceParams = InferenceParams(),
375375
opt_params::OptimizationParams = OptimizationParams())
376+
curr_max_world = get_world_counter()
376377
# Sometimes the caller is lazy and passes typemax(UInt).
377378
# we cap it to the current world age for correctness
378379
if world == typemax(UInt)
379-
world = get_world_counter()
380+
world = curr_max_world
380381
end
381-
382382
# If they didn't pass typemax(UInt) but passed something more subtly
383383
# incorrect, fail out loudly.
384-
@assert world <= get_world_counter()
385-
384+
@assert world <= curr_max_world
386385
method_table = CachedMethodTable(InternalMethodTable(world))
387-
388386
inf_cache = Vector{InferenceResult}() # Initially empty cache
389-
390387
return NativeInterpreter(world, method_table, inf_cache, inf_params, opt_params)
391388
end
392389

src/gf.c

+5-2
Original file line numberDiff line numberDiff line change
@@ -444,8 +444,11 @@ STATIC_INLINE jl_value_t *_jl_rettype_inferred(jl_value_t *owner, jl_method_inst
444444
if (jl_atomic_load_relaxed(&codeinst->min_world) <= min_world &&
445445
max_world <= jl_atomic_load_relaxed(&codeinst->max_world) &&
446446
jl_egal(codeinst->owner, owner)) {
447-
jl_value_t *code = jl_atomic_load_relaxed(&codeinst->inferred);
448-
if (code && (code == jl_nothing || jl_ir_flag_inferred(code)))
447+
jl_value_t *inferred = jl_atomic_load_relaxed(&codeinst->inferred);
448+
if (inferred && ((inferred == jl_nothing) || (
449+
// allow whatever code instance external abstract interpreter produced
450+
// since `jl_ir_flag_inferred` is specific to the native interpreter
451+
codeinst->owner != jl_nothing || jl_ir_flag_inferred(inferred))))
449452
return (jl_value_t*)codeinst;
450453
}
451454
codeinst = jl_atomic_load_relaxed(&codeinst->next);

test/compiler/AbstractInterpreter.jl

+32-1
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,6 @@ end
399399
Core.eval(Core.Compiler, quote f(;a=1) = a end)
400400
@test_throws MethodError Core.Compiler.f(;b=2)
401401

402-
403402
# Custom lookup function
404403
# ======================
405404

@@ -469,3 +468,35 @@ let # generate cache
469468
@test occursin("j_sin_", s)
470469
@test !occursin("j_cos_", s)
471470
end
471+
472+
# custom inferred data
473+
# ====================
474+
475+
@newinterp CustomDataInterp
476+
struct CustomDataInterpToken end
477+
CC.cache_owner(::CustomDataInterp) = CustomDataInterpToken()
478+
struct CustomData
479+
inferred
480+
CustomData(@nospecialize inferred) = new(inferred)
481+
end
482+
function CC.transform_result_for_cache(interp::CustomDataInterp,
483+
mi::Core.MethodInstance, valid_worlds::CC.WorldRange, result::CC.InferenceResult)
484+
inferred_result = @invoke CC.transform_result_for_cache(interp::CC.AbstractInterpreter,
485+
mi::Core.MethodInstance, valid_worlds::CC.WorldRange, result::CC.InferenceResult)
486+
return CustomData(inferred_result)
487+
end
488+
function CC.inlining_policy(interp::CustomDataInterp, @nospecialize(src),
489+
@nospecialize(info::CC.CallInfo), stmt_flag::UInt32)
490+
if src isa CustomData
491+
src = src.inferred
492+
end
493+
return @invoke CC.inlining_policy(interp::CC.AbstractInterpreter, src::Any,
494+
info::CC.CallInfo, stmt_flag::UInt32)
495+
end
496+
let src = code_typed((Int,); interp=CustomDataInterp()) do x
497+
return sin(x) + cos(x)
498+
end |> only |> first
499+
@test count(isinvoke(:sin), src.code) == 1
500+
@test count(isinvoke(:cos), src.code) == 1
501+
@test count(isinvoke(:+), src.code) == 0
502+
end

test/precompile.jl

+76
Original file line numberDiff line numberDiff line change
@@ -1776,6 +1776,82 @@ let newinterp_path = abspath("compiler/newinterp.jl")
17761776
@test found
17771777
end
17781778
end
1779+
1780+
write(joinpath(load_path, "CustomAbstractInterpreterCaching2.jl"), :(module CustomAbstractInterpreterCaching2
1781+
import SimpleModule: basic_caller, basic_callee
1782+
1783+
module Custom
1784+
const CC = Core.Compiler
1785+
include("$($newinterp_path)")
1786+
@newinterp PrecompileInterpreter
1787+
struct CustomData
1788+
inferred
1789+
CustomData(@nospecialize inferred) = new(inferred)
1790+
end
1791+
function CC.transform_result_for_cache(interp::PrecompileInterpreter,
1792+
mi::Core.MethodInstance, valid_worlds::CC.WorldRange, result::CC.InferenceResult)
1793+
inferred_result = @invoke CC.transform_result_for_cache(interp::CC.AbstractInterpreter,
1794+
mi::Core.MethodInstance, valid_worlds::CC.WorldRange, result::CC.InferenceResult)
1795+
return CustomData(inferred_result)
1796+
end
1797+
function CC.inlining_policy(interp::PrecompileInterpreter, @nospecialize(src),
1798+
@nospecialize(info::CC.CallInfo), stmt_flag::UInt32)
1799+
if src isa CustomData
1800+
src = src.inferred
1801+
end
1802+
return @invoke CC.inlining_policy(interp::CC.AbstractInterpreter, src::Any,
1803+
info::CC.CallInfo, stmt_flag::UInt32)
1804+
end
1805+
end
1806+
1807+
Base.return_types((Float64,)) do x
1808+
basic_caller(x)
1809+
end
1810+
Base.return_types((Float64,); interp=Custom.PrecompileInterpreter()) do x
1811+
basic_caller(x)
1812+
end
1813+
Base.return_types((Vector{Float64},)) do x
1814+
sum(x)
1815+
end
1816+
Base.return_types((Vector{Float64},); interp=Custom.PrecompileInterpreter()) do x
1817+
sum(x)
1818+
end
1819+
end) |> string)
1820+
Base.compilecache(Base.PkgId("CustomAbstractInterpreterCaching2"))
1821+
@eval let
1822+
using CustomAbstractInterpreterCaching2
1823+
cache_owner = Core.Compiler.cache_owner(
1824+
CustomAbstractInterpreterCaching2.Custom.PrecompileInterpreter())
1825+
let m = only(methods(CustomAbstractInterpreterCaching.basic_callee))
1826+
mi = only(Base.specializations(m))
1827+
ci = mi.cache
1828+
@test isdefined(ci, :next)
1829+
@test ci.owner === nothing
1830+
@test ci.max_world == typemax(UInt)
1831+
ci = ci.next
1832+
@test !isdefined(ci, :next)
1833+
@test ci.owner === cache_owner
1834+
@test ci.max_world == typemax(UInt)
1835+
end
1836+
let m = only(methods(sum, (Vector{Float64},)))
1837+
found = false
1838+
for mi = Base.specializations(m)
1839+
if mi isa Core.MethodInstance && mi.specTypes == Tuple{typeof(sum),Vector{Float64}}
1840+
ci = mi.cache
1841+
@test isdefined(ci, :next)
1842+
@test ci.owner === cache_owner
1843+
@test ci.max_world == typemax(UInt)
1844+
ci = ci.next
1845+
@test !isdefined(ci, :next)
1846+
@test ci.owner === nothing
1847+
@test ci.max_world == typemax(UInt)
1848+
found = true
1849+
break
1850+
end
1851+
end
1852+
@test found
1853+
end
1854+
end
17791855
end
17801856
end
17811857

0 commit comments

Comments
 (0)