diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj
index 2e37b454167d9..24f8495acf294 100644
--- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -241,6 +241,7 @@
+
@@ -467,5 +468,5 @@
-
+
diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs
new file mode 100644
index 0000000000000..c5600d4830db2
--- /dev/null
+++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs
@@ -0,0 +1,480 @@
+// 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.Diagnostics;
+using Internal.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+#pragma warning disable SA1121 // explicitly using type aliases instead of built-in types
+#if BIT64
+using nuint = System.UInt64;
+#else
+using nuint = System.UInt32;
+#endif
+
+namespace System.Runtime.CompilerServices
+{
+ internal static unsafe class CastHelpers
+ {
+ private static int[]? s_table;
+
+ [DebuggerDisplay("Source = {_source}; Target = {_targetAndResult & ~1}; Result = {_targetAndResult & 1}; VersionNum = {_version & ((1 << 29) - 1)}; Distance = {_version >> 29};")]
+ [StructLayout(LayoutKind.Sequential)]
+ private struct CastCacheEntry
+ {
+ // version has the following structure:
+ // [ distance:3bit | versionNum:29bit ]
+ //
+ // distance is how many iterations the entry is from it ideal position.
+ // we use that for preemption.
+ //
+ // versionNum is a monotonicaly increasing numerical tag.
+ // Writer "claims" entry by atomically incrementing the tag. Thus odd number indicates an entry in progress.
+ // Upon completion of adding an entry the tag is incremented again making it even. Even number indicates a complete entry.
+ //
+ // Readers will read the version twice before and after retrieving the entry.
+ // To have a usable entry both reads must yield the same even version.
+ //
+ internal int _version;
+ internal nuint _source;
+ // pointers have unused lower bits due to alignment, we use one for the result
+ internal nuint _targetAndResult;
+ };
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int KeyToBucket(int[] table, nuint source, nuint target)
+ {
+ // upper bits of addresses do not vary much, so to reduce loss due to cancelling out,
+ // we do `rotl(source, ) ^ target` for mixing inputs.
+ // then we use fibonacci hashing to reduce the value to desired size.
+
+ int hashShift = HashShift(table);
+#if BIT64
+ ulong hash = (((ulong)source << 32) | ((ulong)source >> 32)) ^ (ulong)target;
+ return (int)((hash * 11400714819323198485ul) >> hashShift);
+#else
+ uint hash = (((uint)source >> 16) | ((uint)source << 16)) ^ (uint)target;
+ return (int)((hash * 2654435769u) >> hashShift);
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static ref int AuxData(int[] table)
+ {
+ // element 0 is used for embedded aux data
+ return ref MemoryMarshal.GetArrayDataReference(table);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static ref CastCacheEntry Element(int[] table, int index)
+ {
+ // element 0 is used for embedded aux data, skip it
+ return ref Unsafe.Add(ref Unsafe.As(ref AuxData(table)), index + 1);
+ }
+
+ // TableMask is "size - 1"
+ // we need that more often that we need size
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int TableMask(int[] table)
+ {
+ return AuxData(table);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int HashShift(int[] table)
+ {
+ return Unsafe.Add(ref AuxData(table), 1);
+ }
+
+ private enum CastResult
+ {
+ CannotCast = 0,
+ CanCast = 1,
+ MaybeCast = 2
+ }
+
+ // NOTE!!
+ // This is a copy of C++ implementation in castcache.cpp
+ // Keep the copies, if possible, in sync.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static CastResult TryGet(nuint source, nuint target)
+ {
+ const int BUCKET_SIZE = 8;
+ int[]? table = s_table;
+
+ // we use NULL as a sentinel for a rare case when a table could not be allocated
+ // because we avoid OOMs.
+ // we could use 0-element table instead, but then we would have to check the size here.
+ if (table != null)
+ {
+ int index = KeyToBucket(table, source, target);
+ for (int i = 0; i < BUCKET_SIZE;)
+ {
+ ref CastCacheEntry pEntry = ref Element(table, index);
+
+ // must read in this order: version -> entry parts -> version
+ // if version is odd or changes, the entry is inconsistent and thus ignored
+ int version = Volatile.Read(ref pEntry._version);
+ nuint entrySource = pEntry._source;
+
+ // mask the lower version bit to make it even.
+ // This way we can check if version is odd or changing in just one compare.
+ version &= ~1;
+
+ if (entrySource == source)
+ {
+ nuint entryTargetAndResult = Volatile.Read(ref pEntry._targetAndResult);
+ // target never has its lower bit set.
+ // a matching entryTargetAndResult would the have same bits, except for the lowest one, which is the result.
+ entryTargetAndResult ^= target;
+ if (entryTargetAndResult <= 1)
+ {
+ if (version != pEntry._version)
+ {
+ // oh, so close, the entry is in inconsistent state.
+ // it is either changing or has changed while we were reading.
+ // treat it as a miss.
+ break;
+ }
+
+ return (CastResult)entryTargetAndResult;
+ }
+ }
+
+ if (version == 0)
+ {
+ // the rest of the bucket is unclaimed, no point to search further
+ break;
+ }
+
+ // quadratic reprobe
+ i++;
+ index = (index + i) & TableMask(table);
+ }
+ }
+ return CastResult.MaybeCast;
+ }
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern object IsInstanceOfAny_NoCacheLookup(void* toTypeHnd, object obj);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern object ChkCastAny_NoCacheLookup(void* toTypeHnd, object obj);
+
+ // IsInstanceOf test used for unusual cases (naked type parameters, variant generic types)
+ // Unlike the IsInstanceOfInterface and IsInstanceOfClass functions,
+ // this test must deal with all kinds of type tests
+ [DebuggerHidden]
+ [StackTraceHidden]
+ [DebuggerStepThrough]
+ private static object? IsInstanceOfAny(void* toTypeHnd, object? obj)
+ {
+ if (obj != null)
+ {
+ void* mt = RuntimeHelpers.GetMethodTable(obj);
+ if (mt != toTypeHnd)
+ {
+ CastResult result = TryGet((nuint)mt, (nuint)toTypeHnd);
+ if (result == CastResult.CanCast)
+ {
+ // do nothing
+ }
+ else if (result == CastResult.CannotCast)
+ {
+ obj = null;
+ }
+ else
+ {
+ goto slowPath;
+ }
+ }
+ }
+
+ return obj;
+
+ slowPath:
+ // fall through to the slow helper
+ return IsInstanceOfAny_NoCacheLookup(toTypeHnd, obj);
+ }
+
+ [DebuggerHidden]
+ [StackTraceHidden]
+ [DebuggerStepThrough]
+ private static object? IsInstanceOfInterface(void* toTypeHnd, object? obj)
+ {
+ if (obj != null)
+ {
+ MethodTable* mt = RuntimeHelpers.GetMethodTable(obj);
+ nuint interfaceCount = mt->InterfaceCount;
+ if (interfaceCount != 0)
+ {
+ MethodTable** interfaceMap = mt->InterfaceMap;
+ for (nuint i = 0; ; i += 4)
+ {
+ if (interfaceMap[i + 0] == toTypeHnd)
+ goto done;
+ if (--interfaceCount == 0)
+ break;
+ if (interfaceMap[i + 1] == toTypeHnd)
+ goto done;
+ if (--interfaceCount == 0)
+ break;
+ if (interfaceMap[i + 2] == toTypeHnd)
+ goto done;
+ if (--interfaceCount == 0)
+ break;
+ if (interfaceMap[i + 3] == toTypeHnd)
+ goto done;
+ if (--interfaceCount == 0)
+ break;
+ }
+ }
+
+ if (mt->NonTrivialInterfaceCast)
+ {
+ goto slowPath;
+ }
+
+ obj = null;
+ }
+
+ done:
+ return obj;
+
+ slowPath:
+ return IsInstanceHelper(toTypeHnd, obj);
+ }
+
+ [DebuggerHidden]
+ [StackTraceHidden]
+ [DebuggerStepThrough]
+ private static object? IsInstanceOfClass(void* toTypeHnd, object? obj)
+ {
+ if (obj == null || RuntimeHelpers.GetMethodTable(obj) == toTypeHnd)
+ return obj;
+
+ MethodTable* mt = RuntimeHelpers.GetMethodTable(obj)->ParentMethodTable;
+ for (; ; )
+ {
+ if (mt == toTypeHnd)
+ goto done;
+
+ if (mt == null)
+ break;
+
+ mt = mt->ParentMethodTable;
+ if (mt == toTypeHnd)
+ goto done;
+
+ if (mt == null)
+ break;
+
+ mt = mt->ParentMethodTable;
+ if (mt == toTypeHnd)
+ goto done;
+
+ if (mt == null)
+ break;
+
+ mt = mt->ParentMethodTable;
+ if (mt == toTypeHnd)
+ goto done;
+
+ if (mt == null)
+ break;
+
+ mt = mt->ParentMethodTable;
+ }
+
+ if (RuntimeHelpers.GetMethodTable(obj)->HasTypeEquivalence)
+ {
+ goto slowPath;
+ }
+
+ obj = null;
+
+ done:
+ return obj;
+
+ slowPath:
+ return IsInstanceHelper(toTypeHnd, obj);
+ }
+
+ [DebuggerHidden]
+ [StackTraceHidden]
+ [DebuggerStepThrough]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static object? IsInstanceHelper(void* toTypeHnd, object obj)
+ {
+ CastResult result = TryGet((nuint)RuntimeHelpers.GetMethodTable(obj), (nuint)toTypeHnd);
+ if (result == CastResult.CanCast)
+ {
+ return obj;
+ }
+ else if (result == CastResult.CannotCast)
+ {
+ return null;
+ }
+
+ // fall through to the slow helper
+ return IsInstanceOfAny_NoCacheLookup(toTypeHnd, obj);
+ }
+
+ // ChkCast test used for unusual cases (naked type parameters, variant generic types)
+ // Unlike the ChkCastInterface and ChkCastClass functions,
+ // this test must deal with all kinds of type tests
+ [DebuggerHidden]
+ [StackTraceHidden]
+ [DebuggerStepThrough]
+ private static object? ChkCastAny(void* toTypeHnd, object? obj)
+ {
+ CastResult result;
+
+ if (obj != null)
+ {
+ void* mt = RuntimeHelpers.GetMethodTable(obj);
+ if (mt != toTypeHnd)
+ {
+ result = TryGet((nuint)mt, (nuint)toTypeHnd);
+ if (result != CastResult.CanCast)
+ {
+ goto slowPath;
+ }
+ }
+ }
+
+ return obj;
+
+ slowPath:
+ // fall through to the slow helper
+ object objRet = ChkCastAny_NoCacheLookup(toTypeHnd, obj);
+ // Make sure that the fast helper have not lied
+ Debug.Assert(result != CastResult.CannotCast);
+ return objRet;
+ }
+
+ [DebuggerHidden]
+ [StackTraceHidden]
+ [DebuggerStepThrough]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static object? ChkCastHelper(void* toTypeHnd, object obj)
+ {
+ CastResult result = TryGet((nuint)RuntimeHelpers.GetMethodTable(obj), (nuint)toTypeHnd);
+ if (result == CastResult.CanCast)
+ {
+ return obj;
+ }
+
+ // fall through to the slow helper
+ return ChkCastAny_NoCacheLookup(toTypeHnd, obj);
+ }
+
+ [DebuggerHidden]
+ [StackTraceHidden]
+ [DebuggerStepThrough]
+ private static object? ChkCastInterface(void* toTypeHnd, object? obj)
+ {
+ if (obj != null)
+ {
+ MethodTable* mt = RuntimeHelpers.GetMethodTable(obj);
+ nuint interfaceCount = mt->InterfaceCount;
+ if (interfaceCount == 0)
+ {
+ goto slowPath;
+ }
+
+ MethodTable** interfaceMap = mt->InterfaceMap;
+ for (nuint i = 0; ; i += 4)
+ {
+ if (interfaceMap[i + 0] == toTypeHnd)
+ goto done;
+ if (--interfaceCount == 0)
+ goto slowPath;
+ if (interfaceMap[i + 1] == toTypeHnd)
+ goto done;
+ if (--interfaceCount == 0)
+ goto slowPath;
+ if (interfaceMap[i + 2] == toTypeHnd)
+ goto done;
+ if (--interfaceCount == 0)
+ goto slowPath;
+ if (interfaceMap[i + 3] == toTypeHnd)
+ goto done;
+ if (--interfaceCount == 0)
+ goto slowPath;
+ }
+ }
+
+ done:
+ return obj;
+
+ slowPath:
+ return ChkCastHelper(toTypeHnd, obj);
+ }
+
+ [DebuggerHidden]
+ [StackTraceHidden]
+ [DebuggerStepThrough]
+ private static object? ChkCastClass(void* toTypeHnd, object? obj)
+ {
+ if (obj == null || RuntimeHelpers.GetMethodTable(obj) == toTypeHnd)
+ {
+ return obj;
+ }
+
+ return ChkCastClassSpecial(toTypeHnd, obj);
+ }
+
+ [DebuggerHidden]
+ [StackTraceHidden]
+ [DebuggerStepThrough]
+ private static object? ChkCastClassSpecial(void* toTypeHnd, object obj)
+ {
+ MethodTable* mt = RuntimeHelpers.GetMethodTable(obj);
+ Debug.Assert(mt != toTypeHnd, "The check for the trivial cases should be inlined by the JIT");
+
+ for (; ; )
+ {
+ mt = mt->ParentMethodTable;
+ if (mt == toTypeHnd)
+ goto done;
+
+ if (mt == null)
+ break;
+
+ mt = mt->ParentMethodTable;
+ if (mt == toTypeHnd)
+ goto done;
+
+ if (mt == null)
+ break;
+
+ mt = mt->ParentMethodTable;
+ if (mt == toTypeHnd)
+ goto done;
+
+ if (mt == null)
+ break;
+
+ mt = mt->ParentMethodTable;
+ if (mt == toTypeHnd)
+ goto done;
+
+ if (mt == null)
+ break;
+ }
+
+ goto slowPath;
+
+ done:
+ return obj;
+
+ slowPath:
+ return ChkCastHelper(toTypeHnd, obj);
+ }
+ }
+}
diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
index cd6144da05929..46e9417299445 100644
--- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
+++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
@@ -300,10 +300,37 @@ internal unsafe struct MethodTable
private uint Flags;
[FieldOffset(4)]
public uint BaseSize;
+ [FieldOffset(0x0e)]
+ public ushort InterfaceCount;
+ [FieldOffset(ParentMethodTableOffset)]
+ public MethodTable* ParentMethodTable;
+ [FieldOffset(InterfaceMapOffset)]
+ public MethodTable** InterfaceMap;
// WFLAGS_HIGH_ENUM
private const uint enum_flag_ContainsPointers = 0x01000000;
private const uint enum_flag_HasComponentSize = 0x80000000;
+ private const uint enum_flag_HasTypeEquivalence = 0x00004000;
+ // Types that require non-trivial interface cast have this bit set in the category
+ private const uint enum_flag_NonTrivialInterfaceCast = 0x00080000 // enum_flag_Category_Array
+ | 0x40000000 // enum_flag_ComObject
+ | 0x00400000;// enum_flag_ICastable;
+
+ private const int ParentMethodTableOffset = 0x10
+#if DEBUG
+ + sizeof(nuint) // adjust for debug_m_szClassName
+#endif
+ ;
+
+#if BIT64
+ private const int InterfaceMapOffset = 0x38
+#else
+ private const int InterfaceMapOffset = 0x24
+#endif
+#if DEBUG
+ + sizeof(nuint) // adjust for debug_m_szClassName
+#endif
+ ;
public bool HasComponentSize
{
@@ -321,6 +348,22 @@ public bool ContainsGCPointers
}
}
+ public bool NonTrivialInterfaceCast
+ {
+ get
+ {
+ return (Flags & enum_flag_NonTrivialInterfaceCast) != 0;
+ }
+ }
+
+ public bool HasTypeEquivalence
+ {
+ get
+ {
+ return (Flags & enum_flag_HasTypeEquivalence) != 0;
+ }
+ }
+
public bool IsMultiDimensionalArray
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/coreclr/src/inc/jithelpers.h b/src/coreclr/src/inc/jithelpers.h
index bad18e3b32e57..4dfbba733dcac 100644
--- a/src/coreclr/src/inc/jithelpers.h
+++ b/src/coreclr/src/inc/jithelpers.h
@@ -91,21 +91,22 @@
JITHELPER(CORINFO_HELP_INITCLASS, JIT_InitClass, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_INITINSTCLASS, JIT_InitInstantiatedClass, CORINFO_HELP_SIG_REG_ONLY)
- JITHELPER(CORINFO_HELP_ISINSTANCEOFINTERFACE,JIT_IsInstanceOfInterface, CORINFO_HELP_SIG_REG_ONLY)
- JITHELPER(CORINFO_HELP_ISINSTANCEOFARRAY, JIT_IsInstanceOfArray,CORINFO_HELP_SIG_REG_ONLY)
- JITHELPER(CORINFO_HELP_ISINSTANCEOFCLASS, JIT_IsInstanceOfClass,CORINFO_HELP_SIG_REG_ONLY)
- JITHELPER(CORINFO_HELP_ISINSTANCEOFANY, JIT_IsInstanceOfAny,CORINFO_HELP_SIG_REG_ONLY)
+ // Casting helpers
+ DYNAMICJITHELPER(CORINFO_HELP_ISINSTANCEOFINTERFACE, NULL, CORINFO_HELP_SIG_REG_ONLY)
+ DYNAMICJITHELPER(CORINFO_HELP_ISINSTANCEOFARRAY, NULL, CORINFO_HELP_SIG_REG_ONLY)
+ DYNAMICJITHELPER(CORINFO_HELP_ISINSTANCEOFCLASS, NULL, CORINFO_HELP_SIG_REG_ONLY)
+ DYNAMICJITHELPER(CORINFO_HELP_ISINSTANCEOFANY, NULL, CORINFO_HELP_SIG_REG_ONLY)
+ DYNAMICJITHELPER(CORINFO_HELP_CHKCASTINTERFACE, NULL, CORINFO_HELP_SIG_REG_ONLY)
+ DYNAMICJITHELPER(CORINFO_HELP_CHKCASTARRAY, NULL, CORINFO_HELP_SIG_REG_ONLY)
+ DYNAMICJITHELPER(CORINFO_HELP_CHKCASTCLASS, NULL, CORINFO_HELP_SIG_REG_ONLY)
+ DYNAMICJITHELPER(CORINFO_HELP_CHKCASTANY, NULL, CORINFO_HELP_SIG_REG_ONLY)
+ DYNAMICJITHELPER(CORINFO_HELP_CHKCASTCLASS_SPECIAL, NULL, CORINFO_HELP_SIG_REG_ONLY)
- JITHELPER(CORINFO_HELP_CHKCASTINTERFACE, JIT_ChkCastInterface,CORINFO_HELP_SIG_REG_ONLY)
- JITHELPER(CORINFO_HELP_CHKCASTARRAY, JIT_ChkCastArray, CORINFO_HELP_SIG_REG_ONLY)
- JITHELPER(CORINFO_HELP_CHKCASTCLASS, JIT_ChkCastClass, CORINFO_HELP_SIG_REG_ONLY)
- JITHELPER(CORINFO_HELP_CHKCASTANY, JIT_ChkCastAny, CORINFO_HELP_SIG_REG_ONLY)
-
- JITHELPER(CORINFO_HELP_CHKCASTCLASS_SPECIAL,JIT_ChkCastClassSpecial,CORINFO_HELP_SIG_REG_ONLY)
DYNAMICJITHELPER(CORINFO_HELP_BOX, JIT_Box, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_BOX_NULLABLE, JIT_Box, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_UNBOX, JIT_Unbox, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_UNBOX_NULLABLE, JIT_Unbox_Nullable, CORINFO_HELP_SIG_4_STACK)
+
JITHELPER(CORINFO_HELP_GETREFANY, JIT_GetRefAny, CORINFO_HELP_SIG_8_STACK)
#if defined(_TARGET_ARM_)
DYNAMICJITHELPER(CORINFO_HELP_ARRADDR_ST, JIT_Stelem_Ref, CORINFO_HELP_SIG_4_STACK)
diff --git a/src/coreclr/src/vm/amd64/JitHelpers_Fast.asm b/src/coreclr/src/vm/amd64/JitHelpers_Fast.asm
index 6e0919185cb70..171e5a2c769e9 100644
--- a/src/coreclr/src/vm/amd64/JitHelpers_Fast.asm
+++ b/src/coreclr/src/vm/amd64/JitHelpers_Fast.asm
@@ -52,378 +52,6 @@ endif
extern JIT_InternalThrow:proc
-extern JITutil_ChkCastInterface:proc
-extern JITutil_IsInstanceOfInterface:proc
-extern JITutil_ChkCastAny:proc
-extern JITutil_IsInstanceOfAny:proc
-
-;EXTERN_C Object* JIT_IsInstanceOfClass(MethodTable* pMT, Object* pObject);
-LEAF_ENTRY JIT_IsInstanceOfClass, _TEXT
- ; move rdx into rax in case of a match or null
- mov rax, rdx
-
- ; check if the instance is null
- test rdx, rdx
- je IsNullInst
-
- ; check is the MethodTable for the instance matches pMT
- cmp rcx, qword ptr [rdx]
- jne JIT_IsInstanceOfClass2
-
- IsNullInst:
- REPRET
-LEAF_END JIT_IsInstanceOfClass, _TEXT
-
-LEAF_ENTRY JIT_IsInstanceOfClass2, _TEXT
- ; check if the parent class matches.
- ; start by putting the MethodTable for the instance in rdx
- mov rdx, qword ptr [rdx]
-
- align 16
- CheckParent:
- ; NULL parent MethodTable* indicates that we're at the top of the hierarchy
-
- ; unroll 0
- mov rdx, qword ptr [rdx + OFFSETOF__MethodTable__m_pParentMethodTable]
- cmp rcx, rdx
- je IsInst
- test rdx, rdx
- je DoneWithLoop
-
- ; unroll 1
- mov rdx, qword ptr [rdx + OFFSETOF__MethodTable__m_pParentMethodTable]
- cmp rcx, rdx
- je IsInst
- test rdx, rdx
- je DoneWithLoop
-
- ; unroll 2
- mov rdx, qword ptr [rdx + OFFSETOF__MethodTable__m_pParentMethodTable]
- cmp rcx, rdx
- je IsInst
- test rdx, rdx
- je DoneWithLoop
-
- ; unroll 3
- mov rdx, qword ptr [rdx + OFFSETOF__MethodTable__m_pParentMethodTable]
- cmp rcx, rdx
- je IsInst
- test rdx, rdx
- jne CheckParent
-
- align 16
- DoneWithLoop:
-if METHODTABLE_EQUIVALENCE_FLAGS gt 0
- ; check if the instance is a proxy or has type equivalence
- ; get the MethodTable of the original Object (stored earlier in rax)
- mov rdx, [rax]
- test dword ptr [rdx + OFFSETOF__MethodTable__m_dwFlags], METHODTABLE_EQUIVALENCE_FLAGS
- jne SlowPath
-endif ; METHODTABLE_EQUIVALENCE_FLAGS gt 0
-
- ; we didn't find a match in the ParentMethodTable hierarchy
- ; and it isn't a proxy and doesn't have type equivalence, return NULL
- xor eax, eax
- ret
-if METHODTABLE_EQUIVALENCE_FLAGS gt 0
- SlowPath:
- ; Set up the args to call JITutil_IsInstanceOfAny. Note that rcx already contains
- ; the MethodTable*
- mov rdx, rax ; rdx = Object*
-
- ; Call out to JITutil_IsInstanceOfAny to handle the proxy/equivalence case.
- jmp JITutil_IsInstanceOfAny
-endif ; METHODTABLE_EQUIVALENCE_FLAGS gt 0
- ; if it is a null instance then rax is null
- ; if they match then rax contains the instance
- align 16
- IsInst:
- REPRET
-LEAF_END JIT_IsInstanceOfClass2, _TEXT
-
-; TODO: this is not necessary... we will be calling JIT_ChkCastClass2 all of the time
-; now that the JIT inlines the null check and the exact MT comparison... Or are
-; they only doing it on the IBC hot path??? Look into that. If it will turn out
-; to be cold then put it down at the bottom.
-
-;EXTERN_C Object* JIT_ChkCastClass(MethodTable* pMT, Object* pObject);
-LEAF_ENTRY JIT_ChkCastClass, _TEXT
- ; check if the instance is null
- test rdx, rdx
- je IsNullInst
-
- ; check if the MethodTable for the instance matches pMT
- cmp rcx, qword ptr [rdx]
- jne JIT_ChkCastClassSpecial
-
- IsNullInst:
- ; setup the return value for a match or null
- mov rax, rdx
- ret
-LEAF_END JIT_ChkCastClass, _TEXT
-
-LEAF_ENTRY JIT_ChkCastClassSpecial, _TEXT
- ; save off the instance in case it is a proxy, and to setup
- ; our return value for a match
- mov rax, rdx
-
- ; check if the parent class matches.
- ; start by putting the MethodTable for the instance in rdx
- mov rdx, qword ptr [rdx]
- align 16
- CheckParent:
- ; NULL parent MethodTable* indicates that we're at the top of the hierarchy
-
- ; unroll 0
- mov rdx, qword ptr [rdx + OFFSETOF__MethodTable__m_pParentMethodTable]
- cmp rcx, rdx
- je IsInst
- test rdx, rdx
- je DoneWithLoop
-
- ; unroll 1
- mov rdx, qword ptr [rdx + OFFSETOF__MethodTable__m_pParentMethodTable]
- cmp rcx, rdx
- je IsInst
- test rdx, rdx
- je DoneWithLoop
-
- ; unroll 2
- mov rdx, qword ptr [rdx + OFFSETOF__MethodTable__m_pParentMethodTable]
- cmp rcx, rdx
- je IsInst
- test rdx, rdx
- je DoneWithLoop
-
- ; unroll 3
- mov rdx, qword ptr [rdx + OFFSETOF__MethodTable__m_pParentMethodTable]
- cmp rcx, rdx
- je IsInst
- test rdx, rdx
- jne CheckParent
-
- align 16
- DoneWithLoop:
- ; Set up the args to call JITutil_ChkCastAny. Note that rcx already contains the MethodTable*
- mov rdx, rax ; rdx = Object*
-
- ; Call out to JITutil_ChkCastAny to handle the proxy case and throw a rich
- ; InvalidCastException in case of failure.
- jmp JITutil_ChkCastAny
-
- ; if it is a null instance then rax is null
- ; if they match then rax contains the instance
- align 16
- IsInst:
- REPRET
-LEAF_END JIT_ChkCastClassSpecial, _TEXT
-
-FIX_INDIRECTION macro Reg
-ifdef FEATURE_PREJIT
- test Reg, 1
- jz @F
- mov Reg, [Reg-1]
- @@:
-endif
-endm
-
-; PERF TODO: consider prefetching the entire interface map into the cache
-
-; For all bizarre castes this quickly fails and falls back onto the JITutil_IsInstanceOfInterface
-; helper, this means that all failure cases take the slow path as well.
-;
-; This can trash r10/r11
-LEAF_ENTRY JIT_IsInstanceOfInterface, _TEXT
- test rdx, rdx
- jz IsNullInst
-
- ; get methodtable
- mov rax, [rdx]
- mov r11w, word ptr [rax + OFFSETOF__MethodTable__m_wNumInterfaces]
-
- test r11w, r11w
- jz DoBizarre
-
- ; fetch interface map ptr
- mov rax, [rax + OFFSETOF__MethodTable__m_pInterfaceMap]
-
- ; r11 holds number of interfaces
- ; rax is pointer to beginning of interface map list
- align 16
- Top:
- ; rax -> InterfaceInfo_t* into the interface map, aligned to 4 entries
- ; use offsets of SIZEOF__InterfaceInfo_t to get at entry 1, 2, 3 in this
- ; block. If we make it through the full 4 without a hit we'll move to
- ; the next block of 4 and try again.
-
- ; unroll 0
-ifdef FEATURE_PREJIT
- mov r10, [rax + OFFSETOF__InterfaceInfo_t__m_pMethodTable]
- FIX_INDIRECTION r10
- cmp rcx, r10
-else
- cmp rcx, [rax + OFFSETOF__InterfaceInfo_t__m_pMethodTable]
-endif
- je Found
- ; move to next entry in list
- dec r11w
- jz DoBizarre
-
- ; unroll 1
-ifdef FEATURE_PREJIT
- mov r10, [rax + SIZEOF__InterfaceInfo_t + OFFSETOF__InterfaceInfo_t__m_pMethodTable]
- FIX_INDIRECTION r10
- cmp rcx, r10
-else
- cmp rcx, [rax + SIZEOF__InterfaceInfo_t + OFFSETOF__InterfaceInfo_t__m_pMethodTable]
-endif
- je Found
- ; move to next entry in list
- dec r11w
- jz DoBizarre
-
- ; unroll 2
-ifdef FEATURE_PREJIT
- mov r10, [rax + 2 * SIZEOF__InterfaceInfo_t + OFFSETOF__InterfaceInfo_t__m_pMethodTable]
- FIX_INDIRECTION r10
- cmp rcx, r10
-else
- cmp rcx, [rax + 2 * SIZEOF__InterfaceInfo_t + OFFSETOF__InterfaceInfo_t__m_pMethodTable]
-endif
- je Found
- ; move to next entry in list
- dec r11w
- jz DoBizarre
-
- ; unroll 3
-ifdef FEATURE_PREJIT
- mov r10, [rax + 3 * SIZEOF__InterfaceInfo_t + OFFSETOF__InterfaceInfo_t__m_pMethodTable]
- FIX_INDIRECTION r10
- cmp rcx, r10
-else
- cmp rcx, [rax + 3 * SIZEOF__InterfaceInfo_t + OFFSETOF__InterfaceInfo_t__m_pMethodTable]
-endif
- je Found
- ; move to next entry in list
- dec r11w
- jz DoBizarre
-
- ; if we didn't find the entry in this loop jump to the next 4 entries in the map
- add rax, 4 * SIZEOF__InterfaceInfo_t
- jmp Top
-
- DoBizarre:
- mov rax, [rdx]
- test dword ptr [rax + OFFSETOF__MethodTable__m_dwFlags], METHODTABLE_NONTRIVIALINTERFACECAST_FLAGS
- jnz NonTrivialCast
- xor rax,rax
- ret
-
- align 16
- Found:
- IsNullInst:
- ; return the successful instance
- mov rax, rdx
- ret
-
- NonTrivialCast:
- jmp JITutil_IsInstanceOfInterface
-LEAF_END JIT_IsInstanceOfInterface, _TEXT
-
-; For all bizarre castes this quickly fails and falls back onto the JITutil_ChkCastInterface
-; helper, this means that all failure cases take the slow path as well.
-;
-; This can trash r10/r11
-LEAF_ENTRY JIT_ChkCastInterface, _TEXT
- test rdx, rdx
- jz IsNullInst
-
- ; get methodtable
- mov rax, [rdx]
- mov r11w, word ptr [rax + OFFSETOF__MethodTable__m_wNumInterfaces]
-
- ; speculatively fetch interface map ptr
- mov rax, [rax + OFFSETOF__MethodTable__m_pInterfaceMap]
-
- test r11w, r11w
- jz DoBizarre
-
- ; r11 holds number of interfaces
- ; rax is pointer to beginning of interface map list
- align 16
- Top:
- ; rax -> InterfaceInfo_t* into the interface map, aligned to 4 entries
- ; use offsets of SIZEOF__InterfaceInfo_t to get at entry 1, 2, 3 in this
- ; block. If we make it through the full 4 without a hit we'll move to
- ; the next block of 4 and try again.
-
- ; unroll 0
-ifdef FEATURE_PREJIT
- mov r10, [rax + OFFSETOF__InterfaceInfo_t__m_pMethodTable]
- FIX_INDIRECTION r10
- cmp rcx, r10
-else
- cmp rcx, [rax + OFFSETOF__InterfaceInfo_t__m_pMethodTable]
-endif
- je Found
- ; move to next entry in list
- dec r11w
- jz DoBizarre
-
- ; unroll 1
-ifdef FEATURE_PREJIT
- mov r10, [rax + SIZEOF__InterfaceInfo_t + OFFSETOF__InterfaceInfo_t__m_pMethodTable]
- FIX_INDIRECTION r10
- cmp rcx, r10
-else
- cmp rcx, [rax + SIZEOF__InterfaceInfo_t + OFFSETOF__InterfaceInfo_t__m_pMethodTable]
-endif
- je Found
- ; move to next entry in list
- dec r11w
- jz DoBizarre
-
- ; unroll 2
-ifdef FEATURE_PREJIT
- mov r10, [rax + 2 * SIZEOF__InterfaceInfo_t + OFFSETOF__InterfaceInfo_t__m_pMethodTable]
- FIX_INDIRECTION r10
- cmp rcx, r10
-else
- cmp rcx, [rax + 2 * SIZEOF__InterfaceInfo_t + OFFSETOF__InterfaceInfo_t__m_pMethodTable]
-endif
- je Found
- ; move to next entry in list
- dec r11w
- jz DoBizarre
-
- ; unroll 3
-ifdef FEATURE_PREJIT
- mov r10, [rax + 3 * SIZEOF__InterfaceInfo_t + OFFSETOF__InterfaceInfo_t__m_pMethodTable]
- FIX_INDIRECTION r10
- cmp rcx, r10
-else
- cmp rcx, [rax + 3 * SIZEOF__InterfaceInfo_t + OFFSETOF__InterfaceInfo_t__m_pMethodTable]
-endif
- je Found
- ; move to next entry in list
- dec r11w
- jz DoBizarre
-
- ; if we didn't find the entry in this loop jump to the next 4 entries in the map
- add rax, 4 * SIZEOF__InterfaceInfo_t
- jmp Top
-
- DoBizarre:
- jmp JITutil_ChkCastInterface
-
- align 16
- Found:
- IsNullInst:
- ; return either NULL or the successful instance
- mov rax, rdx
- ret
-LEAF_END JIT_ChkCastInterface, _TEXT
-
; There is an even more optimized version of these helpers possible which takes
; advantage of knowledge of which way the ephemeral heap is growing to only do 1/2
; that check (this is more significant in the JIT_WriteBarrier case).
diff --git a/src/coreclr/src/vm/amd64/cgencpu.h b/src/coreclr/src/vm/amd64/cgencpu.h
index 7e604c1020238..53f73231daf59 100644
--- a/src/coreclr/src/vm/amd64/cgencpu.h
+++ b/src/coreclr/src/vm/amd64/cgencpu.h
@@ -528,14 +528,6 @@ inline BOOL ClrFlushInstructionCache(LPCVOID pCodeAddr, size_t sizeOfCode)
#define JIT_GetSharedGCStaticBaseNoCtor JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain
#define JIT_GetSharedNonGCStaticBaseNoCtor JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain
-#ifndef FEATURE_PAL
-#define JIT_ChkCastClass JIT_ChkCastClass
-#define JIT_ChkCastClassSpecial JIT_ChkCastClassSpecial
-#define JIT_IsInstanceOfClass JIT_IsInstanceOfClass
-#define JIT_ChkCastInterface JIT_ChkCastInterface
-#define JIT_IsInstanceOfInterface JIT_IsInstanceOfInterface
-#endif // FEATURE_PAL
-
#define JIT_Stelem_Ref JIT_Stelem_Ref
#endif // __cgencpu_h__
diff --git a/src/coreclr/src/vm/appdomain.cpp b/src/coreclr/src/vm/appdomain.cpp
index 70e038c6d63a8..0a61d8e8fca23 100644
--- a/src/coreclr/src/vm/appdomain.cpp
+++ b/src/coreclr/src/vm/appdomain.cpp
@@ -32,6 +32,7 @@
#include "comdelegate.h"
#include "siginfo.hpp"
#include "typekey.h"
+#include "castcache.h"
#include "caparser.h"
#include "ecall.h"
@@ -1987,6 +1988,13 @@ void SystemDomain::LoadBaseSystemClasses()
MscorlibBinder::LoadPrimitiveType(ELEMENT_TYPE_I);
MscorlibBinder::LoadPrimitiveType(ELEMENT_TYPE_U);
+ // further loading of nonprimitive types may need casting support.
+ // initialize cast cache here.
+#ifndef CROSSGEN_COMPILE
+ CastCache::Initialize();
+ ECall::PopulateManagedCastHelpers();
+#endif // CROSSGEN_COMPILE
+
// unfortunately, the following cannot be delay loaded since the jit
// uses it to compute method attributes within a function that cannot
// handle Complus exception and the following call goes through a path
diff --git a/src/coreclr/src/vm/castcache.cpp b/src/coreclr/src/vm/castcache.cpp
index 9216385027f8c..b1f14b43c70d1 100644
--- a/src/coreclr/src/vm/castcache.cpp
+++ b/src/coreclr/src/vm/castcache.cpp
@@ -10,8 +10,8 @@
#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
-OBJECTHANDLE CastCache::s_cache = NULL;
-DWORD CastCache::s_lastFlushSize = INITIAL_CACHE_SIZE;
+BASEARRAYREF* CastCache::s_pTableRef = NULL;
+DWORD CastCache::s_lastFlushSize = INITIAL_CACHE_SIZE;
BASEARRAYREF CastCache::CreateCastCache(DWORD size)
{
@@ -93,7 +93,7 @@ BOOL CastCache::MaybeReplaceCacheWithLarger(DWORD size)
return FALSE;
}
- StoreObjectInHandle(s_cache, newTable);
+ SetObjectReference((OBJECTREF *)s_pTableRef, newTable);
return TRUE;
}
@@ -107,10 +107,10 @@ void CastCache::FlushCurrentCache()
}
CONTRACTL_END;
- BASEARRAYREF currentTableRef = (BASEARRAYREF)ObjectFromHandle(s_cache);
+ BASEARRAYREF currentTableRef = *s_pTableRef;
s_lastFlushSize = !currentTableRef ? INITIAL_CACHE_SIZE : CacheElementCount(currentTableRef);
- StoreObjectInHandle(s_cache, NULL);
+ *s_pTableRef = NULL;
}
void CastCache::Initialize()
@@ -123,7 +123,10 @@ void CastCache::Initialize()
}
CONTRACTL_END;
- s_cache = CreateGlobalHandle(NULL);
+ FieldDesc* pTableField = MscorlibBinder::GetField(FIELD__CASTHELPERS__TABLE);
+
+ GCX_COOP();
+ s_pTableRef = (BASEARRAYREF*)pTableField->GetCurrentStaticAddress();
}
TypeHandle::CastResult CastCache::TryGet(TADDR source, TADDR target)
@@ -136,59 +139,58 @@ TypeHandle::CastResult CastCache::TryGet(TADDR source, TADDR target)
}
CONTRACTL_END;
- BASEARRAYREF table = (BASEARRAYREF)ObjectFromHandle(s_cache);
+ BASEARRAYREF table = *s_pTableRef;
// we use NULL as a sentinel for a rare case when a table could not be allocated
- // because we avoid OOMs in conversions
+ // because we avoid OOMs.
// we could use 0-element table instead, but then we would have to check the size here.
- if (!table)
+ if (table != NULL)
{
- return TypeHandle::MaybeCast;
- }
-
- DWORD index = KeyToBucket(table, source, target);
- CastCacheEntry* pEntry = &Elements(table)[index];
+ DWORD index = KeyToBucket(table, source, target);
+ for (DWORD i = 0; i < BUCKET_SIZE;)
+ {
+ CastCacheEntry* pEntry = &Elements(table)[index];
- for (DWORD i = 0; i < BUCKET_SIZE; i++)
- {
- // must read in this order: version -> entry parts -> version
- // if version is odd or changes, the entry is inconsistent and thus ignored
- DWORD version1 = VolatileLoad(&pEntry->version);
- TADDR entrySource = pEntry->source;
+ // must read in this order: version -> entry parts -> version
+ // if version is odd or changes, the entry is inconsistent and thus ignored
+ DWORD version1 = VolatileLoad(&pEntry->version);
+ TADDR entrySource = pEntry->source;
- if (entrySource == source)
- {
- TADDR entryTargetAndResult = VolatileLoad(&pEntry->targetAndResult);
+ // mask the lower version bit to make it even.
+ // This way we can check if version is odd or changing in just one compare.
+ version1 &= ~1;
- // target never has its lower bit set.
- // a matching entryTargetAndResult would have same bits, except for the lowest one, which is the result.
- entryTargetAndResult ^= target;
- if (entryTargetAndResult <= 1)
+ if (entrySource == source)
{
- DWORD version2 = pEntry->version;
- if (version2 != version1 || (version1 & 1))
+ TADDR entryTargetAndResult = VolatileLoad(&pEntry->targetAndResult);
+ // target never has its lower bit set.
+ // a matching entryTargetAndResult would have the same bits, except for the lowest one, which is the result.
+ entryTargetAndResult ^= target;
+ if (entryTargetAndResult <= 1)
{
- // oh, so close, the entry is in inconsistent state.
- // it is either changing or has changed while we were reading.
- // treat it as a miss.
- break;
+ if (version1 != pEntry->version)
+ {
+ // oh, so close, the entry is in inconsistent state.
+ // it is either changing or has changed while we were reading.
+ // treat it as a miss.
+ break;
+ }
+
+ return TypeHandle::CastResult(entryTargetAndResult);
}
+ }
- return TypeHandle::CastResult(entryTargetAndResult);
+ if (version1 == 0)
+ {
+ // the rest of the bucket is unclaimed, no point to search further
+ break;
}
- }
- if (version1 == 0)
- {
- // the rest of the bucket is unclaimed, no point to search further
- break;
+ // quadratic reprobe
+ i++;
+ index = (index + i) & TableMask(table);
}
-
- // quadratic reprobe
- index += i;
- pEntry = &Elements(table)[index & TableMask(table)];
}
-
return TypeHandle::MaybeCast;
}
@@ -207,7 +209,7 @@ void CastCache::TrySet(TADDR source, TADDR target, BOOL result)
do
{
- table = (BASEARRAYREF)ObjectFromHandle(s_cache);
+ table = *s_pTableRef;
if (!table)
{
// we did not allocate a table or flushed it, try replacing, but do not continue looping.
@@ -219,7 +221,7 @@ void CastCache::TrySet(TADDR source, TADDR target, BOOL result)
DWORD index = bucket;
CastCacheEntry* pEntry = &Elements(table)[index];
- for (DWORD i = 0; i < BUCKET_SIZE; i++)
+ for (DWORD i = 0; i < BUCKET_SIZE;)
{
// claim the entry if unused or is more distant than us from its origin.
// Note - someone familiar with Robin Hood hashing will notice that
@@ -255,6 +257,7 @@ void CastCache::TrySet(TADDR source, TADDR target, BOOL result)
}
// quadratic reprobe
+ i++;
index += i;
pEntry = &Elements(table)[index & TableMask(table)];
}
@@ -263,7 +266,7 @@ void CastCache::TrySet(TADDR source, TADDR target, BOOL result)
} while (TryGrow(table));
// reread table after TryGrow.
- table = (BASEARRAYREF)ObjectFromHandle(s_cache);
+ table = *s_pTableRef;
if (!table)
{
// we did not allocate a table.
diff --git a/src/coreclr/src/vm/castcache.h b/src/coreclr/src/vm/castcache.h
index f64611303dbca..2cdc2d13bd50e 100644
--- a/src/coreclr/src/vm/castcache.h
+++ b/src/coreclr/src/vm/castcache.h
@@ -58,7 +58,7 @@ class CastCache
// version has the following structure:
// [ distance:3bit | versionNum:29bit ]
//
- // distance is how many iterations is the entry from it ideal position.
+ // distance is how many iterations the entry is from it ideal position.
// we use that for preemption.
//
// versionNum is a monotonicaly increasing numerical tag.
@@ -199,7 +199,7 @@ class CastCache
// We pick 8 as the probe limit (hoping for 4 probes on average), but the number can be refined further.
static const DWORD BUCKET_SIZE = 8;
- static OBJECTHANDLE s_cache;
+ static BASEARRAYREF* s_pTableRef;
static DWORD s_lastFlushSize;
FORCEINLINE static TypeHandle::CastResult TryGetFromCache(TADDR source, TADDR target)
@@ -262,20 +262,20 @@ class CastCache
// then we use fibonacci hashing to reduce the value to desired size.
#if BIT64
- TADDR hash = (((ULONGLONG)source << 32) | ((ULONGLONG)source >> 32)) ^ target;
+ UINT64 hash = (((UINT64)source << 32) | ((UINT64)source >> 32)) ^ (UINT64)target;
return (DWORD)((hash * 11400714819323198485llu) >> HashShift(table));
#else
- TADDR hash = _rotl(source, 16) ^ target;
+ UINT32 hash = (((UINT32)source << 16) | ((UINT32)source >> 16)) ^ (UINT32)target;
return (DWORD)((hash * 2654435769ul) >> HashShift(table));
#endif
}
- FORCEINLINE static byte* AuxData(BASEARRAYREF table)
+ FORCEINLINE static DWORD* AuxData(BASEARRAYREF table)
{
LIMITED_METHOD_CONTRACT;
// element 0 is used for embedded aux data
- return (byte*)OBJECTREFToObject(table) + ARRAYBASE_SIZE;
+ return (DWORD*)((BYTE*)OBJECTREFToObject(table) + ARRAYBASE_SIZE);
}
FORCEINLINE static CastCacheEntry* Elements(BASEARRAYREF table)
@@ -290,19 +290,19 @@ class CastCache
FORCEINLINE static DWORD& TableMask(BASEARRAYREF table)
{
LIMITED_METHOD_CONTRACT;
- return *(DWORD*)AuxData(table);
+ return *AuxData(table);
}
- FORCEINLINE static BYTE& HashShift(BASEARRAYREF table)
+ FORCEINLINE static DWORD& HashShift(BASEARRAYREF table)
{
LIMITED_METHOD_CONTRACT;
- return *((BYTE*)AuxData(table) + sizeof(DWORD));
+ return *(AuxData(table) + 1);
}
- FORCEINLINE static BYTE& VictimCounter(BASEARRAYREF table)
+ FORCEINLINE static DWORD& VictimCounter(BASEARRAYREF table)
{
LIMITED_METHOD_CONTRACT;
- return *((BYTE*)AuxData(table) + sizeof(DWORD) + 1);
+ return *(AuxData(table) + 2);
}
FORCEINLINE static DWORD CacheElementCount(BASEARRAYREF table)
diff --git a/src/coreclr/src/vm/ceemain.cpp b/src/coreclr/src/vm/ceemain.cpp
index f911e120ac82a..e5128f9de578c 100644
--- a/src/coreclr/src/vm/ceemain.cpp
+++ b/src/coreclr/src/vm/ceemain.cpp
@@ -166,7 +166,6 @@
#include "threadsuspend.h"
#include "disassembler.h"
#include "jithost.h"
-#include "castcache.h"
#ifndef FEATURE_PAL
#include "dwreport.h"
@@ -961,9 +960,6 @@ void EEStartupHelper(COINITIEE fFlags)
// Now we really have fully initialized the garbage collector
SetGarbageCollectorFullyInitialized();
- // This will allocate a handle, so do this after GC is initialized.
- CastCache::Initialize();
-
#ifdef DEBUGGING_SUPPORTED
// Make a call to publish the DefaultDomain for the debugger
// This should be done before assemblies/modules are loaded into it (i.e. SystemDomain::Init)
diff --git a/src/coreclr/src/vm/ecall.cpp b/src/coreclr/src/vm/ecall.cpp
index d69154194a86e..7c3fbf2d0c341 100644
--- a/src/coreclr/src/vm/ecall.cpp
+++ b/src/coreclr/src/vm/ecall.cpp
@@ -143,6 +143,57 @@ void ECall::PopulateManagedStringConstructors()
INDEBUG(fInitialized = true);
}
+void ECall::PopulateManagedCastHelpers()
+{
+#ifndef CROSSGEN_COMPILE
+
+ STANDARD_VM_CONTRACT;
+
+ MethodDesc* pMD = MscorlibBinder::GetMethod((BinderMethodID)(METHOD__CASTHELPERS__ISINSTANCEOFANY));
+ PCODE pDest = pMD->GetMultiCallableAddrOfCode();
+ SetJitHelperFunction(CORINFO_HELP_ISINSTANCEOFANY, pDest);
+ // array cast uses the "ANY" helper
+ SetJitHelperFunction(CORINFO_HELP_ISINSTANCEOFARRAY, pDest);
+
+#ifdef FEATURE_PREJIT
+ // When interface table uses indirect references, just set interface casts to "ANY" helper
+ SetJitHelperFunction(CORINFO_HELP_ISINSTANCEOFINTERFACE, pDest);
+#else
+ pMD = MscorlibBinder::GetMethod((BinderMethodID)(METHOD__CASTHELPERS__ISINSTANCEOFINTERFACE));
+ pDest = pMD->GetMultiCallableAddrOfCode();
+ SetJitHelperFunction(CORINFO_HELP_ISINSTANCEOFINTERFACE, pDest);
+#endif
+
+ pMD = MscorlibBinder::GetMethod((BinderMethodID)(METHOD__CASTHELPERS__ISINSTANCEOFCLASS));
+ pDest = pMD->GetMultiCallableAddrOfCode();
+ SetJitHelperFunction(CORINFO_HELP_ISINSTANCEOFCLASS, pDest);
+
+ pMD = MscorlibBinder::GetMethod((BinderMethodID)(METHOD__CASTHELPERS__CHKCASTANY));
+ pDest = pMD->GetMultiCallableAddrOfCode();
+ SetJitHelperFunction(CORINFO_HELP_CHKCASTANY, pDest);
+ // array cast uses the "ANY" helper
+ SetJitHelperFunction(CORINFO_HELP_CHKCASTARRAY, pDest);
+
+#ifdef FEATURE_PREJIT
+ // When interface table uses indirect references, just set interface casts to "ANY" handler
+ SetJitHelperFunction(CORINFO_HELP_CHKCASTINTERFACE, pDest);
+#else
+ pMD = MscorlibBinder::GetMethod((BinderMethodID)(METHOD__CASTHELPERS__CHKCASTINTERFACE));
+ pDest = pMD->GetMultiCallableAddrOfCode();
+ SetJitHelperFunction(CORINFO_HELP_CHKCASTINTERFACE, pDest);
+#endif
+
+ pMD = MscorlibBinder::GetMethod((BinderMethodID)(METHOD__CASTHELPERS__CHKCASTCLASS));
+ pDest = pMD->GetMultiCallableAddrOfCode();
+ SetJitHelperFunction(CORINFO_HELP_CHKCASTCLASS, pDest);
+
+ pMD = MscorlibBinder::GetMethod((BinderMethodID)(METHOD__CASTHELPERS__CHKCASTCLASSSPECIAL));
+ pDest = pMD->GetMultiCallableAddrOfCode();
+ SetJitHelperFunction(CORINFO_HELP_CHKCASTCLASS_SPECIAL, pDest);
+
+#endif //CROSSGEN_COMPILE
+}
+
static CrstStatic gFCallLock;
// This variable is used to force the compiler not to tailcall a function.
diff --git a/src/coreclr/src/vm/ecall.h b/src/coreclr/src/vm/ecall.h
index 29f22c37593bc..cd8c7fea0dba6 100644
--- a/src/coreclr/src/vm/ecall.h
+++ b/src/coreclr/src/vm/ecall.h
@@ -98,6 +98,9 @@ class ECall
static void DynamicallyAssignFCallImpl(PCODE impl, DWORD index);
static void PopulateManagedStringConstructors();
+
+ static void PopulateManagedCastHelpers();
+
#ifdef DACCESS_COMPILE
// Enumerates all gFCallMethods for minidumps.
static void EnumFCallMethods();
diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h
index aa7556a9966fd..79039e998c918 100644
--- a/src/coreclr/src/vm/ecalllist.h
+++ b/src/coreclr/src/vm/ecalllist.h
@@ -714,6 +714,11 @@ FCFuncStart(gClrConfig)
QCFuncElement("GetConfigBoolValue", ClrConfigNative::GetConfigBoolValue)
FCFuncEnd()
+FCFuncStart(gCastHelpers)
+ FCFuncElement("IsInstanceOfAny_NoCacheLookup", ::IsInstanceOfAny_NoCacheLookup)
+ FCFuncElement("ChkCastAny_NoCacheLookup", ::ChkCastAny_NoCacheLookup)
+FCFuncEnd()
+
FCFuncStart(gArrayFuncs)
FCFuncElement("GetCorElementTypeOfElementType", ArrayNative::GetCorElementTypeOfElementType)
FCFuncElement("IsValueOfElementType", ArrayNative::IsValueOfElementType)
@@ -1203,6 +1208,7 @@ FCClassElement("AssemblyLoadContext", "System.Runtime.Loader", gAssemblyLoadCont
FCClassElement("AssemblyName", "System.Reflection", gAssemblyNameFuncs)
FCClassElement("Buffer", "System", gBufferFuncs)
FCClassElement("CLRConfig", "System", gClrConfig)
+FCClassElement("CastHelpers", "System.Runtime.CompilerServices", gCastHelpers)
FCClassElement("CompatibilitySwitch", "System.Runtime.Versioning", gCompatibilitySwitchFuncs)
FCClassElement("CustomAttribute", "System.Reflection", gCOMCustomAttributeFuncs)
FCClassElement("CustomAttributeEncodedArgument", "System.Reflection", gCustomAttributeEncodedArgument)
diff --git a/src/coreclr/src/vm/i386/asmconstants.h b/src/coreclr/src/vm/i386/asmconstants.h
index 6dbf12d8f2eb5..08d0c4254e957 100644
--- a/src/coreclr/src/vm/i386/asmconstants.h
+++ b/src/coreclr/src/vm/i386/asmconstants.h
@@ -265,9 +265,6 @@ ASMCONSTANTS_C_ASSERT(ComPlusCallInfo__m_pRetThunk == offsetof(ComPlusCallInfo,
#endif // FEATURE_COMINTEROP
-#define NonTrivialInterfaceCastFlags (0x00080000 + 0x40000000 + 0x00400000)
-ASMCONSTANTS_C_ASSERT(NonTrivialInterfaceCastFlags == MethodTable::public_enum_flag_NonTrivialInterfaceCast)
-
#define ASM__VTABLE_SLOTS_PER_CHUNK 8
ASMCONSTANTS_C_ASSERT(ASM__VTABLE_SLOTS_PER_CHUNK == VTABLE_SLOTS_PER_CHUNK)
diff --git a/src/coreclr/src/vm/i386/cgencpu.h b/src/coreclr/src/vm/i386/cgencpu.h
index ce96e8b1176d8..3ea0b4f87dddf 100644
--- a/src/coreclr/src/vm/i386/cgencpu.h
+++ b/src/coreclr/src/vm/i386/cgencpu.h
@@ -546,11 +546,6 @@ inline BOOL ClrFlushInstructionCache(LPCVOID pCodeAddr, size_t sizeOfCode)
// #define JIT_GetSharedNonGCStaticBaseNoCtor
#ifndef FEATURE_PAL
-#define JIT_ChkCastClass JIT_ChkCastClass
-#define JIT_ChkCastClassSpecial JIT_ChkCastClassSpecial
-#define JIT_IsInstanceOfClass JIT_IsInstanceOfClass
-#define JIT_ChkCastInterface JIT_ChkCastInterface
-#define JIT_IsInstanceOfInterface JIT_IsInstanceOfInterface
#define JIT_NewCrossContext JIT_NewCrossContext
#define JIT_Stelem_Ref JIT_Stelem_Ref
#endif // FEATURE_PAL
diff --git a/src/coreclr/src/vm/i386/jithelp.asm b/src/coreclr/src/vm/i386/jithelp.asm
index 7d792a0c03671..14c3975523830 100644
--- a/src/coreclr/src/vm/i386/jithelp.asm
+++ b/src/coreclr/src/vm/i386/jithelp.asm
@@ -66,10 +66,6 @@ endif
EXTERN _g_TailCallFrameVptr:DWORD
EXTERN @JIT_FailFast@0:PROC
EXTERN _s_gsCookie:DWORD
-EXTERN @JITutil_IsInstanceOfInterface@8:PROC
-EXTERN @JITutil_ChkCastInterface@8:PROC
-EXTERN @JITutil_IsInstanceOfAny@8:PROC
-EXTERN @JITutil_ChkCastAny@8:PROC
ifdef WRITE_BARRIER_CHECK
; Those global variables are always defined, but should be 0 for Server GC
@@ -1293,148 +1289,6 @@ _JIT_PatchedCodeEnd@0 proc public
ret
_JIT_PatchedCodeEnd@0 endp
-; This is the ASM portion of JIT_IsInstanceOfInterface. For all the bizarre cases, it quickly
-; fails and falls back on the JITutil_IsInstanceOfInterface helper. So all failure cases take
-; the slow path, too.
-;
-; ARGUMENT_REG1 = array or interface to check for.
-; ARGUMENT_REG2 = instance to be cast.
-
- ALIGN 16
-PUBLIC @JIT_IsInstanceOfInterface@8
-@JIT_IsInstanceOfInterface@8 PROC
- test ARGUMENT_REG2, ARGUMENT_REG2
- jz IsNullInst
-
- mov eax, [ARGUMENT_REG2] ; get MethodTable
-
- push ebx
- push esi
- movzx ebx, word ptr [eax+MethodTable_m_wNumInterfaces]
-
- ; check if this MT implements any interfaces
- test ebx, ebx
- jz IsInstanceOfInterfaceDoBizarre
-
- ; move Interface map ptr into eax
- mov eax, [eax+MethodTable_m_pInterfaceMap]
-
-IsInstanceOfInterfaceTop:
- ; eax -> current InterfaceInfo_t entry in interface map list
-ifdef FEATURE_PREJIT
- mov esi, [eax]
- test esi, 1
- ; Move the deference out of line so that this jump is correctly predicted for the case
- ; when there is no indirection
- jnz IsInstanceOfInterfaceIndir
- cmp ARGUMENT_REG1, esi
-else
- cmp ARGUMENT_REG1, [eax]
-endif
- je IsInstanceOfInterfaceFound
-
-IsInstanceOfInterfaceNext:
- add eax, SIZEOF_InterfaceInfo_t
- dec ebx
- jnz IsInstanceOfInterfaceTop
-
- ; fall through to DoBizarre
-
-IsInstanceOfInterfaceDoBizarre:
- pop esi
- pop ebx
- mov eax, [ARGUMENT_REG2] ; get MethodTable
- test dword ptr [eax+MethodTable_m_dwFlags], NonTrivialInterfaceCastFlags
- jnz IsInstanceOfInterfaceNonTrivialCast
-
-IsNullInst:
- xor eax,eax
- ret
-
-ifdef FEATURE_PREJIT
-IsInstanceOfInterfaceIndir:
- cmp ARGUMENT_REG1,[esi-1]
- jne IsInstanceOfInterfaceNext
-endif
-
-IsInstanceOfInterfaceFound:
- pop esi
- pop ebx
- mov eax, ARGUMENT_REG2 ; the successful instance
- ret
-
-IsInstanceOfInterfaceNonTrivialCast:
- jmp @JITutil_IsInstanceOfInterface@8
-
-@JIT_IsInstanceOfInterface@8 endp
-
-; This is the ASM portion of JIT_ChkCastInterface. For all the bizarre cases, it quickly
-; fails and falls back on the JITutil_ChkCastAny helper. So all failure cases take
-; the slow path, too.
-;
-; ARGUMENT_REG1 = array or interface to check for.
-; ARGUMENT_REG2 = instance to be cast.
-
- ALIGN 16
-PUBLIC @JIT_ChkCastInterface@8
-@JIT_ChkCastInterface@8 PROC
- test ARGUMENT_REG2, ARGUMENT_REG2
- jz ChkCastInterfaceIsNullInst
-
- mov eax, [ARGUMENT_REG2] ; get MethodTable
-
- push ebx
- push esi
- movzx ebx, word ptr [eax+MethodTable_m_wNumInterfaces]
-
- ; speculatively move Interface map ptr into eax
- mov eax, [eax+MethodTable_m_pInterfaceMap]
-
- ; check if this MT implements any interfaces
- test ebx, ebx
- jz ChkCastInterfaceDoBizarre
-
-ChkCastInterfaceTop:
- ; eax -> current InterfaceInfo_t entry in interface map list
-ifdef FEATURE_PREJIT
- mov esi, [eax]
- test esi, 1
- ; Move the deference out of line so that this jump is correctly predicted for the case
- ; when there is no indirection
- jnz ChkCastInterfaceIndir
- cmp ARGUMENT_REG1, esi
-else
- cmp ARGUMENT_REG1, [eax]
-endif
- je ChkCastInterfaceFound
-
-ChkCastInterfaceNext:
- add eax, SIZEOF_InterfaceInfo_t
- dec ebx
- jnz ChkCastInterfaceTop
-
- ; fall through to DoBizarre
-
-ChkCastInterfaceDoBizarre:
- pop esi
- pop ebx
- jmp @JITutil_ChkCastInterface@8
-
-ifdef FEATURE_PREJIT
-ChkCastInterfaceIndir:
- cmp ARGUMENT_REG1,[esi-1]
- jne ChkCastInterfaceNext
-endif
-
-ChkCastInterfaceFound:
- pop esi
- pop ebx
-
-ChkCastInterfaceIsNullInst:
- mov eax, ARGUMENT_REG2 ; either null, or the successful instance
- ret
-
-@JIT_ChkCastInterface@8 endp
; Note that the debugger skips this entirely when doing SetIP,
; since COMPlusCheckForAbort should always return 0. Excep.cpp:LeaveCatch
diff --git a/src/coreclr/src/vm/i386/jitinterfacex86.cpp b/src/coreclr/src/vm/i386/jitinterfacex86.cpp
index 04474c856333c..3b97a6b05af88 100644
--- a/src/coreclr/src/vm/i386/jitinterfacex86.cpp
+++ b/src/coreclr/src/vm/i386/jitinterfacex86.cpp
@@ -195,138 +195,6 @@ __declspec(naked) void F_CALL_CONV JIT_Stelem_Ref(PtrArray* array, unsigned idx,
}
}
-extern "C" __declspec(naked) Object* F_CALL_CONV JIT_IsInstanceOfClass(MethodTable *pMT, Object *pObject)
-{
- STATIC_CONTRACT_THROWS;
- STATIC_CONTRACT_GC_TRIGGERS;
-
-#if defined(FEATURE_TYPEEQUIVALENCE)
- enum
- {
- MTEquivalenceFlags = MethodTable::public_enum_flag_HasTypeEquivalence,
- };
-#endif
-
- __asm
- {
- // Check if the instance is NULL
- test ARGUMENT_REG2, ARGUMENT_REG2
- je ReturnInst
-
- // Get the method table for the instance.
- mov eax, dword ptr [ARGUMENT_REG2]
-
- // Check if they are the same.
- cmp eax, ARGUMENT_REG1
- jne CheckParent
-
- ReturnInst:
- // We matched the class.
- mov eax, ARGUMENT_REG2
- ret
-
- // Check if the parent class matches.
- CheckParent:
- mov eax, dword ptr [eax]MethodTable.m_pParentMethodTable
- cmp eax, ARGUMENT_REG1
- je ReturnInst
-
- // Check if we hit the top of the hierarchy.
- test eax, eax
- jne CheckParent
-
- // Check if the instance is a proxy.
-#if defined(FEATURE_TYPEEQUIVALENCE)
- mov eax, [ARGUMENT_REG2]
- test dword ptr [eax]MethodTable.m_dwFlags, MTEquivalenceFlags
- jne SlowPath
-#endif
- // It didn't match and it isn't a proxy and it doesn't have type equivalence
- xor eax, eax
- ret
-
- // Cast didn't match, so try the worker to check for the proxy/equivalence case.
-#if defined(FEATURE_TYPEEQUIVALENCE)
- SlowPath:
- jmp JITutil_IsInstanceOfAny
-#endif
- }
-}
-
-extern "C" __declspec(naked) Object* F_CALL_CONV JIT_ChkCastClass(MethodTable *pMT, Object *pObject)
-{
- STATIC_CONTRACT_THROWS;
- STATIC_CONTRACT_GC_TRIGGERS;
-
- __asm
- {
- // Check if the instance is NULL
- test ARGUMENT_REG2, ARGUMENT_REG2
- je ReturnInst
-
- // Get the method table for the instance.
- mov eax, dword ptr [ARGUMENT_REG2]
-
- // Check if they are the same.
- cmp eax, ARGUMENT_REG1
- jne CheckParent
-
- ReturnInst:
- // We matched the class.
- mov eax, ARGUMENT_REG2
- ret
-
- // Check if the parent class matches.
- CheckParent:
- mov eax, dword ptr [eax]MethodTable.m_pParentMethodTable
- cmp eax, ARGUMENT_REG1
- je ReturnInst
-
- // Check if we hit the top of the hierarchy.
- test eax, eax
- jne CheckParent
-
- // Call out to JITutil_ChkCastAny to handle the proxy case and throw a rich
- // InvalidCastException in case of failure.
- jmp JITutil_ChkCastAny
- }
-}
-
-extern "C" __declspec(naked) Object* F_CALL_CONV JIT_ChkCastClassSpecial(MethodTable *pMT, Object *pObject)
-{
- STATIC_CONTRACT_THROWS;
- STATIC_CONTRACT_GC_TRIGGERS;
-
- // Assumes that the check for the trivial cases has been inlined by the JIT.
-
- __asm
- {
- // Get the method table for the instance.
- mov eax, dword ptr [ARGUMENT_REG2]
-
- // Check if the parent class matches.
- CheckParent:
- mov eax, dword ptr [eax]MethodTable.m_pParentMethodTable
- cmp eax, ARGUMENT_REG1
- jne CheckNull
-
- // We matched the class.
- mov eax, ARGUMENT_REG2
- ret
-
- CheckNull:
- // Check if we hit the top of the hierarchy.
- test eax, eax
- jne CheckParent
-
- // Call out to JITutil_ChkCastAny to handle the proxy case and throw a rich
- // InvalidCastException in case of failure.
- jmp JITutil_ChkCastAny
- }
-}
-#endif // FEATURE_PAL
-
-#ifndef FEATURE_PAL
HCIMPL1_V(INT32, JIT_Dbl2IntOvf, double val)
{
FCALL_CONTRACT;
diff --git a/src/coreclr/src/vm/jithelpers.cpp b/src/coreclr/src/vm/jithelpers.cpp
index 315c7ebbab5bd..ac95a0001b8d7 100644
--- a/src/coreclr/src/vm/jithelpers.cpp
+++ b/src/coreclr/src/vm/jithelpers.cpp
@@ -2188,365 +2188,7 @@ BOOL ObjIsInstanceOf(Object* pObject, TypeHandle toTypeHnd, BOOL throwCastExcept
return ObjIsInstanceOfCore(pObject, toTypeHnd, throwCastException);
}
-//
-// This optimization is intended for all non-framed casting helpers
-//
-
-#include
-
-HCIMPL2(Object*, JIT_ChkCastClass_Portable, MethodTable* pTargetMT, Object* pObject)
-{
- FCALL_CONTRACT;
-
- //
- // casts pObject to type pMT
- //
-
- if (NULL == pObject)
- {
- return NULL;
- }
-
- PTR_VOID pMT = pObject->GetMethodTable();
-
- do {
- if (pMT == pTargetMT)
- return pObject;
-
- pMT = MethodTable::GetParentMethodTableOrIndirection(pMT);
- } while (pMT);
-
- ENDFORBIDGC();
- return HCCALL2(JITutil_ChkCastAny, CORINFO_CLASS_HANDLE(pTargetMT), pObject);
-}
-HCIMPLEND
-
-//
-// This helper assumes that the check for the trivial cases has been inlined by the JIT.
-//
-HCIMPL2(Object*, JIT_ChkCastClassSpecial_Portable, MethodTable* pTargetMT, Object* pObject)
-{
- CONTRACTL {
- FCALL_CHECK;
- // This assumes that the check for the trivial cases has been inlined by the JIT.
- PRECONDITION(pObject != NULL);
- PRECONDITION(pObject->GetMethodTable() != pTargetMT);
- } CONTRACTL_END;
-
- PTR_VOID pMT = MethodTable::GetParentMethodTableOrIndirection(pObject->GetMethodTable());
-
- while (pMT)
- {
- if (pMT == pTargetMT)
- return pObject;
-
- pMT = MethodTable::GetParentMethodTableOrIndirection(pMT);
- }
-
- ENDFORBIDGC();
- return HCCALL2(JITutil_ChkCastAny, CORINFO_CLASS_HANDLE(pTargetMT), pObject);
-}
-HCIMPLEND
-
-HCIMPL2(Object*, JIT_IsInstanceOfClass_Portable, MethodTable* pTargetMT, Object* pObject)
-{
- FCALL_CONTRACT;
-
- //
- // casts pObject to type pMT
- //
-
- if (NULL == pObject)
- {
- return NULL;
- }
-
- PTR_VOID pMT = pObject->GetMethodTable();
-
- do {
- if (pMT == pTargetMT)
- return pObject;
-
- pMT = MethodTable::GetParentMethodTableOrIndirection(pMT);
- } while (pMT);
-
- if (!pObject->GetMethodTable()->HasTypeEquivalence())
- {
- return NULL;
- }
-
- ENDFORBIDGC();
- return HCCALL2(JITutil_IsInstanceOfAny, CORINFO_CLASS_HANDLE(pTargetMT), pObject);
-}
-HCIMPLEND
-
-HCIMPL2(Object*, JIT_ChkCastInterface_Portable, MethodTable *pInterfaceMT, Object* pObject)
-{
- CONTRACTL {
- FCALL_CHECK;
- PRECONDITION(pInterfaceMT->IsInterface());
- } CONTRACTL_END;
-
- if (NULL == pObject)
- {
- return pObject;
- }
-
- if (pObject->GetMethodTable()->ImplementsInterfaceInline(pInterfaceMT))
- {
- return pObject;
- }
-
- ENDFORBIDGC();
- return HCCALL2(JITutil_ChkCastInterface, pInterfaceMT, pObject);
-}
-HCIMPLEND
-
-HCIMPL2(Object*, JIT_IsInstanceOfInterface_Portable, MethodTable *pInterfaceMT, Object* pObject)
-{
- CONTRACTL {
- FCALL_CHECK;
- PRECONDITION(pInterfaceMT->IsInterface());
- } CONTRACTL_END;
-
- if (NULL == pObject)
- {
- return NULL;
- }
-
- if (pObject->GetMethodTable()->ImplementsInterfaceInline(pInterfaceMT))
- {
- return pObject;
- }
-
- if (!pObject->GetMethodTable()->InstanceRequiresNonTrivialInterfaceCast())
- {
- return NULL;
- }
-
- ENDFORBIDGC();
- return HCCALL2(JITutil_IsInstanceOfInterface, pInterfaceMT, pObject);
-}
-HCIMPLEND
-
-HCIMPL2(Object *, JIT_ChkCastArray, CORINFO_CLASS_HANDLE type, Object *pObject)
-{
- CONTRACTL {
- FCALL_CHECK;
- PRECONDITION(TypeHandle(type).IsArray());
- } CONTRACTL_END;
-
- if (pObject == NULL)
- {
- return NULL;
- }
-
- TypeHandle th = TypeHandle(type);
- TypeHandle::CastResult result = ObjIsInstanceOfCached(pObject, th);
- if (result == TypeHandle::CanCast)
- {
- return pObject;
- }
-
- ENDFORBIDGC();
- Object* pRet = HCCALL2(JITutil_ChkCastAny_NoCacheLookup, type, pObject);
- // Make sure that the fast helper have not lied
- _ASSERTE(result != TypeHandle::CannotCast);
- return pRet;
-}
-HCIMPLEND
-
-
-HCIMPL2(Object *, JIT_IsInstanceOfArray, CORINFO_CLASS_HANDLE type, Object *pObject)
-{
- CONTRACTL {
- FCALL_CHECK;
- PRECONDITION(TypeHandle(type).IsArray());
- } CONTRACTL_END;
-
- if (pObject == NULL)
- {
- return NULL;
- }
-
- OBJECTREF refObj = ObjectToOBJECTREF(pObject);
- VALIDATEOBJECTREF(refObj);
- MethodTable *pMT = refObj->GetMethodTable();
-
- if (!pMT->IsArray())
- {
- // We know that the clsHnd is an array so check the object. If it is not an array return null
- return NULL;
- }
- else
- {
- TypeHandle th = TypeHandle(type);
- TypeHandle::CastResult result = CastCache::TryGetFromCache(pMT, th);
-
- switch (result) {
- case TypeHandle::CanCast:
- return pObject;
- case TypeHandle::CannotCast:
- return NULL;
- default:
- // fall through to the slow helper
- break;
- }
- }
-
- ENDFORBIDGC();
- return HCCALL2(JITutil_IsInstanceOfAny_NoCacheLookup, type, pObject);
-}
-HCIMPLEND
-
-/*********************************************************************/
-// IsInstanceOf test used for unusual cases (naked type parameters, variant generic types)
-// Unlike the IsInstanceOfInterface, IsInstanceOfClass, and IsIsntanceofArray functions,
-// this test must deal with all kinds of type tests
-HCIMPL2(Object *, JIT_IsInstanceOfAny, CORINFO_CLASS_HANDLE type, Object* obj)
-{
- FCALL_CONTRACT;
-
- if (NULL == obj)
- {
- return NULL;
- }
-
- TypeHandle th = TypeHandle(type);
- switch (ObjIsInstanceOfCached(obj, th)) {
- case TypeHandle::CanCast:
- return obj;
- case TypeHandle::CannotCast:
- return NULL;
- default:
- // fall through to the slow helper
- break;
- }
-
- ENDFORBIDGC();
- return HCCALL2(JITutil_IsInstanceOfAny_NoCacheLookup, type, obj);
-}
-HCIMPLEND
-
-// ChkCast test used for unusual cases (naked type parameters, variant generic types)
-// Unlike the ChkCastInterface, ChkCastClass, and ChkCastArray functions,
-// this test must deal with all kinds of type tests
-HCIMPL2(Object *, JIT_ChkCastAny, CORINFO_CLASS_HANDLE type, Object *pObject)
-{
- FCALL_CONTRACT;
-
- if (NULL == pObject)
- {
- return NULL;
- }
-
- TypeHandle th = TypeHandle(type);
- TypeHandle::CastResult result = ObjIsInstanceOfCached(pObject, th);
- if (result == TypeHandle::CanCast)
- {
- return pObject;
- }
-
- ENDFORBIDGC();
- Object* pRet = HCCALL2(JITutil_ChkCastAny_NoCacheLookup, type, pObject);
- // Make sure that the fast helper have not lied
- _ASSERTE(result != TypeHandle::CannotCast);
- return pRet;
-}
-HCIMPLEND
-
-
-NOINLINE HCIMPL2(Object *, JITutil_IsInstanceOfInterface, MethodTable *pInterfaceMT, Object* obj)
-{
- FCALL_CONTRACT;
-
- MethodTable* pMT = obj->GetMethodTable();
- TypeHandle::CastResult result = CastCache::TryGetFromCache(pMT, pInterfaceMT);
-
- switch (result) {
- case TypeHandle::CanCast:
- return obj;
- case TypeHandle::CannotCast:
- return NULL;
- default:
- // fall through to the slow helper
- break;
- }
-
- ENDFORBIDGC();
- return HCCALL2(JITutil_IsInstanceOfAny_NoCacheLookup, CORINFO_CLASS_HANDLE(pInterfaceMT), obj);
-}
-HCIMPLEND
-
-NOINLINE HCIMPL2(Object *, JITutil_ChkCastInterface, MethodTable *pInterfaceMT, Object *obj)
-{
- FCALL_CONTRACT;
-
- MethodTable* pMT = obj->GetMethodTable();
- TypeHandle::CastResult result = CastCache::TryGetFromCache(pMT, pInterfaceMT);
-
- if (result == TypeHandle::CanCast)
- {
- return obj;
- }
-
- ENDFORBIDGC();
- return HCCALL2(JITutil_ChkCastAny_NoCacheLookup, CORINFO_CLASS_HANDLE(pInterfaceMT), obj);
-}
-HCIMPLEND
-
-
-#include
-
-
-//
-// Framed helpers
-//
-NOINLINE HCIMPL2(Object *, JITutil_ChkCastAny, CORINFO_CLASS_HANDLE type, Object *obj)
-{
- FCALL_CONTRACT;
-
- // This case should be handled by frameless helper
- _ASSERTE(obj != NULL);
-
- OBJECTREF oref = ObjectToOBJECTREF (obj);
- VALIDATEOBJECTREF(oref);
-
- TypeHandle clsHnd(type);
-
- HELPER_METHOD_FRAME_BEGIN_RET_1(oref);
- if (!ObjIsInstanceOf(OBJECTREFToObject(oref), clsHnd, TRUE))
- {
- UNREACHABLE(); //ObjIsInstanceOf will throw if cast can't be done
- }
- HELPER_METHOD_FRAME_END();
-
- return OBJECTREFToObject(oref);
-}
-HCIMPLEND
-
-NOINLINE HCIMPL2(Object *, JITutil_IsInstanceOfAny, CORINFO_CLASS_HANDLE type, Object *obj)
-{
- FCALL_CONTRACT;
-
- // This case should be handled by frameless helper
- _ASSERTE(obj != NULL);
-
- OBJECTREF oref = ObjectToOBJECTREF (obj);
- VALIDATEOBJECTREF(oref);
-
- TypeHandle clsHnd(type);
-
- HELPER_METHOD_FRAME_BEGIN_RET_1(oref);
- if (!ObjIsInstanceOf(OBJECTREFToObject(oref), clsHnd))
- oref = NULL;
- HELPER_METHOD_FRAME_END();
-
- return OBJECTREFToObject(oref);
-}
-HCIMPLEND
-
-NOINLINE HCIMPL2(Object*, JITutil_ChkCastAny_NoCacheLookup, CORINFO_CLASS_HANDLE type, Object* obj)
+HCIMPL2(Object*, ChkCastAny_NoCacheLookup, CORINFO_CLASS_HANDLE type, Object* obj)
{
FCALL_CONTRACT;
@@ -2563,13 +2205,14 @@ NOINLINE HCIMPL2(Object*, JITutil_ChkCastAny_NoCacheLookup, CORINFO_CLASS_HANDLE
{
UNREACHABLE(); //ObjIsInstanceOf will throw if cast can't be done
}
+ HELPER_METHOD_POLL();
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(oref);
}
HCIMPLEND
-NOINLINE HCIMPL2(Object*, JITutil_IsInstanceOfAny_NoCacheLookup, CORINFO_CLASS_HANDLE type, Object* obj)
+HCIMPL2(Object*, IsInstanceOfAny_NoCacheLookup, CORINFO_CLASS_HANDLE type, Object* obj)
{
FCALL_CONTRACT;
@@ -2584,6 +2227,7 @@ NOINLINE HCIMPL2(Object*, JITutil_IsInstanceOfAny_NoCacheLookup, CORINFO_CLASS_H
HELPER_METHOD_FRAME_BEGIN_RET_1(oref);
if (!ObjIsInstanceOfCore(OBJECTREFToObject(oref), clsHnd))
oref = NULL;
+ HELPER_METHOD_POLL();
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(oref);
diff --git a/src/coreclr/src/vm/jitinterface.h b/src/coreclr/src/vm/jitinterface.h
index 6eab4785a67b3..4a7002f6c0dfe 100644
--- a/src/coreclr/src/vm/jitinterface.h
+++ b/src/coreclr/src/vm/jitinterface.h
@@ -187,36 +187,6 @@ EXTERN_C FCDECL1(void*, JIT_GetSharedGCStaticBaseNoCtor_Portable, DomainLocalMod
EXTERN_C FCDECL1(void*, JIT_GetSharedNonGCStaticBaseNoCtor, DomainLocalModule *pLocalModule);
EXTERN_C FCDECL1(void*, JIT_GetSharedNonGCStaticBaseNoCtor_Portable, DomainLocalModule *pLocalModule);
-#ifndef JIT_ChkCastClass
-#define JIT_ChkCastClass JIT_ChkCastClass_Portable
-#endif
-EXTERN_C FCDECL2(Object*, JIT_ChkCastClass, MethodTable* pMT, Object* pObject);
-EXTERN_C FCDECL2(Object*, JIT_ChkCastClass_Portable, MethodTable* pMT, Object* pObject);
-
-#ifndef JIT_ChkCastClassSpecial
-#define JIT_ChkCastClassSpecial JIT_ChkCastClassSpecial_Portable
-#endif
-EXTERN_C FCDECL2(Object*, JIT_ChkCastClassSpecial, MethodTable* pMT, Object* pObject);
-EXTERN_C FCDECL2(Object*, JIT_ChkCastClassSpecial_Portable, MethodTable* pMT, Object* pObject);
-
-#ifndef JIT_IsInstanceOfClass
-#define JIT_IsInstanceOfClass JIT_IsInstanceOfClass_Portable
-#endif
-EXTERN_C FCDECL2(Object*, JIT_IsInstanceOfClass, MethodTable* pMT, Object* pObject);
-EXTERN_C FCDECL2(Object*, JIT_IsInstanceOfClass_Portable, MethodTable* pMT, Object* pObject);
-
-#ifndef JIT_ChkCastInterface
-#define JIT_ChkCastInterface JIT_ChkCastInterface_Portable
-#endif
-EXTERN_C FCDECL2(Object*, JIT_ChkCastInterface, MethodTable* pMT, Object* pObject);
-EXTERN_C FCDECL2(Object*, JIT_ChkCastInterface_Portable, MethodTable* pMT, Object* pObject);
-
-#ifndef JIT_IsInstanceOfInterface
-#define JIT_IsInstanceOfInterface JIT_IsInstanceOfInterface_Portable
-#endif
-EXTERN_C FCDECL2(Object*, JIT_IsInstanceOfInterface, MethodTable* pMT, Object* pObject);
-EXTERN_C FCDECL2(Object*, JIT_IsInstanceOfInterface_Portable, MethodTable* pMT, Object* pObject);
-
extern FCDECL1(Object*, JIT_NewS_MP_FastPortable, CORINFO_CLASS_HANDLE typeHnd_);
extern FCDECL1(Object*, JIT_New, CORINFO_CLASS_HANDLE typeHnd_);
@@ -272,18 +242,10 @@ extern "C" FCDECL2(VOID, JIT_CheckedWriteBarrier, Object **dst, Object *ref);
#endif
extern "C" FCDECL2(VOID, JIT_WriteBarrier, Object **dst, Object *ref);
-
extern "C" FCDECL2(VOID, JIT_WriteBarrierEnsureNonHeapTarget, Object **dst, Object *ref);
-extern "C" FCDECL2(Object*, JIT_ChkCastAny, CORINFO_CLASS_HANDLE type, Object *pObject); // JITInterfaceX86.cpp, etc.
-extern "C" FCDECL2(Object*, JIT_IsInstanceOfAny, CORINFO_CLASS_HANDLE type, Object *pObject);
-
-extern "C" FCDECL2(Object*, JITutil_ChkCastInterface, MethodTable *pInterfaceMT, Object *obj);
-extern "C" FCDECL2(Object*, JITutil_IsInstanceOfInterface, MethodTable *pInterfaceMT, Object *obj);
-extern "C" FCDECL2(Object*, JITutil_ChkCastAny, CORINFO_CLASS_HANDLE type, Object *obj);
-extern "C" FCDECL2(Object*, JITutil_IsInstanceOfAny, CORINFO_CLASS_HANDLE type, Object *obj);
-extern "C" FCDECL2(Object*, JITutil_ChkCastAny_NoCacheLookup, CORINFO_CLASS_HANDLE type, Object* obj);
-extern "C" FCDECL2(Object*, JITutil_IsInstanceOfAny_NoCacheLookup, CORINFO_CLASS_HANDLE type, Object* obj);
+extern "C" FCDECL2(Object*, ChkCastAny_NoCacheLookup, CORINFO_CLASS_HANDLE type, Object* obj);
+extern "C" FCDECL2(Object*, IsInstanceOfAny_NoCacheLookup, CORINFO_CLASS_HANDLE type, Object* obj);
extern "C" FCDECL1(void, JIT_InternalThrow, unsigned exceptNum);
extern "C" FCDECL1(void*, JIT_InternalThrowFromHelper, unsigned exceptNum);
diff --git a/src/coreclr/src/vm/metasig.h b/src/coreclr/src/vm/metasig.h
index a1e8c7d817238..e4b2be2dd5f20 100644
--- a/src/coreclr/src/vm/metasig.h
+++ b/src/coreclr/src/vm/metasig.h
@@ -243,6 +243,7 @@ DEFINE_METASIG(SM(IntPtr_Int_IntPtr_Int_Int_Int_RetVoid, I i I i i i, v))
DEFINE_METASIG(SM(IntPtr_IntPtr_Int_Int_IntPtr_RetVoid, I I i i I, v))
DEFINE_METASIG(SM(IntPtr_RefObj_IntPtr_Obj_RetVoid, I r(j) I j, v))
DEFINE_METASIG(SM(Obj_Int_RetVoid, j i,v))
+DEFINE_METASIG(SM(PtrVoid_Obj_RetObj, P(v) j, j))
DEFINE_METASIG(SM(Flt_RetFlt, f, f))
DEFINE_METASIG(SM(Dbl_RetDbl, d, d))
diff --git a/src/coreclr/src/vm/methodtable.h b/src/coreclr/src/vm/methodtable.h
index 4910326e689ab..434d4db9a6e83 100644
--- a/src/coreclr/src/vm/methodtable.h
+++ b/src/coreclr/src/vm/methodtable.h
@@ -1949,17 +1949,6 @@ class MethodTable
bool NativeRequiresAlign8();
#endif // FEATURE_64BIT_ALIGNMENT
- // True if interface casts for an object having this type require more
- // than a simple scan of the interface map
- // See JIT_IsInstanceOfInterface
- inline BOOL InstanceRequiresNonTrivialInterfaceCast()
- {
- LIMITED_METHOD_CONTRACT;
-
- return GetFlag(enum_flag_NonTrivialInterfaceCast);
- }
-
-
//-------------------------------------------------------------------
// PARENT INTERFACES
//
@@ -3839,14 +3828,6 @@ public :
return (m_wFlags2 & (DWORD)mask) == (DWORD)flag;
}
- // Just exposing a couple of these for x86 asm versions of JIT_IsInstanceOfClass and JIT_IsInstanceOfInterface
-public:
- enum
- {
- public_enum_flag_HasTypeEquivalence = enum_flag_HasTypeEquivalence,
- public_enum_flag_NonTrivialInterfaceCast = enum_flag_NonTrivialInterfaceCast,
- };
-
private:
/*
* This stuff must be first in the struct and should fit on a cache line - don't move it. Used by the GC.
diff --git a/src/coreclr/src/vm/mscorlib.h b/src/coreclr/src/vm/mscorlib.h
index e3b3102ef2776..d9bb8c92f665c 100644
--- a/src/coreclr/src/vm/mscorlib.h
+++ b/src/coreclr/src/vm/mscorlib.h
@@ -1452,6 +1452,16 @@ DEFINE_FIELD_U(_deletedCount, GCHeapHashObject, _del
DEFINE_CLASS(GCHEAPHASH, CompilerServices, GCHeapHash)
+DEFINE_CLASS(CASTHELPERS, CompilerServices, CastHelpers)
+DEFINE_FIELD(CASTHELPERS, TABLE, s_table)
+DEFINE_METHOD(CASTHELPERS, ISINSTANCEOFANY, IsInstanceOfAny, SM_PtrVoid_Obj_RetObj)
+DEFINE_METHOD(CASTHELPERS, ISINSTANCEOFCLASS,IsInstanceOfClass, SM_PtrVoid_Obj_RetObj)
+DEFINE_METHOD(CASTHELPERS, ISINSTANCEOFINTERFACE, IsInstanceOfInterface, SM_PtrVoid_Obj_RetObj)
+DEFINE_METHOD(CASTHELPERS, CHKCASTANY, ChkCastAny, SM_PtrVoid_Obj_RetObj)
+DEFINE_METHOD(CASTHELPERS, CHKCASTINTERFACE, ChkCastInterface, SM_PtrVoid_Obj_RetObj)
+DEFINE_METHOD(CASTHELPERS, CHKCASTCLASS, ChkCastClass, SM_PtrVoid_Obj_RetObj)
+DEFINE_METHOD(CASTHELPERS, CHKCASTCLASSSPECIAL, ChkCastClassSpecial, SM_PtrVoid_Obj_RetObj)
+
DEFINE_CLASS_U(CompilerServices, LAHashDependentHashTracker, LAHashDependentHashTrackerObject)
DEFINE_FIELD_U(_dependentHandle, LAHashDependentHashTrackerObject,_dependentHandle)
DEFINE_FIELD_U(_loaderAllocator, LAHashDependentHashTrackerObject,_loaderAllocator)