Skip to content

Support devirtualization for virtual methods that require an instantiating stub#128702

Open
hez2010 wants to merge 68 commits into
dotnet:mainfrom
hez2010:devirt-instantiating-stub-coreclr
Open

Support devirtualization for virtual methods that require an instantiating stub#128702
hez2010 wants to merge 68 commits into
dotnet:mainfrom
hez2010:devirt-instantiating-stub-coreclr

Conversation

@hez2010

@hez2010 hez2010 commented May 28, 2026

Copy link
Copy Markdown
Contributor

Enable devirtualization of virtual methods that require an instantiating stub.
We have landed the refactor in the JIT so that now we are able to handle instantiating stub naturally without a JIT change.

This covers both shared generic virtual methods that don't require a runtime lookup, and generic interface methods that have a default implementation.

GVM devirt for NativeAOT requires more changes which is out of scope of this PR.

IGenericA<string> a = new AImpl();
a.B("GVM");
a.C("Generic DIM");

interface IA
{
    void A(object o);
    void B<T>(T o);
}

interface IGenericA<T> : IA
{
    void C(T o) => B(o);
}

class AImpl : IGenericA<string>
{
    public void A(object o)
    {
        Console.WriteLine(o);
    }

    public void B<T>(T o)
    {
        Console.WriteLine(o);
    }
}

Before:

G_M000_IG01:                ;; offset=0x0000
       push     rbx
       sub      rsp, 32
 
G_M000_IG02:                ;; offset=0x0005
       mov      rcx, 0x7FFB383AF518
       call     CORINFO_HELP_NEWSFAST
       mov      rbx, rax
       mov      rcx, rbx
       mov      rdx, 0x7FFB383AF348
       mov      r8, 0x7FFB383AF828
       call     CORINFO_HELP_VIRTUAL_FUNC_PTR
       mov      rcx, rbx
       mov      rdx, 0xA025C36200
       call     rax
       mov      rcx, rbx
       mov      r11, 0x7FFB380E0070
       mov      rdx, 0xA025C36220
       call     [r11]IGenericA`1[System.__Canon]:C(System.__Canon):this
       nop      
 
G_M000_IG03:                ;; offset=0x005D
       add      rsp, 32
       pop      rbx
       ret      
 
; Total bytes of code 99

After:

G_M56640_IG01:  ;; offset=0x0000
       sub      rsp, 40
G_M56640_IG02:  ;; offset=0x0004
       mov      rcx, 0xA0261EE6F0      ; 'GVM'
       call     [System.Console:WriteLine(System.Object)]
       mov      rcx, 0xA0261EE710      ; 'Generic DIM'
       call     [System.Console:WriteLine(System.Object)]
       nop
G_M56640_IG03:  ;; offset=0x0025
       add      rsp, 40
       ret

; Total bytes of code 42

Similar for R2R:

G_M56640_IG01:  ;; offset=0x0000
       push     rbx
       sub      rsp, 32
G_M56640_IG02:  ;; offset=0x0005
       call     [CORINFO_HELP_READYTORUN_NEW]
       mov      rbx, rax
       mov      rcx, qword ptr [(reloc 0x4000000000420148)]      ; const ptr
       mov      rcx, gword ptr [rcx]
       call     [System.Console:WriteLine(System.Object)]
       mov      rdx, qword ptr [(reloc 0x4000000000420150)]      ; const ptr
       mov      rdx, gword ptr [rdx]
       mov      rcx, rbx
       lea      r11, [(reloc 0x40000000004200f8)]      ; function address
       call     [r11]IGenericA`1[System.String]:C(System.String):this
       nop
G_M56640_IG03:  ;; offset=0x0036
       add      rsp, 32
       pop      rbx
       ret

For NativeAOT:

Before:

  G_M000_IG01:                ;; offset=0x0000
         push     rbx
         sub      rsp, 32
   
  G_M000_IG02:                ;; offset=0x0005
         lea      rcx, [(reloc 0x420c38)]
         call     CORINFO_HELP_NEWSFAST
         mov      rbx, rax
         mov      rcx, rbx
         lea      rdx, [(reloc 0x420c48)]
         call     CORINFO_HELP_GVMLOOKUP_FOR_SLOT
         test     al, 2
         je       SHORT G_M000_IG04
   
  G_M000_IG03:                ;; offset=0x0027
         mov      rdx, qword ptr [rax+0x06]
         mov      rcx, rbx
         lea      r8, gword ptr [(reloc 0x420c68)]
         call     [rax-0x02]
         jmp      SHORT G_M000_IG05
   
  G_M000_IG04:                ;; offset=0x003A
         mov      rcx, rbx
         lea      rdx, gword ptr [(reloc 0x420c68)]
         call     rax
   
  G_M000_IG05:                ;; offset=0x0046
         mov      rcx, rbx
         lea      r11, [(reloc 0x420c58)]
         lea      rdx, gword ptr [(reloc 0x420c70)]
         call     [r11]IGenericA`1[System.String]:C(System.String):this
         nop      
   
  G_M000_IG06:                ;; offset=0x005B
         add      rsp, 32
         pop      rbx
         ret      
   
  ; Total bytes of code 97

After:

G_M56640_IG01:  ;; offset=0x0000
       push     rdi
       push     rsi
       push     rbx
       sub      rsp, 32
                                                ;; size=7 bbWeight=1 PerfScore 3.25
G_M56640_IG02:  ;; offset=0x0007
       lea      rcx, [(reloc 0x4000000000420c30)]      ; AImpl
       call     CORINFO_HELP_NEWSFAST
       mov      rbx, rax
       mov      rcx, rbx
       lea      rdx, [(reloc 0x4000000000420c40)]      ; IA:B[System.String](System.String):this
       call     CORINFO_HELP_GVMLOOKUP_FOR_SLOT
       mov      rsi, rax
       mov      edi, eax
       and      edi, 2
       je       SHORT G_M56640_IG04
G_M56640_IG03:  ;; offset=0x002F
       mov      rdx, qword ptr [rax+0x06]
       mov      rcx, rbx
       lea      r8, gword ptr [(reloc 0x4000000000420c90)]      ; '"GVM"'
       call     [rax-0x02]
       jmp      SHORT G_M56640_IG05
G_M56640_IG04:  ;; offset=0x0042
       mov      rcx, rbx
       lea      rdx, gword ptr [(reloc 0x4000000000420c90)]      ; '"GVM"'
       call     rax
G_M56640_IG05:  ;; offset=0x004E
       test     edi, edi
       je       SHORT G_M56640_IG07
G_M56640_IG06:  ;; offset=0x0052
       mov      rdx, qword ptr [rsi+0x06]
       mov      rcx, rbx
       lea      r8, gword ptr [(reloc 0x4000000000420c98)]      ; '"Generic DIM"'
       call     [rsi-0x02]
       jmp      SHORT G_M56640_IG08
G_M56640_IG07:  ;; offset=0x0065
       mov      rcx, rbx
       lea      rdx, gword ptr [(reloc 0x4000000000420c98)]      ; '"Generic DIM"'
       call     rsi
G_M56640_IG08:  ;; offset=0x0071
       nop
G_M56640_IG09:  ;; offset=0x0072
       add      rsp, 32
       pop      rbx
       pop      rsi
       pop      rdi
       ret

; Total bytes of code 122

Also added a couple of tests from #43668.

Closes #9588
Contributes to #112596

/cc: @jakobbotsch @davidwrighton @MichalStrehovsky

Copilot AI review requested due to automatic review settings May 28, 2026 14:13
@hez2010 hez2010 requested a review from MichalStrehovsky as a code owner May 28, 2026 14:13
@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 May 28, 2026
@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label May 28, 2026

Copilot AI 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

Note

Copilot was unable to run its full agentic suite in this review.

Enables devirtualization of generic virtual methods and default interface methods (DIMs) on generic interfaces, by computing and passing the instantiation argument needed at runtime, instead of failing devirtualization in those cases.

Changes:

  • In the VM JIT interface, remove the early bail-out for generic DIMs and shared generic virtual methods, and instead compute instParamLookup / re-resolve via the exact (non-shared) MethodDesc when possible; defer the instantiating-stub unwrap until after tokenLookupContext is set.
  • In the managed JIT interface (CorInfoImpl), add parallel logic to populate instParamLookup for generic DIM / generic virtual / array-interface devirtualization (R2R uses TypeDictionary / MethodHandle helpers; NativeAOT path returns FAILED_CANON for now), and switch tokenLookupContext to a method context where appropriate.
  • In DevirtualizationManager, drop the unconditional generic-virtual canon bail-out and, for R2R, only fail DIM resolution on variant dispatch (allowing generic DIMs); keep the stricter pre-existing behavior for NativeAOT.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

File Description
src/coreclr/vm/jitinterface.cpp Allow devirt to generic DIM / shared GVM by computing exact MD and instParamLookup; move instantiating-stub unwrap after context setup.
src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs Add instParamLookup population for generic DIM / GVM / array-interface cases; adjust tokenLookupContext; gate AOT vs R2R behavior.
src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs For R2R, only fail DIM resolution on variant dispatch; remove unconditional shared-GVM canon bail-out.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DevirtualizationManager.cs Update stale comment numbering after removing the DIM-restriction note.

Comment thread src/coreclr/vm/jitinterface.cpp Outdated
Comment thread src/coreclr/vm/jitinterface.cpp Outdated
Comment thread src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
Comment thread src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs Outdated
Comment thread src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
Comment thread src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
Comment thread src/coreclr/vm/jitinterface.cpp
Copilot AI review requested due to automatic review settings May 28, 2026 14:18

Copilot AI 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

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

Comment thread src/coreclr/vm/jitinterface.cpp Outdated
Comment thread src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs Outdated
Comment thread src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs Outdated
Comment thread src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs Outdated
Comment thread src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs Outdated
Comment thread src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs Outdated
Copilot AI review requested due to automatic review settings May 28, 2026 14:29

Copilot AI 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

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

Comment thread src/coreclr/vm/jitinterface.cpp
Comment thread src/coreclr/vm/jitinterface.cpp Outdated
Comment thread src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs Outdated
Comment thread src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs Outdated
Comment thread src/coreclr/vm/jitinterface.cpp Outdated
Copilot AI review requested due to automatic review settings May 28, 2026 14:50

Copilot AI 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

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

Comment thread src/coreclr/vm/jitinterface.cpp Outdated
Comment thread src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs Outdated
Comment thread src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
@hez2010

hez2010 commented May 28, 2026

Copy link
Copy Markdown
Contributor Author

@MihuBot

@hez2010

hez2010 commented May 28, 2026

Copy link
Copy Markdown
Contributor Author

@MihuBot -nuget

@davidwrighton davidwrighton 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.

LGTM, but I'd like @MichalStrehovsky to sign off on the native aot test issues being unrelated before we merge.

Copilot AI review requested due to automatic review settings June 26, 2026 01:57

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@hez2010

hez2010 commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

@MichalStrehovsky I disabled the generic recursion test for NativeAOT in 87b4e0c due to #129855. Can you take a look?

Comment thread src/coreclr/tools/Common/Compiler/GenericCycleDetection/GraphBuilder.ForEach.cs Outdated
Comment thread src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs Outdated
Comment thread src/tests/JIT/Generics/VirtualMethods/generic_virtual_methods.cs Outdated
Copilot AI review requested due to automatic review settings June 26, 2026 04:14

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@hez2010 hez2010 requested a review from MichalStrehovsky June 26, 2026 04:18
@MichalStrehovsky

Copy link
Copy Markdown
Member

@MichalStrehovsky I disabled the generic recursion test for NativeAOT in 87b4e0c due to #129855. Can you take a look?

These are not scenarios we're concerned about for native AOT, correct? Otherwise it would be nice to write it in a way that it doesn't trigger a recursion.

@hez2010

hez2010 commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

These are not scenarios we're concerned about for native AOT, correct?

Right. I think this test can be disabled for NativeAOT fine. The type is a struct here is just for testing the interaction between runtime lookups and unboxing stubs, we have other cases that don't need a recursion which works fine under NativeAOT.

Otherwise it would be nice to write it in a way that it doesn't trigger a recursion.

The test here performs a runtime lookup through delegates to resolve to itself with a different instantiation to exercise whether we use the right generic context before / after devirtualization against the same method, and that's why a generic recursion is here.

Copilot AI review requested due to automatic review settings June 26, 2026 06:23

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@hez2010

hez2010 commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

I added several tests to cover NativeAOT's behavior of unboxing stubs + runtime lookups in a non-recursive way.

@MichalStrehovsky MichalStrehovsky 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.

Thank you!

@MichalStrehovsky

Copy link
Copy Markdown
Member

/azp run runtime-coreclr outerloop, runtime-coreclr pgo, runtime-coreclr pgostress, runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines will not run the associated pipelines, because the pull request was updated after the run command was issued. Review the pull request again and issue a new run command.

Copilot AI review requested due to automatic review settings June 26, 2026 08:25

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@hez2010

hez2010 commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

Azure Pipelines will not run the associated pipelines, because the pull request was updated after the run command was issued. Review the pull request again and issue a new run command.

I updated the branch to resolve this. Also fixed a copy-paste error which dismissed the approval.

@MichalStrehovsky Can you trigger the run again?

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.

[Default interfaces] Support for default interface method devirtualization

5 participants