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

Dispatch-resolved version of Expr(:new_opaque_closure, ...) #57996

Open
topolarity opened this issue Apr 3, 2025 · 2 comments
Open

Dispatch-resolved version of Expr(:new_opaque_closure, ...) #57996

topolarity opened this issue Apr 3, 2025 · 2 comments

Comments

@topolarity
Copy link
Member

topolarity commented Apr 3, 2025

OpaqueClosures are currently constructed with this (lower-transformed) constructor:

Expr(:new_opaque_closure, AT::Type{Tuple}, RT::Type, RT::Type, oc_method::Method, captures...)

In the happy case (capture types known + AT / RT are Const), the compiler can statically resolve the dispatch to MethodInstance (::typeof.(captures))(::AT...) for oc_method and simplify the construction of this OpaqueClosure significantly (including compiling all of its code ahead-of-time)

Right now, codegen attempts to resolve this on-the-fly to a compiled CodeInstance, which can then be used to codegen essentially:

Expr(:new, Core.OpaqueClosure, captures, Base.get_world_counter(), oc_method, C_NULL, specptr)

where specptr was taken out of a CodeInstance that the oc_method has been resolved to.

This is the right transformation, but it's happening in the wrong place, since codegen is no longer allowed to call into type-inference (this is one of the last offenders).

In order to support this, we need some way of providing the CI to codegen ahead-of-time.

The simplest proposal may be to support:

Expr(:new_opaque_closure, AT::Type{Tuple}, RT::Type, RT::Type, ci::CodeInstance, captures...)

One related wrinkle will be solving this TODO:

if (ci == NULL || (jl_value_t*)ci == jl_nothing || ci->rettype != rettype || !jl_egal(sigtype, mi->specTypes)) { // TODO: correctly handle the ABI conversion if rettype != ci->rettype

ci->rettype is required by inference to be the inferred return type, not a widened (or intersected/type-asserted) version of the same, and right now we simply lose this optimization if you don't infer exactly as expected:

julia> make_oc(x::Int) = Base.Experimental.@opaque Tuple{}->Int ()->Core.compilerbarrier(:type, x)
julia> code_llvm(make_oc, (Int,))
define void @julia_make_oc_7945(ptr noalias nocapture noundef nonnull sret({ ptr, i64, ptr, ptr, ptr }) align 8 dereferenceable(40) %sret_return, ptr noalias nocapture noundef nonnull align 8 dereferenceable(16) %return_roots, i64 signext %"x::Int64") local_unnamed_addr #0 {
top:
# ...
  %6 = call nonnull ptr @jl_new_opaque_closure_jlcall(ptr null, ptr nonnull %jlcallframe1, i32 6)
# ...
}

which for --trim also means that you lose the ability to compile / run this code.

Maybe ABIOverride can be a suitable mechanism for requesting compilation for the required ABI?

@Keno
Copy link
Member

Keno commented Apr 3, 2025

Maybe ABIOverride can be a suitable mechanism for requesting compilation for the required ABI?

What prevents us from just overriding the rettype in the CodeInstance? ABIOverride was required precisely because the CodeInstance did not have a place for specifying the argument ABI other than mi->specTypes, but it does for the rt.

@topolarity
Copy link
Member Author

What prevents us from just overriding the rettype in the CodeInstance?

@vtjnash (iiuc) claims that it is an important invariant, but I'm not sure what directly motivates it

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

No branches or pull requests

2 participants