-
Notifications
You must be signed in to change notification settings - Fork 5.2k
JIT: Devirtualize generic virtual methods #122023
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
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR enables devirtualization support for generic virtual methods in the JIT compiler, allowing calls to generic virtual methods to be devirtualized when the exact type is known at JIT time. This optimization eliminates virtual dispatch overhead and enables further optimizations like inlining.
Key changes:
- Removes the assertion that previously blocked generic method devirtualization
- Generalizes array interface devirtualization to support generic virtual methods by renaming
wasArrayInterfaceDevirttoneedsMethodContext - Adds runtime lookup support for generic method instantiation parameters
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/vm/jitinterface.cpp | Removes assertion blocking generic method devirtualization and adds logic to handle generic virtual methods using FindOrCreateAssociatedMethodDesc |
| src/coreclr/inc/corinfo.h | Renames wasArrayInterfaceDevirt to needsMethodContext to generalize the field meaning |
| src/coreclr/jit/jitconfigvalues.h | Adds JitEnableGenericVirtualDevirtualization configuration flag to control the feature |
| src/coreclr/jit/gentree.h | Adds IsGenericVirtual() helper method to identify generic virtual method calls |
| src/coreclr/jit/gentree.cpp | Updates IsDevirtualizationCandidate() to include generic virtual methods (non-AOT only) |
| src/coreclr/jit/importercalls.cpp | Implements devirtualization logic for generic virtual methods including runtime lookup handling, updates comments and variable names, and introduces DEVIRT label for control flow |
| src/coreclr/jit/inline.h | Renames arrayInterface field to needsMethodContext in InlineCandidateInfo struct |
| src/coreclr/jit/indirectcalltransformer.cpp | Updates to use renamed needsMethodContext field |
| src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs | Updates managed struct definition to match renamed field |
| src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | Updates to use renamed field in managed implementation |
| src/coreclr/tools/superpmi/superpmi-shared/agnostic.h | Updates SuperPMI data structure with renamed field |
| src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp | Updates SuperPMI recording/replay to use renamed field |
530e75d to
6e678a5
Compare
src/coreclr/jit/importercalls.cpp
Outdated
| // If we have a RUNTIMELOOKUP helper call for the method handle, | ||
| // we need to pass that as the inst param instead. | ||
| CallArg* const methHndArg = call->gtArgs.FindWellKnownArg(WellKnownArg::RuntimeMethodHandle); | ||
| GenTree* const methHndNode = methHndArg != nullptr ? methHndArg->GetEarlyNode() : nullptr; | ||
| if (methHndNode && methHndNode->OperIs(GT_RUNTIMELOOKUP)) | ||
| { | ||
| instParam = methHndNode; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain this behavior?
It mixes syntax and semantics. Nothing guarantees that a runtime lookup appears as a GT_RUNTIMELOOKUP node. It could have been stored to a local and a number of other things.
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pushed a new commit including comments to clarify this behavior. Also updated the description of the PR.
|
@MihuBot |
Otherwise we will emit a call to the instantiated method directly instead of a call to the wrapper entrypoint of a shared generic method (the canonical generic method)
|
I think it's ready for review now. |
Enable devirtualization support for generic virtual methods.
When we see a base method having a method instantiation, we use
FindOrCreateAssociatedMethodDescto obtain the devirted method.If we end up with an instantiating stub (i.e. a generic method that requires runtime lookup), we will need:
runtime/src/coreclr/jit/importer.cpp
Line 1341 in 3b63772
Also introduced a jit knob so that it can be turned off at any time.
AOT support is not included in this PR, which needs additional work in managed type system.
Also, devirting a generic virtual method to a class that has a different generic type context is not yet supported, I would like to leave it for future improvement:
For example, let's say we have a
and we managed to devirt
Base.M<string>intoDerived<SomeClass>.M<string>. Here in order to call M, we need to do a runtime lookup, but the context being used for runtime lookup only contains the information of method instantiations, so if we continue to use this context, we will miss the class instantiations because we don't have the right context here. Similar issue also exists for cases like devirtingBase<Class1>.M<string>toDerived<Class2>.M<string>, which can result in incorrect class instantiations.To resolve this, we need to replace the generic context handle used for runtime lookup with the right one that has both the class instantiations and the method instantiations of the devirted method. In my analysis resolving this will unblock many devirt opportunities for PLINQ.
Codegen example:
Codegen diff:
Contributes to #112596
cc: @dotnet/jit-contrib