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

Optimize GDV checks for objects with known type #84661

Merged
merged 26 commits into from
Apr 22, 2023

Conversation

EgorBo
Copy link
Member

@EgorBo EgorBo commented Apr 11, 2023

Optimize *obj == const_class GDV's guards if we can get the exact type of obj. This is needed for #84659

Codegen improvement example:

string Test(byte[] data) => Encoding.UTF8.GetString(data);

Was:

; Assembly listing for method Prog:Test(ubyte[]):System.String:this
; Tier-1 compilation
; 1 inlinees with PGO data; 1 single block inlinees; 0 inlinees without PGO data
       sub      rsp, 40
       mov      rcx, 0x1C4C0C009F0      ; const ptr
       mov      rcx, gword ptr [rcx]
       mov      rax, 0x7FFE3DD8A348      ; System.Text.UTF8Encoding+UTF8EncodingSealed
       cmp      qword ptr [rcx], rax ;;; <---- redundant check, obj is already of UTF8EncodingSealed
       jne      SHORT G_M52604_IG07
       test     rdx, rdx
       je       SHORT G_M52604_IG04
       cmp      dword ptr [rdx+08H], 32
       jg       SHORT G_M52604_IG04
       call     [System.Text.UTF8Encoding+UTF8EncodingSealed:GetStringForSmallInput(ubyte[]):System.String:this]
       jmp      SHORT G_M52604_IG05
G_M52604_IG04:  
       call     [System.Text.Encoding:GetString(ubyte[]):System.String:this]
G_M52604_IG05:  
       nop      
       add      rsp, 40
       ret      
G_M52604_IG07:  
       call     [System.Text.UTF8Encoding+UTF8EncodingSealed:GetString(ubyte[]):System.String:this]
       jmp      SHORT G_M52604_IG05
; Total bytes of code 71

Now:

; Assembly listing for method Prog:Test(ubyte[]):System.String:this
; Tier-1 compilation
; optimized code
; 1 inlinees with PGO data; 1 single block inlinees; 0 inlinees without PGO data
       sub      rsp, 40
       mov      rcx, 0x1B4148009F0      
       mov      rcx, gword ptr [rcx]
       test     rdx, rdx
       je       SHORT G_M52604_IG04
       cmp      dword ptr [rdx+08H], 32
       jg       SHORT G_M52604_IG04
       call     [System.Text.UTF8Encoding+UTF8EncodingSealed:GetStringForSmallInput(ubyte[]):System.String:this]
       jmp      SHORT G_M52604_IG05
G_M52604_IG04:  
       call     [System.Text.Encoding:GetString(ubyte[]):System.String:this]
G_M52604_IG05:  
       nop      
       add      rsp, 40
       ret      
; Total bytes of code 48

@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 Apr 11, 2023
@ghost ghost assigned EgorBo Apr 11, 2023
@ghost
Copy link

ghost commented Apr 11, 2023

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

Issue Details

Optimize *obj == const_class GDV's guards if we can get the exact type of obj. This is needed for #84659

Codegen improvement example:

string Test(byte[] data) => Encoding.UTF8.GetString(data);

Was:

; Assembly listing for method Prog:Test(ubyte[]):System.String:this
; Tier-1 compilation
; 1 inlinees with PGO data; 1 single block inlinees; 0 inlinees without PGO data
       sub      rsp, 40
       mov      rcx, 0x1C4C0C009F0      ; const ptr
       mov      rcx, gword ptr [rcx]
       mov      rax, 0x7FFE3DD8A348      ; System.Text.UTF8Encoding+UTF8EncodingSealed
       cmp      qword ptr [rcx], rax ;;; <---- redundant check, obj is already of UTF8EncodingSealed
       jne      SHORT G_M52604_IG07
       test     rdx, rdx
       je       SHORT G_M52604_IG04
       cmp      dword ptr [rdx+08H], 32
       jg       SHORT G_M52604_IG04
       call     [System.Text.UTF8Encoding+UTF8EncodingSealed:GetStringForSmallInput(ubyte[]):System.String:this]
       jmp      SHORT G_M52604_IG05
G_M52604_IG04:  
       call     [System.Text.Encoding:GetString(ubyte[]):System.String:this]
G_M52604_IG05:  
       nop      
       add      rsp, 40
       ret      
G_M52604_IG07:  
       call     [System.Text.UTF8Encoding+UTF8EncodingSealed:GetString(ubyte[]):System.String:this]
       jmp      SHORT G_M52604_IG05
; Total bytes of code 71

Now:

; Assembly listing for method Prog:Test(ubyte[]):System.String:this
; Tier-1 compilation
; optimized code
; 1 inlinees with PGO data; 1 single block inlinees; 0 inlinees without PGO data
       sub      rsp, 40
       mov      rcx, 0x1B4148009F0      
       mov      rcx, gword ptr [rcx]
       test     rdx, rdx
       je       SHORT G_M52604_IG04
       cmp      dword ptr [rdx+08H], 32
       jg       SHORT G_M52604_IG04
       call     [System.Text.UTF8Encoding+UTF8EncodingSealed:GetStringForSmallInput(ubyte[]):System.String:this]
       jmp      SHORT G_M52604_IG05
G_M52604_IG04:  
       call     [System.Text.Encoding:GetString(ubyte[]):System.String:this]
G_M52604_IG05:  
       nop      
       add      rsp, 40
       ret      
; Total bytes of code 48
Author: EgorBo
Assignees: EgorBo
Labels:

area-CodeGen-coreclr

Milestone: -

@EgorBo
Copy link
Member Author

EgorBo commented Apr 11, 2023

PTAL @AndyAyersMS - I wanted to land this fix in the past and gave up but now I found a real use-case

@AndyAyersMS
Copy link
Member

Seems like something we already should have been able to clean up.

What is it we know when we do this new type compare fold that we don't know later on (say in value numbering)?

@EgorBo
Copy link
Member Author

EgorBo commented Apr 11, 2023

Seems like something we already should have been able to clean up.

What is it we know when we do this new type compare fold that we don't know later on (say in value numbering)?

I don't think we have anything like that in VN, you probably confuse it with your #72136 which is different. I could move this opt to VN as well but all the cases I found looked like can be handled earlier in Morph. Essentially, it happens when we have something like this:

Foo().Bar();

when we look at Bar virtual call we mark it as GDV candidate because we don't know exact type of Foo(). Then later when we inline Foo() and get something with exact type, so we ended up with that redundant GDV check. So it's a sort of inlining-ordering issue that we clean up here.

If you want to repro it locally:

public class ClassA
{
    public virtual int GetVal() => 42;
}

public sealed class ClassB : ClassA
{
    public override int GetVal() => 43;
}

class Prog
{
    static readonly ClassB s_default = new ClassB();

    static ClassA Default => s_default;

    static void Main()
    {
        for (int i = 0; i < 100; i++)
        {
            Test();
            Thread.Sleep(16);
        }
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    static int Test() => Default.GetVal();
}

Run with

DOTNET_TieredPGO=1
DOTNET_JitDisasm=Test

It simulates a similar pattern I found in Encoding.UTF8.*

@AndyAyersMS
Copy link
Member

With your changes is this firing in LateDevirtualization?

I don't think we have anything like that in VN, you probably confuse it with your #72136 which is different.

Is what you're saying is that VN doesn't know how to handle method table loads from these "const ptrs"?

I'm ok fixing it in morph but remember that morph is purely local and if a known value arrives at the comparison via some data flow morph will never see it. So consider fixing it in VN as well.

@EgorBo
Copy link
Member Author

EgorBo commented Apr 11, 2023

@AndyAyersMS I've just merged VN impl 🙂 It turned out VN knew it only if it could find VNF_JitNew func, so basically it used track allocations only

@EgorBo
Copy link
Member Author

EgorBo commented Apr 11, 2023

It seems it found quite a few cases for my app running locally with PGO.

Copy link
Member

@AndyAyersMS AndyAyersMS left a comment

Choose a reason for hiding this comment

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

LGTM, but I am ok also having it checked earlier too, like you were doing.

Usually, the earlier the better for these things.

@EgorBo
Copy link
Member Author

EgorBo commented Apr 12, 2023

Some nice diffs in benchmarks.run.pgo collection (thanks for adding it!). We don't have apsnet one but presumably it should also hit them. I didn't see any diff improvements from the change in morph so I removed it.

Although, it seems that NativeAOT is not happy. Perhaps, it's not legal to represent types as constants there?

@AndyAyersMS
Copy link
Member

I just redid the ASP.NET collection but maybe another GUID change came in that I missed. Will start it going again.

@EgorBo
Copy link
Member Author

EgorBo commented Apr 12, 2023

/azp list

@azure-pipelines

This comment was marked as off-topic.

@EgorBo
Copy link
Member Author

EgorBo commented Apr 12, 2023

/azp run runtime-coreclr pgo, runtime-coreclr libraries-pgo

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

@AndyAyersMS
Copy link
Member

I just redid the ASP.NET collection but maybe another GUID change came in that I missed. Will start it going again.

The new collection just uploaded.

@EgorBo
Copy link
Member Author

EgorBo commented Apr 12, 2023

I just redid the ASP.NET collection but maybe another GUID change came in that I missed. Will start it going again.

The new collection just uploaded.

Thanks! -15237 bytes on that one.

@EgorBo
Copy link
Member Author

EgorBo commented Apr 12, 2023

/azp run runtime-extra-platforms

@EgorBo
Copy link
Member Author

EgorBo commented Apr 14, 2023

/azp run runtime-coreclr pgo, runtime-coreclr libraries-pgo

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

void* embedClsHnd = (void*)info.compCompHnd->embedClassHandle(handle, &pEmbedClsHnd);
if (pEmbedClsHnd == nullptr)
{
// Skip indirect handles for now since this path is mostly for PGO scenarios
Copy link
Contributor

Choose a reason for hiding this comment

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

Note: indirect handles could be handled by essentially switching out the address for the indirection to be pEmbedClsHnd.

src/coreclr/jit/valuenum.cpp Outdated Show resolved Hide resolved
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@EgorBo
Copy link
Member Author

EgorBo commented Apr 21, 2023

I'm struggling with a NativeAOT assert (happens when ILC compiles crossgen2 project) - it happens inside embedClassHandle EE API Jit calls:

image

where the values in the assert are:

{[S.P.CoreLib]System.Action`1<System.Collections.Generic.List`1<ILCompiler.DependencyAnalysisFramework.DependencyNodeCore`1<System.__Canon>>>}

==

{[S.P.CoreLib]System.Action`1<System.__Canon>}

@MichalStrehovsky @jkotas does it tell you anything, Am I even allowed to use shared types at all in that API?

I've inserted info.compCompHnd->compareTypesForEquality(handle, handle) != TypeCompareState::May check to filter shared types out and it fixed the assert but the jit diffs are now empty 🙁

@jkotas
Copy link
Member

jkotas commented Apr 21, 2023

The assert is complaining about directly embedding type handle that requires dictionary lookup. It is not right in any configs. The types that require dictionary lookup should be never embedded directly. You can try to check CORINFO_FLG_SHAREDINST to see whether the type requires dictionary lookup.

@EgorBo
Copy link
Member Author

EgorBo commented Apr 21, 2023

The assert is complaining about directly embedding type handle that requires dictionary lookup. It is not right in any configs. The types that require dictionary lookup should be never embedded directly. You can try to check CORINFO_FLG_SHAREDINST to see whether the type requires dictionary lookup.

Thanks, that makes perfect sense

@EgorBo
Copy link
Member Author

EgorBo commented Apr 21, 2023

/azp run runtime-coreclr pgo, runtime-coreclr libraries-pgo

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

@EgorBo
Copy link
Member Author

EgorBo commented Apr 22, 2023

The check has fixed the assert and the diffs are back to normal: https://dev.azure.com/dnceng-public/public/_build/results?buildId=248263&view=ms.vss-build-web.run-extensions-tab

@EgorBo
Copy link
Member Author

EgorBo commented Apr 22, 2023

all CI jobs passed except license/CLA 😢

@EgorBo EgorBo closed this Apr 22, 2023
@EgorBo EgorBo reopened this Apr 22, 2023
@EgorBo EgorBo merged commit 7c61b98 into dotnet:main Apr 22, 2023
@EgorBo EgorBo deleted the improve-gdv-check branch April 22, 2023 13:14
@ghost ghost locked as resolved and limited conversation to collaborators May 22, 2023
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
Development

Successfully merging this pull request may close these issues.

4 participants