Skip to content

Equivalent CodeInstance replacement does not update / consider edges #62021

Description

@topolarity

Noticed this while working on TypedCallable.

We use this equivalence predicate to check whether an invokee CodeInstance can be swapped out for another in the cache before codegen:

cached = ccall(:jl_get_ci_equiv, Any, (Any, UInt), callee, get_inference_world(workqueue.interp))::CodeInstance
if cached === callee
# make sure callee is gc-rooted and cached, as required by jl_add_codeinsts_to_jit
code_cache(workqueue.interp)[mi] = callee
else
# use an existing CI from the cache, if there is available one that is compatible
callee === ci && (ci = cached)
callee = cached
end

and then to support invoking the cached "equivalent" CI, our AOT / JIT codegen has a very similar notion of "equivalence" to choose a target CI:

julia/src/jitlayers.cpp

Lines 2331 to 2334 in f89b539

return jl_atomic_load_relaxed(&CI2->min_world) <= MinWorld &&
jl_atomic_load_relaxed(&CI2->max_world) >= MaxWorld &&
jl_egal(CI2->def, Def) && jl_egal(CI2->owner, Owner) &&
jl_egal(CI2->rettype, RetType);

but notably this does not replace the CodeInstance in the :invoke or .edges of the CI.

IPO-safety guarantees us that this replacement is sound even if the exct, ipo_purity_bits, etc. from the new CodeInstance are wide compared to the CI originally inferred against, but this guarantee only applies point-wise in world age - it requires that we continue to respect the world / invalidation bounds for the invoked CI. My concern is that if later the "equivalent" CI is invalidated but your original CI in your .edges was not, it seems that this can become a stale invalidation bug.

Even before then, it appears that the world bounds for the produced CI can be invalid if the "equivalent" CI has a more narrow world range.

If reachable this bug is quite hard to hit in practice, since it relies on two different runs for the same MethodInstance yielding appreciably different inference results, which for the time being ~usually only happens due to cache effects, etc.

I put Claude 🤖 on reproducing, but we've gotten sidetracked after discovering other missing edges.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugIndicates an unexpected problem or unintended behaviorcompiler:inferenceType inferencecorrectness bug ⚠Bugs that are likely to lead to incorrect results in user code without throwing

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions