Skip to content

NativeAOT: devirtualize isinst/castclass for monomorphic cases #80831

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

Merged
merged 2 commits into from
Jan 19, 2023

Conversation

EgorBo
Copy link
Member

@EgorBo EgorBo commented Jan 19, 2023

Closes #80828

Luckily, we already have JIT-EE API to get exact list of implementations/subclasses for given type.

interface IMyInterface{} // this interface has a single implementation

class Program : IMyInterface
{
    static void Main()
    {
        Case1(new Program());
        Case2(new Program());
    }


    [MethodImpl(MethodImplOptions.NoInlining)]
    static bool Case1(object o) => o is IMyInterface;

    [MethodImpl(MethodImplOptions.NoInlining)]
    static IMyInterface Case2(object o) => (IMyInterface)o;
}

Previous NativeAOT codegen:

; Method Program:Case1(System.Object):bool
       sub      rsp, 40
       mov      rdx, rcx
       lea      rcx, [(reloc)]
       call     CORINFO_HELP_ISINSTANCEOFINTERFACE
       test     rax, rax
       setne    al
       movzx    rax, al
       add      rsp, 40
       ret      
; Total bytes of code: 33


; Method Program:Case2(System.Object):IMyInterface
       sub      rsp, 40
       mov      rdx, rcx
       lea      rcx, [(reloc)]
       call     CORINFO_HELP_CHKCASTINTERFACE
       nop      
       add      rsp, 40
       ret      
; Total bytes of code: 25

New NativeAOT codegen:

; Method Program:Case1(System.Object):bool
       test     rcx, rcx
       je       SHORT G_M13626_IG05
       lea      rax, [(reloc)]
       cmp      qword ptr [rcx], rax
       je       SHORT G_M13626_IG05
       xor      rcx, rcx
G_M13626_IG05:
       xor      eax, eax
       test     rcx, rcx
       setne    al
       ret      
; Total bytes of code: 28


; Method Program:Case2(System.Object):IMyInterface
       sub      rsp, 40
       mov      rdx, rcx
       mov      rax, rdx
       test     rax, rax
       je       SHORT G_M12806_IG05
       lea      rcx, [(reloc)]
       cmp      qword ptr [rax], rcx
       je       SHORT G_M12806_IG05
       mov      rcx, qword ptr [rsp+20H]
       call     CORINFO_HELP_CHKCASTINTERFACE ;; this is used to throw InvalidCastException only
G_M12806_IG05:
       nop      
       add      rsp, 40
       ret      
; Total bytes of code: 43

@ghost ghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jan 19, 2023
@ghost ghost assigned EgorBo Jan 19, 2023
@ghost
Copy link

ghost commented Jan 19, 2023

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

Issue Details

Closes #80828

Luckily, we already have JIT-EE API to get exact list of implementations/subclasses for given type.

interface IMyInterface{} // this interface has a single implementation

class Program : IMyInterface
{
    static void Main()
    {
        Case1(new Program());
        Case2(new Program());
    }


    [MethodImpl(MethodImplOptions.NoInlining)]
    static bool Case1(object o) => o is IMyInterface;

    [MethodImpl(MethodImplOptions.NoInlining)]
    static IMyInterface Case2(object o) => (IMyInterface)o;
}

Previous NativeAOT codegen:

; Method Program:Case1(System.Object):bool
       sub      rsp, 40
       mov      rdx, rcx
       lea      rcx, [(reloc)]
       call     CORINFO_HELP_ISINSTANCEOFINTERFACE
       test     rax, rax
       setne    al
       movzx    rax, al
       add      rsp, 40
       ret      
; Total bytes of code: 33


; Method Program:Case2(System.Object):IMyInterface
       sub      rsp, 40
       mov      rdx, rcx
       lea      rcx, [(reloc)]
       call     CORINFO_HELP_CHKCASTINTERFACE
       nop      
       add      rsp, 40
       ret      
; Total bytes of code: 25

New NativeAOT codegen:

; Method Program:Case1(System.Object):bool
       test     rcx, rcx
       je       SHORT G_M13626_IG05
       lea      rax, [(reloc)]
       cmp      qword ptr [rcx], rax
       je       SHORT G_M13626_IG05
       xor      rcx, rcx
G_M13626_IG05:
       xor      eax, eax
       test     rcx, rcx
       setne    al
       ret      
; Total bytes of code: 28


; Method Program:Case2(System.Object):IMyInterface
       sub      rsp, 40
       mov      rdx, rcx
       mov      rax, rdx
       test     rax, rax
       je       SHORT G_M12806_IG05
       lea      rcx, [(reloc)]
       cmp      qword ptr [rax], rcx
       je       SHORT G_M12806_IG05
       mov      rcx, qword ptr [rsp+20H]
       call     CORINFO_HELP_CHKCASTINTERFACE ;; this is used to throw InvalidCastException only
G_M12806_IG05:
       nop      
       add      rsp, 40
       ret      
; Total bytes of code: 43
Author: EgorBo
Assignees: -
Labels:

area-CodeGen-coreclr

Milestone: -

@am11
Copy link
Member

am11 commented Jan 19, 2023

Is Case2 regression avoidable?

@AndyAyersMS
Copy link
Member

Is Case2 regression avoidable?

The code is bigger but should also be faster, since we will rarely need to call the helper (only if the cast will throw).

Though I'm not quite sure why we're shuffling those registers about like we do...

@MichalStrehovsky
Copy link
Member

Curious: if we had hot/cold splitting support in NativeAOT (#77583), would that place the ;; this is used to throw InvalidCastException only in the cold region, or would that be more work?

Comment on lines +5720 to +5723
if (this->IsTargetAbi(CORINFO_NATIVEAOT_ABI) &&
((helper == CORINFO_HELP_ISINSTANCEOFINTERFACE) || (helper == CORINFO_HELP_CHKCASTINTERFACE)) &&
(info.compCompHnd->getExactClasses(pResolvedToken->hClass, 1, &actualImplCls) == 1) &&
(actualImplCls != NO_CLASS_HANDLE) && impIsClassExact(actualImplCls))
Copy link
Member

Choose a reason for hiding this comment

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

Is there any reason this optimization shouldn't kick in for any other potential EE that returns 1 from getExactClasses?

Copy link
Member Author

Choose a reason for hiding this comment

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

@jakobbotsch purely throughput reasons - 2 ifs + non inlined call getExactClasses since I don't think it will be useful outside of NativeAOT anytime soon 🙂

Copy link
Member

Choose a reason for hiding this comment

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

Seems a bit premature? If I remove the NAOT check and hack SPMI to always return 0 on replay of getExactClasses I see no significant TP differences. IMO, as the general rule (no need to change this now) we should not be sacrificing abstractions unless they have demonstrable impact.

Copy link
Member Author

Choose a reason for hiding this comment

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

no strong opinion on that, will remove as part of some next PR

@EgorBo
Copy link
Member Author

EgorBo commented Jan 19, 2023

Curious: if we had hot/cold splitting support in NativeAOT (#77583), would that place the ;; this is used to throw InvalidCastException only in the cold region, or would that be more work?

It doesn't look like it's handled in hot/cold splitting now, but at least it's still considered as "cold" in jit

@EgorBo EgorBo merged commit 4544bdd into dotnet:main Jan 19, 2023
@EgorBo EgorBo deleted the naot-devirt-isinst branch January 19, 2023 12:32
mdh1418 pushed a commit to mdh1418/runtime that referenced this pull request Jan 24, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Feb 18, 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.

NativeAOT: devirtualize isinst for monomorphic cases
5 participants