dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.16k stars 4.72k forks source link

NAOT: isinst with multiple guesses without fallback #97602

Closed EgorBo closed 8 months ago

EgorBo commented 8 months ago
interface IMyInterface { }

public class MyImplementation1 : IMyInterface { }
public class MyImplementation2 : IMyInterface { }

class Program
{

    static void Main()
    {
        Test(new MyImplementation1());
        Test(new MyImplementation2());
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    static void Test(object o)
    {
        if (o is IMyInterface)
            Console.WriteLine("MyInterface!");
    }
}

Since there are only 2 classes implementing IMyInterface, JIT should be able to optimize Test as follows:

if (o != null && ((o.GetType() == typeof(MyImplementation1) || o.GetType() == typeof(MyImplementation2))
{
}

without any helper call fallback. Similar to what NAOT already does for virtual calls.

Current codegen for Test:

; Assembly listing for method Program:Test(System.Object) (FullOpts)
       sub      rsp, 40
       mov      rdx, rcx
       lea      rcx, [(reloc 0x4000000000422460)] ; IMyInterface
       call     CORINFO_HELP_ISINSTANCEOFINTERFACE
       test     rax, rax
       je       SHORT G_M3485_IG04
       lea      rcx, gword ptr [(reloc 0x4000000000422478)] ; '"MyInterface!"'
       call     System.Console:WriteLine(System.String)
G_M3485_IG04:
       nop      
       add      rsp, 40
       ret      
; Total bytes of code 42

Expected codegen:

; Assembly listing for method Program:Test(System.Object) (FullOpts)
       sub      rsp, 40
       test     rcx, rcx
       je       SHORT G_M3485_IG05
       mov      rcx, qword ptr [rcx]
       lea      rax, [(reloc 0x40000000004224f8)] ; MyImplementation1
       cmp      rcx, rax
       je       SHORT G_M3485_IG04
       lea      rax, [(reloc 0x4000000000422518)] ; MyImplementation2
       cmp      rcx, rax
       jne      SHORT G_M3485_IG05
G_M3485_IG04: 
       lea      rcx, gword ptr [(reloc 0x4000000000422520)] ; '"MyInterface!"'
       call     System.Console:WriteLine(System.String)
G_M3485_IG05: 
       nop      
       add      rsp, 40
       ret      
; Total bytes of code 54
ghost commented 8 months ago

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

Issue Details
```cs interface IMyInterface { } public class MyImplementation1 : IMyInterface { } public class MyImplementation2 : IMyInterface { } class Program { static void Main() { Test(new MyImplementation1()); Test(new MyImplementation2()); } [MethodImpl(MethodImplOptions.NoInlining)] static void Test(object o) { if (o is IMyInterface) Console.WriteLine("MyInterface!"); } } ``` Since there are only 2 classes implementing `IMyInterface`, JIT should be able to optimize `Test` as follows: ```cs if (o != null && ((o.GetType() == typeof(MyImplementation1) || o.GetType() == typeof(MyImplementation2)) { } ``` without any helper call fallback. Similar to what NAOT already does for virtual calls.
Author: EgorBo
Assignees: -
Labels: `area-CodeGen-coreclr`, `untriaged`, `needs-area-label`
Milestone: -