未验证 提交 c3dc1fdf 编写于 作者: V Vladimir Sadov 提交者: GitHub

Moving fast parts of cast FCALLs to managed code. (#1068)

* Exposed casting cache to managed code
* Implemented a managed version of cache lookup
* Moved `JIT_IsInstanceOfAny` and `JIT_ChkCastAny`  to managed code as the first ones to move.
* Skip managed JIT helpers in exception stack traces and debugger
* Managed `JIT_IsInstanceOfInterface`
* All other cast helpers are managed.

Fixes:https://github.com/dotnet/coreclr/issues/27931
上级 375a15ba
......@@ -241,6 +241,7 @@
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\CrossLoaderAllocatorHashHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\DependentHandle.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\GCHeapHash.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\CastHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\ICastableHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\QCallHandles.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeFeature.CoreCLR.cs" />
......@@ -467,5 +468,5 @@
<ItemGroup>
<EmbeddedFiles Include="@(GeneratedResxSource)" />
</ItemGroup>
</Target>
</Target>
</Project>
// 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, <half-size>) ^ 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<int, CastCacheEntry>(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);
}
}
}
......@@ -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)]
......
......@@ -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)
......
......@@ -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).
......
......@@ -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__
......@@ -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
......
......@@ -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.
......
......@@ -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 @@ private:
// 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 @@ private:
// 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 @@ private:
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)
......
......@@ -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)
......
......@@ -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.
......
......@@ -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();
......
......@@ -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)
......
......@@ -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)
......
......@@ -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
......
......@@ -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
......
......@@ -195,138 +195,6 @@ Epilog:
}
}
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;
......
......@@ -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 <optsmallperfcritical.h>
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 <optdefault.h>
//
// 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);
......
......@@ -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);
......
......@@ -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))
......
......@@ -1949,17 +1949,6 @@ public:
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 @@ private:
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.
......
......@@ -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)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册