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)