Skip to content

Conversation

@jakobbotsch
Copy link
Member

@jakobbotsch jakobbotsch commented Jan 31, 2026

This function should JIT the method that has the user IL. For runtime async functions reflection exposes the Task-returning variant which is normally a thunk, so treat these transparently.

This function should JIT the method that has the user IL.
@jakobbotsch jakobbotsch requested review from Copilot and jkotas January 31, 2026 13:36
@github-actions github-actions bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Jan 31, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates CoreCLR’s ReflectionInvocation_PrepareMethod path to avoid JIT-preparing runtime async thunk methods, and instead prepare the associated variant that owns the user IL.

Changes:

  • Detect async thunk MethodDescs in PrepareMethodHelper.
  • Redirect preparation to the async variant via GetAsyncVariant() prior to prestub/JIT.

Comment on lines 1286 to 1289
if (pMD->IsAsyncThunkMethod())
{
pMD = pMD->GetAsyncVariant();
}
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

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

Reassigning pMD to the async variant before the prestub/wrapper-stub logic can skip preparing the original MethodDesc when the incoming method is a wrapper stub (e.g., value-type unboxing stubs can carry async method data/flags). That can change PrepareMethodHelper behavior (notably for ReflectionInvocation_PrepareDelegate) by no longer calling prestub on the wrapper stub the caller will actually invoke.

Consider preserving the original pMD for the existing prestub + IsWrapperStub() handling, and only resolving/jitting the async variant for the wrapped target (after unwrapping), so both the wrapper stub and the user-IL owning variant get prepared.

Also, GetAsyncVariant() defaults allowInstParam to TRUE, while ReflectionInvocation_PrepareMethod resolves the associated MD with allowInstParam = FALSE; it’s likely safer to pass FALSE here as well to avoid ending up preparing an instantiating-stub variant unexpectedly.

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

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

Maybe do it like this to avoid compiling wrapper stub for the async variant? I think the wrapper stub for the async variant is less likely to be ever used.

    if (pMD->IsWrapperStub())
    {
        if (pMD->ShouldCallPrestub())
            pMD->DoPrestub(NULL);
        pMD = pMD->GetWrappedMethodDesc();
    }

    if (pMD->IsAsyncThunk())
    {
        if (pMD->ShouldCallPrestub())
            pMD->DoPrestub(NULL);
        pMD = pMD->GetAsyncVariant();
    }

    if (pMD->ShouldCallPrestub())
        pMD->DoPrestub(NULL);

Copy link
Member Author

Choose a reason for hiding this comment

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

That seems better. Used that.

Copy link
Contributor

@hez2010 hez2010 Jan 31, 2026

Choose a reason for hiding this comment

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

Maybe do it like this to avoid compiling wrapper stub for the async variant? I think the wrapper stub for the async variant is less likely to be ever used.

I don't have a strong opinion, but godbolt is relying on PrepareMethod to inspect the JIT disasm of assemblies. I'm not sure in such use cases whether users want/should see the JIT assembly code of async wrappers in addition to the real async variant.

Copy link
Member

Choose a reason for hiding this comment

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

PrepareMethod triggers JITing of the generic wrapper stubs today. Do users see the disassembly for those?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah. Users are seeing disassembly for async wrapper stubs today, but I think users should see the async variants, or both async wrapper stubs and the async variants.
Example: https://godbolt.org/z/cqdP7GMqT

Copy link
Member

Choose a reason for hiding this comment

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

Yes, I agree. With this change, they should see both.

@jakobbotsch jakobbotsch changed the title Avoid async thunks in RuntimeHelpers.PrepareMethod Look through async thunks in RuntimeHelpers.PrepareMethod Jan 31, 2026
Copilot AI review requested due to automatic review settings January 31, 2026 17:40
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.

Comment on lines +1293 to 1298
if (pMD->IsAsyncThunkMethod())
{
if (pMD->ShouldCallPrestub())
pMD->DoPrestub(NULL);
pMD = pMD->GetAsyncVariant();
}
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

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

The new behavior that looks through async thunk methods is not covered by an automated regression test. Please add a test that uses a runtime-async method ([RuntimeAsyncMethodGeneration(true)]) and verifies that calling RuntimeHelpers.PrepareMethod on the reflection-exposed MethodHandle causes the underlying user-IL variant (not just the Task-returning thunk) to be jitted/prepared (e.g., via JIT/ETW/EventPipe events or another observable signal).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants