Skip to content

Conversation

@hez2010
Copy link
Contributor

@hez2010 hez2010 commented Nov 27, 2025

Enable devirtualization support for generic virtual methods.

When we see a base method having a method instantiation, we use FindOrCreateAssociatedMethodDesc to obtain the devirted method.

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, if we end up with an instantiating stub (i.e. a shared generic method that requires runtime lookup), we don't have the correct generic context so we need to bail out for now.

Codegen example:

public class IntProcessor : VirtualGenericClass, IVritualGenericInterface
{
    public override void Process<T>(T item)
    {
        Console.WriteLine(item.ToString());
    }
}

public interface IVritualGenericInterface
{
    void Process<T>(T item) where T : notnull;
}

public static void Test<T>(IVritualGenericInterface ifce, T item) where T : notnull
{
    ifce.Process(item);
}

public static void Test<T>(VirtualGenericClass baseClass, T item) where T : notnull
{
    baseClass.Process(item);
}

static void Test()
{
    IVritualGenericInterface i = new IntProcessor();
    Test(i, 42);

    VirtualGenericClass c = new IntProcessor();
    Test(c, 42);
}

Codegen diff:

 G_M27646_IG01:  ;; offset=0x0000
-       push     rsi
        push     rbx
-       sub      rsp, 40
+       sub      rsp, 32
-                                                ;; size=6 bbWeight=1 PerfScore 2.25
+                                                ;; size=5 bbWeight=1 PerfScore 1.25
-G_M27646_IG02:  ;; offset=0x0006
+G_M27646_IG02:  ;; offset=0x0005
-       mov      rbx, 0x7FFE0803F318      ; Program+IntProcessor
+       mov      rbx, 0x221CA429C38      ; 'System.Int32'
        mov      rcx, rbx
-       call     CORINFO_HELP_NEWSFAST
+       call     [System.Console:WriteLine(System.Object)]
-       mov      rsi, rax
+       mov      ecx, 42
-       mov      rcx, rsi
+       call     [System.Number:Int32ToDecStr(int):System.String]
-       mov      rdx, 0x7FFE0803F100      ; Program+IVritualGenericInterface
+       mov      rcx, rax
-       mov      r8, 0x7FFE0803F6A8      ; token handle
+       call     [System.Console:WriteLine(System.String)]
-       call     CORINFO_HELP_VIRTUAL_FUNC_PTR
-       mov      rcx, rsi
-       mov      edx, 42
-       call     rax
        mov      rcx, rbx
-       call     CORINFO_HELP_NEWSFAST
+       call     [System.Console:WriteLine(System.Object)]
-       mov      rbx, rax
+       mov      ecx, 42
-       mov      rcx, rbx
+       call     [System.Number:Int32ToDecStr(int):System.String]
-       mov      rdx, 0x7FFE0803EF30      ; Program+VirtualGenericClass
+       mov      rcx, rax
-       mov      r8, 0x7FFE0803F8A8      ; token handle
+       call     [System.Console:WriteLine(System.String)]
-       call     CORINFO_HELP_VIRTUAL_FUNC_PTR
-       mov      rcx, rbx
-       mov      edx, 42
-       call     rax
        nop
-                                                ;; size=109 bbWeight=1 PerfScore 14.00
+                                                ;; size=69 bbWeight=1 PerfScore 20.00
-G_M27646_IG03:  ;; offset=0x0073
+G_M27646_IG03:  ;; offset=0x004A
-       add      rsp, 40
+       add      rsp, 32
        pop      rbx
-       pop      rsi
        ret
-                                                ;; size=7 bbWeight=1 PerfScore 2.25
+                                                ;; size=6 bbWeight=1 PerfScore 1.75

Contributes to #112596

cc: @dotnet/jit-contrib

Copilot AI review requested due to automatic review settings November 27, 2025 17:12
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Nov 27, 2025
@github-actions github-actions bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Nov 27, 2025
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

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 wasArrayInterfaceDevirt to needsMethodContext
  • 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

@hez2010
Copy link
Contributor Author

hez2010 commented Nov 28, 2025

@MihuBot

Copy link
Member

@jakobbotsch jakobbotsch left a comment

Choose a reason for hiding this comment

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

LGTM apart from that. Thanks!
@davidwrighton can you take a look too?

@davidwrighton
Copy link
Member

@hez2010 sorry about the delay, I got stuck in a bunch of work just before the holidays and wasn't able to do the review before vacation began.

@jakobbotsch
Copy link
Member

I'm guessing the crossgen issues are related, I don't see them on other PRs. Can you see if you can reproduce them?

@hez2010
Copy link
Contributor Author

hez2010 commented Jan 8, 2026

I'm guessing the crossgen issues are related, I don't see them on other PRs. Can you see if you can reproduce them?

I managed to reproduce the freebsd one on my local cross-build environment. Will investigate it soon to see whether it's related or not.

@hez2010
Copy link
Contributor Author

hez2010 commented Jan 9, 2026

We hit the assertion here:

/home/i/runtime/src/coreclr/jit/importercalls.cpp:8666
Assertion failed 'call->IsVirtualStub()' in 'System.Threading.StackHelper:CallOnEmptyStack[System.__Canon,System.__Canon,System.__Canon,System.__Canon](System.Func`4[System.__Canon,System.__Canon,System.__Canon,System.__Canon],System.__Canon,System.__Canon,System.__Canon):System.__Canon' during 'Importation' (IL size 116; hash 0x7bf56612; FullOpts)

/home/i/runtime/src/coreclr/jit/importercalls.cpp:8666
Assertion failed 'call->IsVirtualStub()' in 'System.Threading.StackHelper:CallOnEmptyStack[System.__Canon,System.__Canon](System.Func`2[System.__Canon,System.__Canon],System.__Canon):System.__Canon' during 'Importation' (IL size 102; hash 0x657768d4; FullOpts)

https://github.com/hez2010/runtime/blob/40d7569852bd8cecef706532d374ab0e8f7e53f2/src/coreclr/jit/importercalls.cpp#L8664-L8670

I think GVM applies the same with virtual stub, where in R2R mode, we might see GVM calls to non-virtuals. I think we should change the assertion to call->IsVirtualStub() || call->IsGenericVirtual(). What do you think? @jakobbotsch

@jakobbotsch
Copy link
Member

I think GVM applies the same with virtual stub, where in R2R mode, we might see GVM calls to non-virtuals. I think we should change the assertion to call->IsVirtualStub() || call->IsGenericVirtual(). What do you think? @jakobbotsch

Seems reasonable to me.

@hez2010
Copy link
Contributor Author

hez2010 commented Jan 11, 2026

CI is green now.
By the way, do you know why the assertion message didn't show up in the CI log before? It makes it hard to spot the cause of bug without a local repro. (I guess it's because MSBuild somehow omitted all the logs from crossgen2?)

@jakobbotsch
Copy link
Member

/azp run runtime-coreclr jitstress, runtime-coreclr outerloop

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

@jakobbotsch
Copy link
Member

CI is green now. By the way, do you know why the assertion message didn't show up in the CI log before? It makes it hard to spot the cause of bug without a local repro. (I guess it's because MSBuild somehow omitted all the logs from crossgen2?)

I am not sure, perhaps that is something specific to how the crossgen2 testing is done for the community targets.

I kicked off some more testing. There are some known test failures in jitstress (for async), so might take some filtering once it's done to validate things.

@hez2010
Copy link
Contributor Author

hez2010 commented Jan 12, 2026

I kicked off some more testing. There are some known test failures in jitstress (for async), so might take some filtering once it's done to validate things.

All failures seem to be preexisting and unrelated.

@jakobbotsch
Copy link
Member

/ba-g Failure is #122345 that is not being matched automatically

@jakobbotsch jakobbotsch merged commit 87b470c into dotnet:main Jan 13, 2026
184 of 189 checks passed
@jakobbotsch
Copy link
Member

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants