Skip to content

Be more careful about iterator invalidation during recursive invalida… #57934

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 1, 2025

Conversation

Keno
Copy link
Member

@Keno Keno commented Mar 29, 2025

…tion

It is possible for one MethodInstance to have a backedge to itself (typejoin is a prime example). This didn't used to be much of a problem prior to #57625 because we would just delete the backedges list at the top of invalidation. However, now that we're more selective, we need to be careful not to move backedges around while a frame higher on the stack may be looking at it. To this end, move the compaction part of the deletion into a separate pass and only delete (but don't move around) backedges while a frame higher on the stack may be looking at it.

Fixes #57696

@Keno Keno added backport 1.12 Change should be backported to release-1.12 bugfix This change fixes an existing bug labels Mar 29, 2025
@KristofferC KristofferC mentioned this pull request Mar 31, 2025
36 tasks
…tion

It is possible for one MethodInstance to have a backedge to itself (`typejoin`
is a prime example). This didn't used to be much of a problem prior to
#57625 because we would just delete the backedges list at the top of invalidation.
However, now that we're more selective, we need to be careful not to move
backedges around while a frame higher on the stack may be looking at it.
To this end, move the compaction part of the deletion into a separate pass
and only delete (but don't move around) backedges while a frame higher on
the stack may be looking at it.
@Keno Keno merged commit d6f5b7f into master Apr 1, 2025
4 of 7 checks passed
@Keno Keno deleted the kf/57696 branch April 1, 2025 14:50
KristofferC pushed a commit that referenced this pull request Apr 1, 2025
#57934)

…tion

It is possible for one MethodInstance to have a backedge to itself
(`typejoin` is a prime example). This didn't used to be much of a
problem prior to #57625 because we would just delete the backedges list
at the top of invalidation. However, now that we're more selective, we
need to be careful not to move backedges around while a frame higher on
the stack may be looking at it. To this end, move the compaction part of
the deletion into a separate pass and only delete (but don't move
around) backedges while a frame higher on the stack may be looking at
it.

Fixes #57696

(cherry picked from commit d6f5b7f)
@KristofferC KristofferC mentioned this pull request Apr 4, 2025
51 tasks
@timholy
Copy link
Member

timholy commented Apr 11, 2025

Since I see this is queued for backporting, it's worth mentioning that this broke SnoopCompile (the in-progress update for 1.12).
Specifically, in https://github.com/timholy/SnoopCompile.jl/blob/469db0fb77249bf406f5563d5c7da1586d8ca052/test/snoop_invalidations_new.jl#L160-L164 the logging stream is missing

MethodInstance for Main.MethodLogs.f(::Integer)
  "jl_method_table_insert"
  MethodInstance for Main.MethodLogs.f(::Signed)
  "jl_method_table_insert"
  f(::Signed) @ Main /tmp/SnoopC/snoop_invalidations_new.jl:165
  "jl_method_table_insert"

@timholy
Copy link
Member

timholy commented Apr 11, 2025

Here's a MWE. The distinguishing characteristic is the presence of both a poorly-specialized and invoked caller with the same signature:

f(::Integer) = 1
callsfrts(x) = f(Base.inferencebarrier(x)::Signed)
invokesfs(x) = invoke(f, Tuple{Signed}, x)

# compilation
invokesfs(1)                        # invoked callee
callsfrts(1)                        # runtime-dispatched callee

# invalidation
logmeths = ccall(:jl_debug_method_invalidation, Any, (Cint,), 1);
f(::Int) = 2
f(::Signed) = 4
ccall(:jl_debug_method_invalidation, Any, (Cint,), 0);

m = which(f, (Signed,))
m  logmeths

You want the final line to be true.

Here's the backedge graph before invalidation:

julia> m = only(methods(f))
f(::Integer)
     @ Main REPL[1]:1

julia> mis = collect(Base.specializations(m))
2-element Vector{Any}:
 MethodInstance for f(::Int64)
 MethodInstance for f(::Signed)

julia> mi = mis[1]
MethodInstance for f(::Int64)

julia> mi.backedges
2-element Vector{Any}:
 Tuple{typeof(f), Signed}
 CodeInstance for MethodInstance for invokesfs(::Int64)

julia> mi2 = mis[2]
MethodInstance for f(::Signed)

julia> mi2.backedges
1-element Vector{Any}:
 CodeInstance for MethodInstance for callsfrts(::Int64)

@timholy
Copy link
Member

timholy commented Apr 11, 2025

Filed as #58080 so it doesn't get lost

@KristofferC
Copy link
Member

This is already backported in #57972.

@KristofferC KristofferC removed the backport 1.12 Change should be backported to release-1.12 label Apr 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugfix This change fixes an existing bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

memory corruption by "Align module base between invalidation and edge tracking (#57625)"
3 participants