Enable inline type checks for all type equality cases (#2276)
authorAndy Ayers <andya@microsoft.com>
Wed, 29 Jan 2020 08:47:56 +0000 (00:47 -0800)
committerGitHub <noreply@github.com>
Wed, 29 Jan 2020 08:47:56 +0000 (00:47 -0800)
Now that method tables are not shared by distinct types, we can always emit
inlined type equality tests.

Closes #1258.

src/coreclr/src/vm/jitinterface.cpp
src/coreclr/tests/src/JIT/Intrinsics/TypeEquality.cs [new file with mode: 0644]
src/coreclr/tests/src/JIT/Intrinsics/TypeEquality_r.csproj [new file with mode: 0644]
src/coreclr/tests/src/JIT/Intrinsics/TypeEquality_ro.csproj [new file with mode: 0644]
src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.cs

index 0a32499..725ac62 100644 (file)
@@ -3759,37 +3759,14 @@ BOOL CEEInfo::isValueClass(CORINFO_CLASS_HANDLE clsHnd)
 
 /*********************************************************************/
 // Decides how the JIT should do the optimization to inline the check for
-//     GetTypeFromHandle(handle) == obj.GetType() (for CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE)
-//     GetTypeFromHandle(X) == GetTypeFromHandle(Y) (for CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN)
+//     GetTypeFromHandle(handle) == obj.GetType()
+//     GetTypeFromHandle(X) == GetTypeFromHandle(Y)
 //
 // This will enable to use directly the typehandle instead of going through getClassByHandle
 CorInfoInlineTypeCheck CEEInfo::canInlineTypeCheck(CORINFO_CLASS_HANDLE clsHnd, CorInfoInlineTypeCheckSource source)
 {
-    CONTRACTL {
-        NOTHROW;
-        GC_NOTRIGGER;
-        MODE_PREEMPTIVE;
-    } CONTRACTL_END;
-
-    CorInfoInlineTypeCheck ret;
-
-    JIT_TO_EE_TRANSITION_LEAF();
-
-    if (source == CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN)
-    {
-        // It's always okay to compare type handles coming from IL tokens
-        ret = CORINFO_INLINE_TYPECHECK_PASS;
-    }
-    else
-    {
-        _ASSERTE(source == CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE);
-        ret = canInlineTypeCheckWithObjectVTable(clsHnd) ?
-            CORINFO_INLINE_TYPECHECK_PASS : CORINFO_INLINE_TYPECHECK_NONE;
-    }
-
-    EE_TO_JIT_TRANSITION_LEAF();
-
-    return(ret);
+    LIMITED_METHOD_CONTRACT;
+    return CORINFO_INLINE_TYPECHECK_PASS;
 }
 
 /*********************************************************************/
@@ -3799,37 +3776,8 @@ CorInfoInlineTypeCheck CEEInfo::canInlineTypeCheck(CORINFO_CLASS_HANDLE clsHnd,
 // This will enable to use directly the typehandle instead of going through getClassByHandle
 BOOL CEEInfo::canInlineTypeCheckWithObjectVTable (CORINFO_CLASS_HANDLE clsHnd)
 {
-    CONTRACTL {
-        NOTHROW;
-        GC_NOTRIGGER;
-        MODE_PREEMPTIVE;
-    } CONTRACTL_END;
-
-    BOOL ret = FALSE;
-
-    JIT_TO_EE_TRANSITION_LEAF();
-
-    _ASSERTE(clsHnd);
-
-    TypeHandle VMClsHnd(clsHnd);
-    if (VMClsHnd == TypeHandle(g_pCanonMethodTableClass))
-    {
-        // We can't do this optimization in shared generics code because of we do not know what the actual type is going to be.
-        // (It can be array, marshalbyref, etc.)
-        ret = FALSE;
-    }
-    else
-    {
-        // It is safe to perform this optimization
-        // NOTE: clsHnd could be a TypeDesc with shared MethodTable (ex: typeof(int*) == o.GetType()), but that is still optimizable.
-        //       That is because optimized version compares literally a handle to a methodtable and that will be not equal,
-        //       which is valid since none of MethodDesc types can have heap instances.
-        ret = TRUE;
-    }
-
-    EE_TO_JIT_TRANSITION_LEAF();
-
-    return(ret);
+    LIMITED_METHOD_CONTRACT;
+    return TRUE;
 }
 
 /*********************************************************************/
diff --git a/src/coreclr/tests/src/JIT/Intrinsics/TypeEquality.cs b/src/coreclr/tests/src/JIT/Intrinsics/TypeEquality.cs
new file mode 100644 (file)
index 0000000..aa4fad2
--- /dev/null
@@ -0,0 +1,107 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System;
+using System.Runtime.CompilerServices;
+
+// Optimization of type equality tests to various
+// vtable and handle comparisons.
+
+class X<Q>
+{
+   [MethodImpl(MethodImplOptions.NoInlining)]
+   public static bool Is(object o)
+   {
+        return typeof(Q) == o.GetType();
+   }
+
+   [MethodImpl(MethodImplOptions.NoInlining)]
+   public static bool IsR(object o)
+   {
+        return o.GetType() == typeof(Q);
+   }
+
+   [MethodImpl(MethodImplOptions.NoInlining)]
+   public static bool Is<P>()
+   {
+       return typeof(Q) == typeof(P);
+   }
+}
+
+class X
+{
+   [MethodImpl(MethodImplOptions.NoInlining)]
+   public static bool Is<Q>(object o)
+   {
+        return typeof(Q) == o.GetType();
+   }
+
+   [MethodImpl(MethodImplOptions.NoInlining)]
+   public static bool IsR<Q>(object o)
+   {
+       return o.GetType() == typeof(Q);
+   }
+
+   [MethodImpl(MethodImplOptions.NoInlining)]
+   public static bool Is<P,Q>()
+   {
+       return typeof(Q) == typeof(P);
+   }
+}
+
+class P
+{
+   public static int Main()
+   {   
+      bool passed = true;
+
+      string s = "string";
+      object o = new object();
+      string[] sarray = new string[0];
+      object[] oarray = new object[0];
+
+      // positive cases
+      passed &= X<string>.Is(s);
+      passed &= X<object>.Is(o);
+      passed &= X<string[]>.Is(sarray);
+      passed &= X<object[]>.Is(oarray);
+
+      passed &= X<string>.IsR(s);
+
+      passed &= X.Is<string, string>();
+
+      passed &= X.Is<string>(s);
+      passed &= X.Is<object>(o);
+      passed &= X.Is<string[]>(sarray);
+      passed &= X.Is<object[]>(oarray);
+
+      passed &= X.IsR<string>(s);
+
+      passed &= X<string>.Is<string>();
+
+      // negative cases
+      bool failed = false;
+
+      failed |= X<string>.Is(o);
+      failed |= X<object>.Is(s);
+      failed |= X<string[]>.Is(oarray);
+      failed |= X<object[]>.Is(sarray);
+
+      failed |= X<string>.IsR(o);
+
+      failed |= X.Is<string, object>();
+
+      failed |= X.Is<string>(o);
+      failed |= X.Is<object>(s);
+      failed |= X.Is<string[]>(oarray);
+      failed |= X.Is<object[]>(sarray);
+
+      failed |= X.IsR<string>(o);
+
+      failed |= X<object>.Is<string>();
+
+      return passed && !failed ? 100 : -1;
+   }
+}
diff --git a/src/coreclr/tests/src/JIT/Intrinsics/TypeEquality_r.csproj b/src/coreclr/tests/src/JIT/Intrinsics/TypeEquality_r.csproj
new file mode 100644 (file)
index 0000000..2e353c5
--- /dev/null
@@ -0,0 +1,10 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <DebugType>None</DebugType>
+    <Optimize />
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="TypeEquality.cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/coreclr/tests/src/JIT/Intrinsics/TypeEquality_ro.csproj b/src/coreclr/tests/src/JIT/Intrinsics/TypeEquality_ro.csproj
new file mode 100644 (file)
index 0000000..71027d7
--- /dev/null
@@ -0,0 +1,10 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <DebugType>None</DebugType>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="TypeEquality.cs" />
+  </ItemGroup>
+</Project>
index 6dee554..179e96f 100644 (file)
@@ -1,3 +1,8 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
 using System;
 using System.Collections;
 using System.Collections.Generic;
@@ -174,4 +179,4 @@ public struct GenericStruct<T>
 public enum SimpleEnum
 {
     A,B,C
-}
\ No newline at end of file
+}