Skip to content

JIT: inline shared generics with runtime lookups inside#99265

Merged
EgorBo merged 45 commits into
dotnet:mainfrom
EgorBo:shared-generics-2
Mar 14, 2024
Merged

JIT: inline shared generics with runtime lookups inside#99265
EgorBo merged 45 commits into
dotnet:mainfrom
EgorBo:shared-generics-2

Conversation

@EgorBo

@EgorBo EgorBo commented Mar 4, 2024

Copy link
Copy Markdown
Member

Closes #81432
Closes #8662
Closes #65815
(probably more issues, need to check)

Based on #81635.

This PR implements @jkotas's idea to inline shared generics which require runtime lookups.
Example:

[MethodImpl(MethodImplOptions.NoInlining)]
bool Test<T>() => Callee<T>();

bool Callee<T>() => typeof(T).IsValueType;

Codegen for Test<string> in Main:

; Assembly listing for method Program:Test[System.__Canon]():bool:this
       push     rbx
       sub      rsp, 48
       mov      qword ptr [rsp+0x28], rdx
       mov      rbx, rcx
       mov      rcx, qword ptr [rdx+0x10]
       mov      rax, qword ptr [rcx+0x10]
       test     rax, rax
       je       SHORT G_M000_IG04
       mov      rdx, rax
       jmp      SHORT G_M000_IG05
G_M000_IG04:
       mov      rcx, rdx
       mov      rdx, 0x7FFCC5FE7088
       call     CORINFO_HELP_RUNTIMEHANDLE_METHOD
       mov      rdx, rax
G_M000_IG05:
       mov      rcx, rbx
       call     [Program:Callee[System.__Canon]():bool:this]   ;;;  <---- not inlined
       nop      
       add      rsp, 48
       pop      rbx
       ret      
; Total bytes of code 68

Codegen for Test<string> in this PR:

; Assembly listing for method Program:Test[System.__Canon]():ubyte:this
       xor      eax, eax
       ret

I think the JIT side is ok (I just need to double check various "this context load is invariant" places), but I am not sure I implemented the VM side (only CoreCLR for now) correctly.

@ghost ghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Mar 4, 2024
@ghost ghost assigned EgorBo Mar 4, 2024
@ghost

ghost commented Mar 4, 2024

Copy link
Copy Markdown

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

Issue Details

Closes #81432

This PR implements @jkotas's idea to inline shared generics which require runtime lookups.
Example:

[MethodImpl(MethodImplOptions.NoInlining)]
bool Test<T>() => Callee<T>();

bool Callee<T>() => typeof(T).IsValueType;

Codegen for Test<string> in Main:

; Assembly listing for method Program:Test[System.__Canon]():bool:this
       push     rbx
       sub      rsp, 48
       mov      qword ptr [rsp+0x28], rdx
       mov      rbx, rcx
       mov      rcx, qword ptr [rdx+0x10]
       mov      rax, qword ptr [rcx+0x10]
       test     rax, rax
       je       SHORT G_M000_IG04
       mov      rdx, rax
       jmp      SHORT G_M000_IG05
G_M000_IG04:
       mov      rcx, rdx
       mov      rdx, 0x7FFCC5FE7088
       call     CORINFO_HELP_RUNTIMEHANDLE_METHOD
       mov      rdx, rax
G_M000_IG05:
       mov      rcx, rbx
       call     [Program:Callee[System.__Canon]():bool:this]   ;;;  <---- not inlined
       nop      
       add      rsp, 48
       pop      rbx
       ret      
; Total bytes of code 68

Codegen for Test<string> in this PR:

; Assembly listing for method Program:Test[System.__Canon]():ubyte:this
       xor      eax, eax
       ret

I think the JIT side is ok (I just need to double check various "this context load is invariant" places), but I am not sure I implemented the VM side (only CoreCLR for now) correctly.

Author: EgorBo
Assignees: -
Labels:

area-CodeGen-coreclr

Milestone: -

Comment thread src/coreclr/inc/corinfo.h Outdated
@EgorBo EgorBo force-pushed the shared-generics-2 branch from d7e7396 to 5095668 Compare March 5, 2024 01:03
@EgorBo

EgorBo commented Mar 5, 2024

Copy link
Copy Markdown
Member Author

/azp run runtime-coreclr outerloop, runtime-coreclr gcstress0x3-gcstress0xc

@azure-pipelines

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

@EgorBo EgorBo marked this pull request as ready for review March 5, 2024 13:54
@EgorBo

EgorBo commented Mar 5, 2024

Copy link
Copy Markdown
Member Author

@MihuBot

Comment thread src/coreclr/jit/importercalls.cpp Outdated
Comment thread src/coreclr/jit/importer.cpp Outdated
Comment thread src/coreclr/vm/jitinterface.cpp Outdated
Comment thread src/coreclr/jit/importer.cpp Outdated
Comment thread src/coreclr/jit/inline.h Outdated
Comment thread src/coreclr/jit/importercalls.cpp Outdated
@EgorBo

EgorBo commented Mar 9, 2024

Copy link
Copy Markdown
Member Author

@jkotas one thing I struggle with currently is when I have tokenContext being class (CORINFO_CONTEXTFLAGS_CLASS) -- how do I know whether to use CORINFO_LOOKUP_CLASSPARAM or CORINFO_LOOKUP_THISOBJ ?

@jkotas

jkotas commented Mar 9, 2024

Copy link
Copy Markdown
Member

when I have tokenContext being class (CORINFO_CONTEXTFLAGS_CLASS) -- how do I know whether to use CORINFO_LOOKUP_CLASSPARAM or CORINFO_LOOKUP_THISOBJ

It is not possible to tell from CORINFO_CONTEXTFLAGS_CLASS context alone. You need to have the current method to figure it out.

@EgorBo

EgorBo commented Mar 9, 2024

Copy link
Copy Markdown
Member Author

when I have tokenContext being class (CORINFO_CONTEXTFLAGS_CLASS) -- how do I know whether to use CORINFO_LOOKUP_CLASSPARAM or CORINFO_LOOKUP_THISOBJ

It is not possible to tell from CORINFO_CONTEXTFLAGS_CLASS context alone. You need to have the current method to figure it out.

Current method as in the one we currently inline, right? not the m_methodBeingCompiled (which is the root method). So I presume I need to extend the JIT-EE interface

@EgorBo

EgorBo commented Mar 9, 2024

Copy link
Copy Markdown
Member Author

Although, I am not sure I understand where should I pass it, ComputeRuntimeLookupForSharedGenericToken is not a direct jit-ee api..

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

JIT side LGTM.

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

The ilc and crossgen2 (to the extent that I understand crossgen2) parts lgtm modulo the question.

}

private bool getReadyToRunHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, ref CORINFO_LOOKUP_KIND pGenericLookupKind, CorInfoHelpFunc id, ref CORINFO_CONST_LOOKUP pLookup)
private bool getReadyToRunHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, ref CORINFO_LOOKUP_KIND pGenericLookupKind, CorInfoHelpFunc id, CORINFO_METHOD_STRUCT_* callerHandle, ref CORINFO_CONST_LOOKUP pLookup)

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.

I'm curious why callerHandle can be unused here (we use pResolvedToken.tokenContext to get the context instead), but embedGenericHandle and getCallInfo need the callerHandle and don't use context. Since the tests are passing, maybe callerHandle is redundant with tokenContext?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Switched to callerHandle. From my understanding tokenContext could be a class here in case of inline - that's the whole reason why we propagated CallerHandle.

@MichalStrehovsky

Copy link
Copy Markdown
Member

funny enough, it made R2R'd corelib a bit smaller

It's also a small size improvement for native AOT: MichalStrehovsky/rt-sz#2 (comment)

Comment thread src/coreclr/vm/jitinterface.cpp Outdated
@jkotas

jkotas commented Mar 13, 2024

Copy link
Copy Markdown
Member

METHOD_BEING_COMPILED_CONTEXT should not be needed anymore and it can be deleted. Did you run into problems with deleting it?

@EgorBo

EgorBo commented Mar 13, 2024

Copy link
Copy Markdown
Member Author

METHOD_BEING_COMPILED_CONTEXT should not be needed anymore and it can be deleted. Did you run into problems with deleting it?

I found two places I don't have a good understanding what they're needed for:

  1. rootContext->m_RuntimeContext = METHOD_BEING_COMPILED_CONTEXT();
    - it sees to be used to detect recursive inlining or something like that
  2. pResult->contextHandle = METHOD_BEING_COMPILED_CONTEXT();

I might delete the 1st one but I think I'll need an SPMI collection for that so ideally after this PR is merged if you don't mind

EgorBo added 3 commits March 13, 2024 14:25
# Conflicts:
#	src/coreclr/inc/jiteeversionguid.h
#	src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs
@jkotas

jkotas commented Mar 13, 2024

Copy link
Copy Markdown
Member

I might delete the 1st one but I think I'll need an SPMI collection for that so ideally after this PR is merged if you don't mind

It is fine with me to do it in a follow up PR. Note that deleting METHOD_BEING_COMPILED_CONTEXT is JIT/EE interface breaking change, so you need to rev the GUID.

The second place is what enabled the limited shared generic inlining that should be superseded by your change. It checks for the happy path that the optimization was capable of handling.

@EgorBo

EgorBo commented Mar 13, 2024

Copy link
Copy Markdown
Member Author

/azp run runtime-coreclr crossgen2

@azure-pipelines

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

@EgorBo

EgorBo commented Mar 13, 2024

Copy link
Copy Markdown
Member Author

The second place is what enabled the limited shared generic inlining that should be superseded by your change. It checks for the happy path that the optimization was capable of handling.

@jkotas do you mean it should be just pResult->contextHandle = callerHandleAsContext without those conditions, rights?

@jkotas

jkotas commented Mar 13, 2024

Copy link
Copy Markdown
Member

It should be pResult->contextHandle = MAKE_CLASSCONTEXT(exactType.AsPtr()); that is there a few lines before the if check. The if check and pResult->contextHandle = METHOD_BEING_COMPILED_CONTEXT() should be deleted without replacement.

pResult->contextHandle = callerHandleAsContext without those conditions, rights?

This would not work well - it would be a code quality regression. The context needs to be exactType to carry as much instantiation details as possible into the inlinee.

@EgorBo

EgorBo commented Mar 13, 2024

Copy link
Copy Markdown
Member Author

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

@azure-pipelines

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

@EgorBo

EgorBo commented Mar 13, 2024

Copy link
Copy Markdown
Member Author

@jkotas anything else (assuming CI passes)?
I already have a couple of ideas for a follow up to clean up/improve CQ, so going to include METHOD_BEING_COMPILED_CONTEXT there

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

Looks great! Thank you

@EgorBo EgorBo merged commit 825f053 into dotnet:main Mar 14, 2024
@EgorBo

EgorBo commented Mar 14, 2024

Copy link
Copy Markdown
Member Author

Thanks for help!

@EgorBo

EgorBo commented Mar 18, 2024

Copy link
Copy Markdown
Member Author

Found at least 80 benchmarks improved by 10% or more: https://gist.github.com/EgorBo/b6424f7118ff176682f63875d89fb52e

@EgorBo

EgorBo commented Mar 19, 2024

Copy link
Copy Markdown
Member Author

@github-actions github-actions Bot locked and limited conversation to collaborators Apr 28, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

4 participants