Skip to content
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

Make Expr(:invoke) target be a CodeInstance, not MethodInstance #54899

Merged
merged 1 commit into from
Nov 21, 2024

Conversation

vtjnash
Copy link
Member

@vtjnash vtjnash commented Jun 23, 2024

This is a look at what is necessary and what it would look like for codegen to be using CodeInstance directly to get the API for a function call (specifically the rettype), rather than using a lookup call at that point to decide upon the API. It is based around the idea that eventually we would keep track of these anyways to form a graph of the inferred edge data, for use later in validation anyways (instead of attempting to invert the backedges graph in staticdata_utils.c), so we might as well use the same target type for the :invoke call representation also.

This does not depend upon #54894, but is much better with it. It did depend upon #54738, though there still remains work to do to teach it how to select a correct alternative CodeInstance after deserializing. Also still to do: codegen also should be looking for equivalent objects, not just using the exact one given, per the definition of equivalency in https://hackmd.io/@vtjnash/codeinstances (and that is what precompile should be doing also).

Depends on #56552, #56551, #56547, #56598

@vtjnash vtjnash requested a review from Keno June 23, 2024 05:13
@vtjnash vtjnash force-pushed the jn/codeinfo-edges branch 2 times, most recently from af03fd5 to 91be20b Compare June 24, 2024 20:51
@vtjnash vtjnash force-pushed the jn/codeinfo-edges branch 2 times, most recently from ce5726f to d2c2e51 Compare July 16, 2024 21:46
@vtjnash vtjnash force-pushed the jn/codeinfo-edges branch 3 times, most recently from 2f824bf to 33701f1 Compare July 22, 2024 20:40
@aviatesk aviatesk force-pushed the jn/codeinfo-edges branch 7 times, most recently from a9c4eeb to fe8d042 Compare September 27, 2024 09:49
@aviatesk aviatesk force-pushed the jn/codeinfo-edges branch 2 times, most recently from a5a2db9 to 6f64fd6 Compare October 4, 2024 06:30
@aviatesk aviatesk force-pushed the jn/codeinfo-edges branch 2 times, most recently from 52efd2b to 2ecae72 Compare October 16, 2024 12:15
@aviatesk aviatesk force-pushed the jn/codeinfo-edges branch 7 times, most recently from 140f560 to 23c3250 Compare October 25, 2024 13:49
@vtjnash vtjnash force-pushed the jn/codeinfo-edges branch 2 times, most recently from d33a71e to 809d3fb Compare October 31, 2024 18:26
Base automatically changed from jn/codeinfo-edges to master November 1, 2024 10:15
@@ -1539,8 +1549,6 @@ function handle_modifyop!_call!(ir::IRCode, idx::Int, stmt::Expr, info::ModifyOp
edge = info.edges[1]
Copy link
Member Author

Choose a reason for hiding this comment

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

@aviatesk is this the right API calls to use to extract the CodeInstance for a result? It is now the only place we call compileable_specialization with a MethodInstance instead of a CodeInstance, so it would be nice to get to simplify this callsite too

Copy link
Member

Choose a reason for hiding this comment

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

There isn’t an API to extract a specific edge from CallInfo, we only have add_edges!. As for handle_modify_op!, some checks are performed beforehand to ensure this code works correctly.

Copy link
Member

Choose a reason for hiding this comment

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

In cases where edge === nothing, it’s usually because inference failed on the inner operations of the modify-function, so it might be fine to simply reject those.

Copy link
Member Author

Choose a reason for hiding this comment

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

okay, I thought other parts were using getresult, so I wasn't sure if that applied here

Keno added a commit that referenced this pull request Nov 14, 2024
Together with #54899, this PR is intending to replicate the functionality
of #54373, which allowed particular specializations to have a different
ABI signature than what would be suggested by the MethodInstance's
`specTypes` field. This PR handles that by adding a special `ABIOverwrite`
type, which, when placed in the `owner` field of a `CodeInstance`
instructs the system to use the given signature instead.
Keno added a commit that referenced this pull request Nov 14, 2024
Together with #54899, this PR is intending to replicate the functionality
of #54373, which allowed particular specializations to have a different
ABI signature than what would be suggested by the MethodInstance's
`specTypes` field. This PR handles that by adding a special `ABIOverwrite`
type, which, when placed in the `owner` field of a `CodeInstance`
instructs the system to use the given signature instead.
@@ -1042,6 +1042,7 @@ call obsolete versions of a function `f`.
Prior to Julia 1.9, this function was not exported, and was called as `Base.invokelatest`.
"""
function invokelatest(@nospecialize(f), @nospecialize args...; kwargs...)
@inline
Copy link
Member

Choose a reason for hiding this comment

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

Any reason for adding 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.

Just noticing that codegen was doing dumb stuff here because of the nospecialize. It would generate a call to make a Tuple for the args so that it could call the apply function to unpack those again to call the builtin 🙄

@@ -1539,8 +1549,6 @@ function handle_modifyop!_call!(ir::IRCode, idx::Int, stmt::Expr, info::ModifyOp
edge = info.edges[1]
Copy link
Member

Choose a reason for hiding this comment

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

There isn’t an API to extract a specific edge from CallInfo, we only have add_edges!. As for handle_modify_op!, some checks are performed beforehand to ensure this code works correctly.

@@ -1539,8 +1549,6 @@ function handle_modifyop!_call!(ir::IRCode, idx::Int, stmt::Expr, info::ModifyOp
edge = info.edges[1]
Copy link
Member

Choose a reason for hiding this comment

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

In cases where edge === nothing, it’s usually because inference failed on the inner operations of the modify-function, so it might be fine to simply reject those.

@aviatesk
Copy link
Member

There is this another place where compileable_specialization(::MethodInstance, ...) is used:

return compileable_specialization(mi, Effects(), et, info, state)

I recall that most of these cases were due to inference failures, but simply doing return nothing here caused some regressions, if I remember correctly.

@vtjnash
Copy link
Member Author

vtjnash commented Nov 14, 2024

simply doing return nothing here caused some regressions

I tried that and confirmed: it especially regresses the case where we found there was a get_compileable_sig different from sig, and that it was therefore wasteful to infer the sig, but in that case we still want to call compileable_specialization to find the get_compileable_sig target and use that as the invoke target. I updated the comment here to mention this.

@vtjnash vtjnash force-pushed the jn/invoke-codeinstance branch 2 times, most recently from 94762b7 to a7545af Compare November 14, 2024 22:07
assert(jl_is_method_instance(meth));
jl_value_t *c = args[0];
assert(jl_is_code_instance(c) || jl_is_method_instance(c));
jl_method_instance_t *meth = jl_is_method_instance(c) ? (jl_method_instance_t*)c : ((jl_code_instance_t*)c)->def;
Copy link
Member

Choose a reason for hiding this comment

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

This should probably just invoke the codeinstance directly.

Copy link
Member Author

Choose a reason for hiding this comment

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

Perhaps. We still have to support the legacy thing for a bit longer, at least while we solve a few last inference / optimizer bugs around this

@vtjnash
Copy link
Member Author

vtjnash commented Nov 18, 2024

One stacked PR remaining (#56598), but this is already passing tests, so it can be merged once that is done

@vtjnash vtjnash marked this pull request as ready for review November 20, 2024 20:32
@vtjnash
Copy link
Member Author

vtjnash commented Nov 20, 2024

This should be ready to go now, since it seems working, it would be nice to merge before it picks up spurious conflicts.

@nanosoldier runbenchmarks(!"scalar", vs=":master")

Some improvements can be done later:

  • jitlayers/aotcompile/staticdata should be looking for equivalent objects instead of deferring that entirely to the runtime (as a jl_invoke)
  • conversely, interpreter should be trying to execute the object directly, instead of immediately looking for an equivalent one
  • non-nothing owner needs special handling implemented (currently it is still illegal to expect to put a different owner there), since codegen may become inconsistent and incorrect

Make :invoke edges to CodeInstance (with rettype and edges) instead of
MethodInstance, for more accuracy (except when inference made a mistake).

Remove lookup parameter from codegen, since we no longer do any lookup.
@Keno
Copy link
Member

Keno commented Nov 21, 2024

@vtjnash small conflicts with my worldage PR, so I rebased this for you, just FYI

@nanosoldier
Copy link
Collaborator

Your benchmark job has completed - possible performance regressions were detected. A full report can be found here.

@vtjnash vtjnash merged commit c31710a into master Nov 21, 2024
5 of 7 checks passed
@vtjnash vtjnash deleted the jn/invoke-codeinstance branch November 21, 2024 14:10
aviatesk added a commit to JuliaDebug/Cthulhu.jl that referenced this pull request Nov 21, 2024
Although these code paths don’t seem to be used very often.
Keno added a commit that referenced this pull request Nov 22, 2024
This PR allows generated functions to return a `CodeInstance` containing
optimized IR, allowing them to bypass inference and directly adding
inferred code into the ordinary course of execution. This is an enabling
capability for various external compiler implementations that may want
to provide compilation results to the Julia runtime.

As a minimal demonstrator of this capability, this adds a
Cassette-like `with_new_compiler` higher-order function, which
will compile/execute its arguments with the currently loaded `Compiler`
package. Unlike `@activate Compiler[:codegen]`, this change is not
global and the cache is fully partitioned. This by itself is a very
useful feature when developing Compiler code to be able to test
the full end-to-end codegen behavior before the changes are capable
of fully self-hosting.

A key enabler for this was the recent merging of #54899. This PR
includes a hacky version of the second TODO left at the end of
that PR, just to make everthing work end-to-end.

This PR is working end-to-end, but all three parts of it (the CodeInstance
return from generated functions, the `with_new_compiler` feature,
and the interpreter integration) need some additional cleanup. This
PR is mostly intended as a discussion point for what that additional
work needs to be.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants