Skip to content
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

JIT: Devirtualization and inlining for GVM #112353

Draft
wants to merge 65 commits into
base: main
Choose a base branch
from

Conversation

hez2010
Copy link
Contributor

@hez2010 hez2010 commented Feb 10, 2025

We use FindOrCreateAssociatedMethodDesc with allowInstParam=false to get the exact MethodDesc for a devirted GVM.

While the resulted MethodDesc may be an instantiation stub, so either

  • an InstParam that can be fetched from WrappedMethodDesc if it doesn't require a runtime lookup
  • a RUNTIMELOOKUP node we created for ldvirtftn if it requires a runtime lookup (in this case FindOrCreateAssociatedMethodDesc will yield us a method handle that has shared generics in the method instantiation).

So if we see a RUNTIMELOOKUP, we push it to the call args as the InstParam, otherwise we can use the instantiated entry of the WrappedMethodDesc as the InstParam.

Also extend the late devirt a bit to support devirting generic virtual methods.

To make sure we can get the method handle or RUNTIMELOOKUP, we need to stop spilling ldvirtftn. However, NAOT and lowering is not happy about gtCallAddr being a CALL, so we split the call after inlining is done.

NativeAOT/R2R is not support yet (due to fat function pointers).

Example:

VirtualGenericClass c = new IntProcessor();
c.Process(42);

public class VirtualGenericClass
{
    public virtual void Process<T>(T item)
    {
        Console.WriteLine(item.ToString());
    }
}

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

Before:

G_M27646_IG01:  ;; offset=0x0000
       push     rbx
       sub      rsp, 32
                                                ;; size=5 bbWeight=1 PerfScore 1.25
G_M27646_IG02:  ;; offset=0x0005
       mov      rcx, 0x7FF7C5D68548      ; IntProcessor
       call     CORINFO_HELP_NEWSFAST
       mov      rbx, rax
       mov      rcx, rbx
       mov      rdx, 0x7FF7C5D68350      ; VirtualGenericClass
       mov      r8, 0x7FF7C5D686B8      ; token handle
       call     CORINFO_HELP_VIRTUAL_FUNC_PTR
       mov      rcx, rbx
       mov      edx, 42
       call     rax
       nop
                                                ;; size=57 bbWeight=1 PerfScore 7.00
G_M27646_IG03:  ;; offset=0x003E
       add      rsp, 32
       pop      rbx
       ret
                                                ;; size=6 bbWeight=1 PerfScore 1.75

After:

G_M27646_IG01:  ;; offset=0x0000
       sub      rsp, 40
                                                ;; size=4 bbWeight=1 PerfScore 0.25
G_M27646_IG02:  ;; offset=0x0004
       mov      ecx, 42
       call     [System.Number:Int32ToDecStr(int):System.String]
       mov      rcx, rax
       call     [System.Console:WriteLine(System.String)]
       nop
                                                ;; size=21 bbWeight=1 PerfScore 6.75
G_M27646_IG03:  ;; offset=0x0019
       add      rsp, 40
       ret
                                                ;; size=5 bbWeight=1 PerfScore 1.25

/cc: @jakobbotsch @AndyAyersMS

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Feb 10, 2025
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Feb 10, 2025
@hez2010
Copy link
Contributor Author

hez2010 commented Feb 10, 2025

@MihuBot

@EgorBo
Copy link
Member

EgorBo commented Feb 10, 2025

Looks like CI failures are related? also, the diffs look more like inliner actually no longer inlines what it used to?

@hez2010
Copy link
Contributor Author

hez2010 commented Feb 10, 2025

Looks like CI failures are related?

They should be fixed with 4b809c8, where we must not allow a method InstParam present on the devirted call when the method itself is generic.

the diffs look more like inliner actually no longer inlines what it used to

Not sure about this for now, may need more investigation. Let's see the latest run.

@hez2010
Copy link
Contributor Author

hez2010 commented Feb 10, 2025

Seems that the VM can dispatch a generic method if there's no method InstParam, so that this would enable us devirt while not inlining a GVM.

@hez2010 hez2010 changed the title JIT: Initial devirtualization and inlining for GVM JIT: Devirtualization and inlining for GVM Feb 10, 2025
@hez2010
Copy link
Contributor Author

hez2010 commented Feb 10, 2025

Devirtualizing GVM without inlining is supported now.
@MihuBot

@hez2010
Copy link
Contributor Author

hez2010 commented Feb 10, 2025

@MihuBot

@EgorBo
Copy link
Member

EgorBo commented Feb 11, 2025

No spmi diffs and only 1 method affected in jit-diffs. Can't tell it's a big improvement for that method, but it definitely implies a poor test coverage for this optimization. Since changes around method resolving are always risky - do we want to proceed? Is there a real-world motivation behind this?

@hez2010
Copy link
Contributor Author

hez2010 commented Feb 13, 2025

Hmm the changes need to be separated into two parts, one for VM and another one for JIT, because it needs a JIT-EE interface guid update. Otherwise the dvInfo will be invalid and lead to SPMI failures.
Will just to make sure there's no test failure.

@hez2010
Copy link
Contributor Author

hez2010 commented Feb 13, 2025

Thinking twice, we don't need the extra flag in dvInfo at all, we already have the info in gtLdvirtftnHnd.
So actually we don't need a JIT-EE interface guid update.

@hez2010
Copy link
Contributor Author

hez2010 commented Feb 14, 2025

I think it's in a great form now, while seems that it still has some bad interactions with boxed structs during inlining, need to investigate...

@hez2010
Copy link
Contributor Author

hez2010 commented Feb 14, 2025

Seems that lowering on windows-x86 is unhappy about not spilling the CORINFO_HELP_VIRTUAL_FUNC_PTR call:

Assert failure(PID 61084 [0x0000ee9c], Thread: 40756 [0x9f34]): Assertion failed 'node->GetRegNum() != REG_NA' in 'GenInstance`2[System.__Canon,int]:VirtForward[System.__Canon,System.__Canon](System.__Canon,int,System.__Canon,System.__Canon):System.String:this' during 'Lowering nodeinfo' (IL size 23; hash 0xb3dc36bf; FullOpts)

@hez2010
Copy link
Contributor Author

hez2010 commented Feb 15, 2025

@MihuBot

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.

2 participants