Skip to content

inference: reinfer and track missing code for inlining#59413

Merged
vtjnash merged 5 commits intomasterfrom
jn/reinfer-missing-for-inlining
Nov 10, 2025
Merged

inference: reinfer and track missing code for inlining#59413
vtjnash merged 5 commits intomasterfrom
jn/reinfer-missing-for-inlining

Conversation

@vtjnash
Copy link
Member

@vtjnash vtjnash commented Aug 27, 2025

When code is potentially needed for inlining, but missing for any reason, be sure to regenerate it during inference with the correct ci_meets_requirement flags (SOURCE_MODE_GET_SOURCE instead of NOT_REQUIRED) so it is prepared for the optimizer if needed.

This was supposed to be the correct fix for someone else's bug with missing inlining when expected, but I don't remember what bug it was anymore.

julia> @atomic Base.method_instance(promote, (Float64,Int)).cache.inferred = 0x22
0x22

julia> @code_typed 1.0+1
CodeInfo(
    @ promotion.jl:433 within `+`
1 ─ %1 =    invoke Base.promote(x::Float64, y::Int64)::Tuple{Float64, Float64}
│   %2 =   builtin Core.getfield(%1, 1)::Float64
│   %3 =   builtin Core.getfield(%1, 2)::Float64
│   @ promotion.jl:433 within `+` @ float.jl:492
│   %4 = intrinsic Base.add_float(%2, %3)::Float64
└──      return %4
) => Float64

Fixes #59846

@vtjnash vtjnash requested a review from aviatesk August 27, 2025 17:32
@JeffBezanson
Copy link
Member

I'm aware of a couple inlining issues, e.g. #58906, and

@code_typed ((a, b) -> @inline(Base.setindex(a, b, :next)))((next = zero(UInt32), prev = zero(UInt32)), 2)

and

julia> myconvert3(x; T::Type{V}=Int) where {V} = convert(V,x)
julia> f(x) = myconvert3(x;T=Int)
julia> code_typed(f, (Float64,))
1-element Vector{Any}:
 CodeInfo(
1 ─ %1 =    invoke Core.kwcall((T = Int64,)::@NamedTuple{T::DataType}, Main.myconvert3::typeof(myconvert3), x::Float64)::Any
└──      return %1
) => Any

(though these might not all be strictly just inlining). This does not fix any of them, but we should try to find an example.

@vtjnash
Copy link
Member Author

vtjnash commented Aug 29, 2025

No, those are the sort of issues. This is more related to precompile files causing performance to suffer later. (and that first example is already fixed on master, I think by c05b68f)

@vtjnash vtjnash force-pushed the jn/reinfer-missing-for-inlining branch from f3fc258 to 9b3d3f1 Compare September 12, 2025 16:14
@aviatesk aviatesk force-pushed the jn/reinfer-missing-for-inlining branch 8 times, most recently from c4c85f3 to 21158a0 Compare September 19, 2025 16:06
return ci_has_source(interp, code)
end

function ci_get_source(interp::AbstractInterpreter, code::CodeInstance)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this definition should only be used for NativeInterpreter, and for AbstractInterpreter it should be defined to perform a lookup to code_cache by default?
The current test failure should also be fixed by this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, using codegen_cache felt like a hack I added just for getting the PR started. With your refactoring, I think we should not need this anymore for any interpreter (since the code should now be in the regular local cache)? I'm not really sure there is a point to having separate codegen_cache and local_cache--seems like we can and should just merge those, so that everything done in this AbstractInterpreter is now predictably in local_cache; and optionally also in global_cache.

@aviatesk aviatesk force-pushed the jn/reinfer-missing-for-inlining branch 3 times, most recently from 2894282 to 5b2f8e7 Compare October 2, 2025 17:37
@vtjnash
Copy link
Member Author

vtjnash commented Oct 7, 2025

I believe I finally tracked down the last failing test to be an IRInterp problem. While the normal interpreter prepares code for successful inlining with update stmt info, the IRInterp appears that it may not. So since it appears that disabling IRInterp bypasses that bug, it should be possible to make sure IRInterp is doing the correct work in abstract_call to propagate the call result info, so that when inlining processes the result of IRInterp, it gets the same result as if it was processing the result of the normal interp.

@vtjnash vtjnash force-pushed the jn/reinfer-missing-for-inlining branch from 5b2f8e7 to 077c3f2 Compare October 8, 2025 18:49
@vtjnash
Copy link
Member Author

vtjnash commented Oct 8, 2025

I finally tracked it down even more fully. It is a known implementation problem with IRInterp that it degrades code optimization quality when used, which has had a TODO in the code from #48913, but it was previously hidden from tests by the combination of luck, this bug, #58183, and that we have incomplete test coverage of when exactly inlining must occur. I put a hack in place to ensure IRInterp is used no more nor less after this PR than before, so that it cannot cause any new regressions than already existed.

@vtjnash vtjnash added the compiler:inference Type inference label Oct 8, 2025
@vtjnash vtjnash force-pushed the jn/reinfer-missing-for-inlining branch from 077c3f2 to 43944c6 Compare October 8, 2025 21:04
@aviatesk aviatesk force-pushed the jn/reinfer-missing-for-inlining branch from 43944c6 to f1b6506 Compare October 9, 2025 08:55
@aviatesk
Copy link
Member

aviatesk commented Oct 9, 2025

Thanks for looking into the test failures.

I put a hack in place to ensure IRInterp is used no more nor less after this PR than before, so that it cannot cause any new regressions than already existed.

Could you elaborate what specific fixes you made for this?

I thought most of the tests newly marked as broken were because the ci_get_source implementation doesn't support external AbstractInterpreter.
I'm going to make a fix for this and address the tests marked broken in this PR today.

@vtjnash
Copy link
Member Author

vtjnash commented Oct 9, 2025

Seems good. It is a little awkward intermediate state, since once this PR is done, we should deprecate ci_get_source (inference is supposed to provide it in the InferenceResult, but right now there are about a dozen different ways to get approximately the same thing)

Could you elaborate what specific fixes you made for this?

Prohibits IRInterp unless the method body is inlineable. That aligns with the old implementation, before this PR fixed the problems with getting code and caused the worse code generated by IRInterp to be caught by an existing test.

@vtjnash
Copy link
Member Author

vtjnash commented Oct 9, 2025

Looks like the other current test failure just needs to be relaxed somewhat on ordering to just make sure flbi is invalidating useflbi:

diff --git a/test/precompile.jl b/test/precompile.jl
index 04ec1b2e007..73dbd7b99c6 100644
--- a/test/precompile.jl
+++ b/test/precompile.jl
@@ -1124,9 +1124,9 @@ precompile_test_harness("code caching") do dir
             idxv = findnext(==("verify_methods"), invalidations, idxv+1)
         end
         @test invalidations[idxv-1].def.def.name === :flbi
-        idxv = findnext(==("verify_methods"), invalidations, idxv+1)
-        @show invalidations
-        @test invalidations[idxv-1].def.def.name === :useflbi
+        idxv = findnext(==(invalidations[idxv-1]), invalidations, idxv+1)
+        @test invalidations[idxv-1] == "verify_methods"
+        @test invalidations[idxv-2].def.def.name === :useflbi
 
         m = only(methods(MB.map_nbits))
         @test !hasvalid(m.specializations::Core.MethodInstance, world+1) # insert_backedges invalidations also trigger their backedges

@aviatesk aviatesk force-pushed the jn/reinfer-missing-for-inlining branch from f1b6506 to 5f16afd Compare October 9, 2025 20:38
@aviatesk
Copy link
Member

aviatesk commented Oct 9, 2025

Seems good. It is a little awkward intermediate state, since once this PR is done, we should deprecate ci_get_source (inference is supposed to provide it in the InferenceResult, but right now there are about a dozen different ways to get approximately the same thing)

I agree.
How far should we go with this PR?
Personally, I would like to update JET/Cthulhu to work with this PR as well, but at least for Cthulhu, it would be better to finish Cedric's PR first, and waiting for that might unnecessarily block this PR.

@aviatesk aviatesk force-pushed the jn/reinfer-missing-for-inlining branch 2 times, most recently from 95a8224 to 5a1faf4 Compare October 12, 2025 16:26
@DilumAluthge
Copy link
Member

Ah. In that case, can I temporarily disable the flaky test?

#59879

DilumAluthge added a commit that referenced this pull request Oct 18, 2025
DilumAluthge added a commit that referenced this pull request Oct 18, 2025
DilumAluthge added a commit that referenced this pull request Oct 18, 2025
@vtjnash vtjnash force-pushed the jn/reinfer-missing-for-inlining branch 2 times, most recently from d80d173 to c083fd9 Compare November 5, 2025 22:43
When code is potentially needed for inlining, but missing, be sure to
regenerate it during inference with the correct `ci_meets_requirement`
flags (SOURCE_MODE_GET_SOURCE instead of NOT_REQUIRED). This removes the
dependence on what had happened to be in the cache when deciding
optimization heuristic decisions.

To achieve this, this commit comprehensively refactors the data
structures for passing source from inference to inlining. Specifically
it introduces the `InferredCallResult` type as a centralized data
structure for holding source information, which is stored in the
`info.call_results` field of `info::MethodMatchInfo`. The
`info.call_results` field is managed in the same way as `info.edges`,
making the code simpler.

Also be sure to use CACHE_MODE_LOCAL, since it needs to actually end up
in the local cache (as we know the global cache already has it, but
volatile may discard it before inlining).

This simplifies the role of many steps, since inference has complete
responsibility for making all IPO decisions, and the optimizer and
codegen have only the ability to consume and change the code inside of a
function.

The special invoke resolver for inlining is simplified now by delegating
to general resolver code: the special resolver for :invoke-d calls now
uses info.result from InvokeCallInfo and delegates to the general
resolve_todo function instead of reimplementing the inlining logic.

It should be noted that `ConstCallInfo` has been removed. This would be
a breaking change for packages like Cthulhu, but since no information is
lost, preserving existing functionality should not be impossible.

Fix a finalizer optimizer bug: declared type was Nothing, which does not
match runtime behavior and results in segfaults if code is reachable.

🤖 Produced with some use of Claude Code
Prevents cache entries being created with wider applicability than
actually valid, from global cache :invoke lookup inlining.

Delay updating CodeInstance fields until we can fill them all in
correctly at once, avoiding torn state updates.
Incomplete work (other than not being in the cache) should be kept out
of being reachable from any part of the cache. Only populate the parts
that we know the optimizer will unfortunately try to look at.
@vtjnash vtjnash force-pushed the jn/reinfer-missing-for-inlining branch from c083fd9 to 0d19993 Compare November 7, 2025 18:24
@vtjnash vtjnash merged commit 998cb27 into master Nov 10, 2025
7 checks passed
@vtjnash vtjnash deleted the jn/reinfer-missing-for-inlining branch November 10, 2025 18:56
@KristofferC KristofferC added the backport 1.13 Change should be backported to release-1.13 label Jan 14, 2026
maleadt added a commit that referenced this pull request Jan 24, 2026
…0787)

Backport a fix from #59413 to unbreak `Compiler.inflate_ir!` with const-return IR.
maleadt added a commit that referenced this pull request Jan 24, 2026
…0788)

Backport a fix from #59413 to unbreak `Compiler.inflate_ir!` with const-return IR.
@KristofferC KristofferC mentioned this pull request Jan 26, 2026
43 tasks
@KristofferC KristofferC mentioned this pull request Feb 4, 2026
56 tasks
@KristofferC KristofferC mentioned this pull request Mar 13, 2026
25 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport 1.13 Change should be backported to release-1.13 compiler:inference Type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Intermittend failure in precompile invalidations test (on Windows)?

5 participants