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: inline shared generics with runtime lookups inside #99265

Merged
merged 45 commits into from
Mar 14, 2024

Conversation

EgorBo
Copy link
Member

@EgorBo EgorBo commented Mar 4, 2024

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.

@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 Mar 4, 2024
@ghost ghost assigned EgorBo Mar 4, 2024
@ghost
Copy link

ghost commented Mar 4, 2024

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: -

src/coreclr/inc/corinfo.h Outdated Show resolved Hide resolved
@EgorBo EgorBo force-pushed the shared-generics-2 branch from d7e7396 to 5095668 Compare March 5, 2024 01:03
@EgorBo
Copy link
Member Author

EgorBo commented Mar 5, 2024

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

Copy link

Azure Pipelines successfully started running 2 pipeline(s).

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

EgorBo commented Mar 5, 2024

@MihuBot

@EgorBo
Copy link
Member Author

EgorBo commented Mar 9, 2024

@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
Copy link
Member

jkotas commented Mar 9, 2024

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
Copy link
Member Author

EgorBo commented Mar 9, 2024

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
Copy link
Member Author

EgorBo commented Mar 9, 2024

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

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.

JIT side LGTM.

Copy link
Member

@MichalStrehovsky MichalStrehovsky left a comment

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.

@@ -827,7 +827,7 @@ public void CompileMethod(MethodWithGCInfo methodCodeNodeNeedingCode, Logger log
}
}

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
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
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
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)

@jkotas
Copy link
Member

jkotas commented Mar 13, 2024

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

@EgorBo
Copy link
Member Author

EgorBo commented Mar 13, 2024

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
Copy link
Member

jkotas commented Mar 13, 2024

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
Copy link
Member Author

EgorBo commented Mar 13, 2024

/azp run runtime-coreclr crossgen2

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@EgorBo
Copy link
Member Author

EgorBo commented Mar 13, 2024

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
Copy link
Member

jkotas commented Mar 13, 2024

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
Copy link
Member Author

EgorBo commented Mar 13, 2024

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

Copy link

Azure Pipelines successfully started running 2 pipeline(s).

@EgorBo
Copy link
Member Author

EgorBo commented Mar 13, 2024

@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

Copy link
Member

@jkotas jkotas left a comment

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
206 checks passed
@EgorBo
Copy link
Member Author

EgorBo commented Mar 14, 2024

Thanks for help!

@EgorBo
Copy link
Member Author

EgorBo commented Mar 18, 2024

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

@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