Skip to content

Support async generic virtual methods - #121524

Merged
MichalStrehovsky merged 4 commits into
dotnet:mainfrom
MichalStrehovsky:gvmasync
Nov 12, 2025
Merged

Support async generic virtual methods#121524
MichalStrehovsky merged 4 commits into
dotnet:mainfrom
MichalStrehovsky:gvmasync

Conversation

@MichalStrehovsky

Copy link
Copy Markdown
Member
  • Since generic virtual method dispatch is based on RuntimeMethodHandle, we need a RuntimeMethodHandle for async variants. Emit the bit in the compiler, at runtime (when type loading), and read it when needed. Reflection stack should never see this RuntimeMethodHandle.
  • Add AsyncVariant property on MethodDesc (since runtime type system does this for unboxing methods too).
  • Add a flag that distinguishes async variants in native layout. This is not specific to GVMs, it also covers other cases of runtime generic dictionary building, such as MakeGenericMethod.
  • Add a flag that distinguishes these in generic method dictionary hashtables (both static and built at runtime)
  • Lots of piping through the extra bool

Cc @dotnet/ilc-contrib

* Since generic virtual method dispatch is based on `RuntimeMethodHandle`, we need a `RuntimeMethodHandle` for async variants. Emit the bit in the compiler, at runtime (when type loading), and read it when needed. Reflection stack should never see this `RuntimeMethodHandle`.
* Add `AsyncVariant` property on `MethodDesc` (since runtime type system does this for unboxing methods too).
* Add a flag that distinguishes async variants in native layout. This is not specific to GVMs, it also covers other cases of runtime generic dictionary building, such as `MakeGenericMethod`.
* Add a flag that distinguishes these in generic method dictionary hashtables (both static and built at runtime)
* Lots of piping through the extra bool
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull Request Overview

This pull request adds support for async generic virtual methods by introducing an IsAsyncVariant flag throughout the runtime type system. The core mechanism involves emitting and reading special RuntimeMethodHandles for async variants while ensuring they remain transparent to the reflection stack.

Key Changes

  • Added AsyncVariant property to MethodDesc and related type system classes to track async method variants
  • Introduced IsAsyncVariant flag constants in multiple locations (RuntimeMethodHandle, GenericMethodsHashtable, NativeFormat MethodFlags) to distinguish async variants in data structures
  • Modified GVM metadata emission to skip async variants since they duplicate metadata
  • Updated all method resolution and instantiation code paths to thread the asyncVariant boolean parameter through

Reviewed Changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 5 comments.

Show a summary per file
FileDescription
TypeGVMEntriesNode.csFilters out async variants when scanning for GVM entries to avoid metadata duplication
RuntimeMethodHandleNode.csStores async variant flag in the instantiation length field, retrieves actual method target for metadata
NativeLayoutVertexNode.csMarks methods with IsAsyncVariant flag and retrieves correct metadata token
GenericMethodsHashtableNode.csEncodes async variant flag in the token field of hashtable entries
RuntimeConstants.csDefines IsAsyncVariant constants for RuntimeMethodHandle and GenericMethodsHashtable
NativeFormat.csAdds IsAsyncVariant flag to MethodFlags enum
TypeSystemContext.Runtime.csAdds asyncVariant to method key comparison and resolution logic
RuntimeMethodDesc.csImplements AsyncVariant property and threads it through method resolution
RuntimeMethodDesc.Canon.csPasses AsyncVariant when resolving canonical method target
MethodDesc.Runtime.csDefines virtual AsyncVariant property with default false implementation
InstantiatedMethod.Runtime.csDelegates AsyncVariant property to method definition
TypeLoaderEnvironment.csUpdates method component retrieval to include asyncVariant output parameter
TypeLoaderEnvironment.Metadata.csPasses isAsyncVariant: false in virtual resolve scenarios
TypeLoaderEnvironment.LdTokenResultLookup.csAdds async variant handling and assertion that reflection never sees async variants
TypeLoaderEnvironment.GVMResolution.csThreads async variant flag through GVM resolution
TypeLoaderEnvironment.ConstructedGenericMethodsLookup.csDecodes async variant flag from hashtable and static data, stores in method entries
TypeBuilder.csStores async variant flag when registering generic method entries
NativeLayoutInfoLoadContext.csReads async variant flag from native layout and passes to method resolution
GenericDictionaryCell.csPasses AsyncVariant when creating RuntimeMethodHandle
ExecutionEnvironmentImplementation.MappingTables.csAsserts async variants are not visible to reflection
RuntimeMethodHandle.csRestructures MethodHandleInfo to store flags packed with NumGenericArgs

…ypeSystem/InstantiatedMethod.Runtime.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

@jkotasjkotas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Worth doing outer loop run or size check to validate that the async variants are not getting pulled in unexpectedly?

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

Worth doing outer loop run or size check to validate that the async variants are not getting pulled in unexpectedly?

Triggered outerloop. rt-sz runs will be on the floor until we start producing .NET 11 bits unfortunately.

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

Worth doing outer loop run or size check to validate that the async variants are not getting pulled in unexpectedly?

Triggered outerloop. rt-sz runs will be on the floor until we start producing .NET 11 bits unfortunately.

Libs outerloop looks good. The CoreCLR test tree outerloop is on the floor, fix in #121541.

Let's not merge yet, I did a self-review and there's one more thing I want to fix.

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/ba-g timeouts

@MichalStrehovsky
MichalStrehovsky merged commit 3927844 into dotnet:mainNov 12, 2025
100 of 119 checks passed
@MichalStrehovsky
MichalStrehovsky deleted the gvmasync branch November 12, 2025 21:47
@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

Worth doing outer loop run or size check to validate that the async variants are not getting pulled in unexpectedly?

Figured out the right combination of numbers 10 and 11 that is currently needed to consume SDK builds for main.

The regression for the "reflection" test is the cost of the additional code in the type loader. The savings are from better integer compression because we dropped metadata handle.

Size statistics

Pull request #121524

ProjectSize beforeSize afterDifference
TodosApi-linux 24289464 24264888 -24576
TodosApi-windows 25775616 25752576 -23040
avalonia.app-linux 18833280 18816896 -16384
avalonia.app-windows 19412992 19395072 -17920
hello-linux 1139120 1139120 0
hello-minimal-linux 1003848 1003848 0
hello-minimal-windows 771584 771584 0
hello-windows 924672 924672 0
kestrel-minimal-linux 5234168 5230072 -4096
kestrel-minimal-windows 4811776 4811264 -512
reflection-linux 1808256 1812352 4096
reflection-windows 1680896 1682432 1536
webapiaot-linux 9563488 9555296 -8192
webapiaot-windows 10169856 10163712 -6144
winrt-component-minimal-windows 720896 720896 0

@github-actionsgithub-actionsBot locked and limited conversation to collaborators Dec 17, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@MichalStrehovsky@jkotas