Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move unboxing helpers to managed code #109135

Merged
merged 19 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c70a57c
Move unboxing helpers to managed code
davidwrighton Oct 23, 2024
f1258a4
Add boxinghelpers.cs
davidwrighton Oct 23, 2024
02636f8
Fix issues noted in CI/Review
davidwrighton Oct 24, 2024
d8e01e3
Fix more issues found in CI
davidwrighton Oct 24, 2024
b2c6991
Fix issue where UnboxNoCheck writes too many bytes when unboxing a tr…
davidwrighton Oct 25, 2024
12d17e5
Move unboxing helpers to CastHelpers class
davidwrighton Oct 28, 2024
3f281b9
Before conversion to ref byte everywhere
davidwrighton Oct 30, 2024
8dfaedc
It should all work now, and be fast
davidwrighton Oct 30, 2024
be624da
Cleanup dead code
davidwrighton Oct 30, 2024
dec085f
Update src/coreclr/System.Private.CoreLib/src/System/Runtime/Compiler…
davidwrighton Oct 30, 2024
e22038b
Merge branch 'main' of https://github.com/dotnet/runtime into Unboxin…
davidwrighton Oct 30, 2024
e44fab4
Fix build break
davidwrighton Oct 30, 2024
2964105
And finish up the details around optimizing for the fastest case in U…
davidwrighton Oct 30, 2024
6469fbb
Merge branch 'UnboxingHelpers' of https://github.com/davidwrighton/ru…
davidwrighton Oct 30, 2024
1e51dd2
- Remove unused helper functions InitValueClass and InitValueClassPtr
davidwrighton Oct 31, 2024
ee6d42d
Performance seeking behavior has finished. The perf is amazing
davidwrighton Nov 13, 2024
7761ad8
Create GetNumInstanceFieldBytesIfContainsGCPointers helper and use it…
davidwrighton Nov 13, 2024
55e0df9
Last minute changes
davidwrighton Nov 13, 2024
6a8d1ae
Update wording
davidwrighton Nov 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourc

if (pDestMT->IsNullable)
{
RuntimeHelpers.Unbox_Nullable(ref dest, pDestMT, obj);
CastHelpers.Unbox_Nullable(ref dest, pDestMT, obj);
}
else if (obj is null || RuntimeHelpers.GetMethodTable(obj) != pDestMT)
{
Expand Down Expand Up @@ -546,7 +546,7 @@ private unsafe void InternalSetValue(object? value, nint flattenedIndex)
{
if (pElementMethodTable->IsNullable)
{
RuntimeHelpers.Unbox_Nullable(ref offsetDataRef, pElementMethodTable, value);
CastHelpers.Unbox_Nullable(ref offsetDataRef, pElementMethodTable, value);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ internal static unsafe bool InternalEqualTypes(object a, object b)
{
if (a.GetType() == b.GetType())
return true;

#if FEATURE_TYPEEQUIVALENCE
MethodTable* pMTa = RuntimeHelpers.GetMethodTable(a);
MethodTable* pMTb = RuntimeHelpers.GetMethodTable(b);

Expand All @@ -474,6 +474,9 @@ internal static unsafe bool InternalEqualTypes(object a, object b)
GC.KeepAlive(b);

return ret;
#else
return false;
#endif // FEATURE_TYPEEQUIVALENCE
}

// Used by the ctor. Do not call directly.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,39 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;

namespace System.Runtime.CompilerServices
{
[StackTraceHidden]
[DebuggerStepThrough]
internal static unsafe class CastHelpers
internal static unsafe partial class CastHelpers
{
// In coreclr the table is allocated and written to on the native side.
internal static int[]? s_table;

[LibraryImport(RuntimeHelpers.QCall)]
internal static partial void ThrowInvalidCastException(void* fromTypeHnd, void* toTypeHnd);

[DoesNotReturn]
internal static void ThrowInvalidCastException(object fromType, void* toTypeHnd)
{
ThrowInvalidCastException(RuntimeHelpers.GetMethodTable(fromType), toTypeHnd);
throw null!; // Provide hint to the inliner that this method does not return
}

[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);

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern ref byte Unbox_Helper(void* toTypeHnd, object obj);
private static extern void WriteBarrier(ref object? dst, object? obj);

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void WriteBarrier(ref object? dst, object? obj);
private static extern void UnboxNullableValue(ref byte destPtr, MethodTable* typeMT, object obj);

// IsInstanceOf test used for unusual cases (naked type parameters, variant generic types)
// Unlike the IsInstanceOfInterface and IsInstanceOfClass functions,
Expand Down Expand Up @@ -365,7 +376,7 @@ internal static unsafe class CastHelpers
}

[DebuggerHidden]
private static ref byte Unbox(void* toTypeHnd, object obj)
private static ref byte Unbox(MethodTable* toTypeHnd, object obj)
{
// This will throw NullReferenceException if obj is null.
if (RuntimeHelpers.GetMethodTable(obj) == toTypeHnd)
Expand Down Expand Up @@ -492,5 +503,141 @@ private static unsafe void ArrayTypeCheck_Helper(object obj, void* elementType)
ThrowArrayMismatchException();
}
}

// Helpers for Unboxing
#if FEATURE_TYPEEQUIVALENCE
[DebuggerHidden]
[MethodImpl(MethodImplOptions.NoInlining)]
private static bool AreTypesEquivalent(MethodTable* pMTa, MethodTable* pMTb)
{
if (pMTa == pMTb)
{
return true;
}

if (!pMTa->HasTypeEquivalence || !pMTb->HasTypeEquivalence)
{
return false;
}

return RuntimeHelpers.AreTypesEquivalent(pMTa, pMTb);
}
#endif // FEATURE_TYPEEQUIVALENCE

[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsNullableForType(MethodTable* typeMT, MethodTable* boxedMT)
{
if (!typeMT->IsNullable)
{
return false;
}

// Normally getting the first generic argument involves checking the PerInstInfo to get the count of generic dictionaries
// in the hierarchy, and then doing a bit of math to find the right dictionary, but since we know this is nullable
// we can do a simple double deference to do the same thing.
Debug.Assert(typeMT->InstantiationArg0() == **typeMT->PerInstInfo);
MethodTable *pMTNullableArg = **typeMT->PerInstInfo;
if (pMTNullableArg == boxedMT)
{
return true;
}
else
{
#if FEATURE_TYPEEQUIVALENCE
return AreTypesEquivalent(pMTNullableArg, boxedMT);
#else
return false;
#endif // FEATURE_TYPEEQUIVALENCE
}
}

[DebuggerHidden]
[MethodImpl(MethodImplOptions.NoInlining)]
private static void Unbox_Nullable_NotIsNullableForType(ref byte destPtr, MethodTable* typeMT, object obj)
{
// For safety's sake, also allow true nullables to be unboxed normally.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I know that this is pre-existing comment.) This is not for safety's sake. We would not give up any safety if we threw for boxed Nullable<T> that should not exist here. It is just to hide bugs. Have you tried deleting this to see whether anything fails?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some cases in func eval which claim to use this pathway. In addition, it IS a pathway that has been used in reflection in the past, so I don't want to remove this path.

// This should not happen normally, but we want to be robust
if (typeMT != RuntimeHelpers.GetMethodTable(obj))
{
CastHelpers.ThrowInvalidCastException(obj, typeMT);
}
Buffer.BulkMoveWithWriteBarrier(ref destPtr, ref RuntimeHelpers.GetRawData(obj), typeMT->GetNumInstanceFieldBytes());
}

[DebuggerHidden]
internal static void Unbox_Nullable(ref byte destPtr, MethodTable* typeMT, object? obj)
{
if (obj == null)
{
if (!typeMT->ContainsGCPointers)
{
SpanHelpers.ClearWithoutReferences(ref destPtr, typeMT->GetNumInstanceFieldBytes());
}
else
{
// If the type ContainsGCPointers, we can compute the size without resorting to loading the BaseSizePadding field from the EEClass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// If the type ContainsGCPointers, we can compute the size without resorting to loading the BaseSizePadding field from the EEClass

nuint numInstanceFieldBytes = typeMT->BaseSize - (nuint)(2 * sizeof(IntPtr));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

numInstanceFieldBytes local var is unusued. Is this unfinished refactoring?

It may be nice to move this to a property or method on MethodTable. There are more places where this micro-optimization can be used. The property impl can assert that this is only used on when ContainsGCPointers is true and that it returns the same value as full GetNumInstanceFieldBytes.

// Otherwise, use the helper which is safe for that situation
SpanHelpers.ClearWithReferences(ref Unsafe.As<byte, IntPtr>(ref destPtr), (typeMT->BaseSize - (nuint)(2 * sizeof(IntPtr))) / (nuint)sizeof(IntPtr));
}
}
else
{
if (!IsNullableForType(typeMT, RuntimeHelpers.GetMethodTable(obj)))
{
Unbox_Nullable_NotIsNullableForType(ref destPtr, typeMT, obj);
}
else
{
UnboxNullableValue(ref destPtr, typeMT, obj);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason why we are not just calling BulkMoveWithWriteBarrier/Memmove here to copy the value? I would expect it to be faster.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It turns out that there is a fair amount of type system structure that would need to be ported to managed code to call BulkMoveWithWriteBarrier. I made the decision to pull this out into its own FCALL here. Especially since I expect that most paths will need to do a BulkMoveWithWriteBarrier which isn't that different in perf. I can explore pulling enough type system structure for this if you'd like me to.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course, now that I look at this, I realized that I can actually encode the needed bits for this particular load into the space used by m_pInterfaceMap which would allow all of this to avoid a couple of memory loads, branches etc. All of that should allow me to optimize the rest of all of this logic.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to make sure that I understand - is the main missing piece Nullable::ValueAddr?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its ValueAddr + the computation of the size of the instance field bytes of the Value field. That requires access to the EEClass + access to a fielddesc, etc. It boils down to not that many instructions, but it IS a fair number of concepts and type system structures. That is a task probably worth doing at some point, but I'd like to avoid doing it now. The new encoding of data, which is redundant with existing data, allows for extremely cheap access, and is simple to encode into the managed code without adding more concepts than MethodTable.

}
}
}

[DebuggerHidden]
[MethodImpl(MethodImplOptions.NoInlining)]
internal static ref byte Unbox_Helper(MethodTable* pMT1, object obj)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
internal static ref byte Unbox_Helper(MethodTable* pMT1, object obj)
private static ref byte Unbox_Helper(MethodTable* pMT1, object obj)

{
// must be a value type
Debug.Assert(pMT1->IsValueType);

MethodTable* pMT2 = RuntimeHelpers.GetMethodTable(obj);
if ((!pMT1->IsPrimitive || !pMT2->IsPrimitive ||
pMT1->GetPrimitiveCorElementType() != pMT2->GetPrimitiveCorElementType())
#if FEATURE_TYPEEQUIVALENCE
&& !AreTypesEquivalent(pMT1, pMT2)
#endif // FEATURE_TYPEEQUIVALENCE
)
{
CastHelpers.ThrowInvalidCastException(obj, pMT1);
}

return ref RuntimeHelpers.GetRawData(obj);
}

[DebuggerHidden]
[MethodImpl(MethodImplOptions.NoInlining)]
internal static void Unbox_TypeTest_Helper(MethodTable *pMT1, MethodTable *pMT2)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
internal static void Unbox_TypeTest_Helper(MethodTable *pMT1, MethodTable *pMT2)
private static void Unbox_TypeTest_Helper(MethodTable *pMT1, MethodTable *pMT2)

{
if ((!pMT1->IsPrimitive || !pMT2->IsPrimitive ||
pMT1->GetPrimitiveCorElementType() != pMT2->GetPrimitiveCorElementType())
#if FEATURE_TYPEEQUIVALENCE
&& !AreTypesEquivalent(pMT1, pMT2)
#endif // FEATURE_TYPEEQUIVALENCE
)
{
CastHelpers.ThrowInvalidCastException(pMT1, pMT2);
}
}

[DebuggerHidden]
internal static void Unbox_TypeTest(MethodTable *pMT1, MethodTable *pMT2)
{
if (pMT1 == pMT2)
return;
else
Unbox_TypeTest_Helper(pMT1, pMT2);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -448,9 +448,6 @@ internal static unsafe bool ObjectHasComponentSize(object obj)
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern unsafe object? Box(MethodTable* methodTable, ref byte data);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern unsafe void Unbox_Nullable(ref byte destination, MethodTable* toTypeHnd, object? obj);

// Given an object reference, returns its MethodTable*.
//
// WARNING: The caller has to ensure that MethodTable* does not get unloaded. The most robust way
Expand Down Expand Up @@ -706,6 +703,16 @@ internal unsafe struct MethodTable
[FieldOffset(ElementTypeOffset)]
public void* ElementType;

/// <summary>
/// The PerInstInfo is used to describe the generic arguments and dictionary of this type.
/// It points as a PerInstInfo, which is an array of pointers to generic dictionaries, which then point
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It points as a PerInstInfo

This does not parse for me.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the wording. Hopefully it is a bit more clear although for sure, the actual design of this is a very confusing structure.

/// to the actual type arguments + the contents of the generic dictionary. The size of the PerInstInfo is
/// defined in the negative space of that structure, and the size of the generic dictionary is described
/// in the DictionaryLayout of the associated canonical MethodTable.
/// </summary>
[FieldOffset(ElementTypeOffset)]
public MethodTable*** PerInstInfo;

/// <summary>
/// This interface map used to list out the set of interfaces. Only meaningful if InterfaceCount is non-zero.
/// </summary>
Expand All @@ -730,6 +737,7 @@ internal unsafe struct MethodTable
private const uint enum_flag_Category_Mask = 0x000F0000;
private const uint enum_flag_Category_ValueType = 0x00040000;
private const uint enum_flag_Category_Nullable = 0x00050000;
private const uint enum_flag_Category_IsPrimitiveMask = 0x000E0000;
private const uint enum_flag_Category_PrimitiveValueType = 0x00060000; // sub-category of ValueType, Enum or primitive value type
private const uint enum_flag_Category_TruePrimitive = 0x00070000; // sub-category of ValueType, Primitive (ELEMENT_TYPE_I, etc.)
private const uint enum_flag_Category_Array = 0x00080000;
Expand Down Expand Up @@ -780,7 +788,9 @@ internal unsafe struct MethodTable

public bool NonTrivialInterfaceCast => (Flags & enum_flag_NonTrivialInterfaceCast) != 0;

#if FEATURE_TYPEEQUIVALENCE
public bool HasTypeEquivalence => (Flags & enum_flag_HasTypeEquivalence) != 0;
#endif // FEATURE_TYPEEQUIVALENCE

public bool HasFinalizer => (Flags & enum_flag_HasFinalizer) != 0;

Expand Down Expand Up @@ -820,7 +830,7 @@ public int MultiDimensionalArrayRank
public bool IsByRefLike => (Flags & (enum_flag_HasComponentSize | enum_flag_IsByRefLike)) == enum_flag_IsByRefLike;

// Warning! UNLIKE the similarly named Reflection api, this method also returns "true" for Enums.
public bool IsPrimitive => (Flags & enum_flag_Category_Mask) is enum_flag_Category_PrimitiveValueType or enum_flag_Category_TruePrimitive;
public bool IsPrimitive => (Flags & enum_flag_Category_IsPrimitiveMask) == enum_flag_Category_PrimitiveValueType;

public bool IsTruePrimitive => (Flags & enum_flag_Category_Mask) is enum_flag_Category_TruePrimitive;

Expand Down Expand Up @@ -877,6 +887,9 @@ public TypeHandle GetArrayElementTypeHandle()
/// </summary>
[MethodImpl(MethodImplOptions.InternalCall)]
public extern MethodTable* GetMethodTableMatchingParentClass(MethodTable* parent);

[MethodImpl(MethodImplOptions.InternalCall)]
public extern MethodTable* InstantiationArg0();
}

[StructLayout(LayoutKind.Sequential)]
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/inc/jithelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@
DYNAMICJITHELPER(CORINFO_HELP_BOX, JIT_Box, METHOD__NIL)
JITHELPER(CORINFO_HELP_BOX_NULLABLE, JIT_Box, METHOD__NIL)
DYNAMICJITHELPER(CORINFO_HELP_UNBOX, NULL, METHOD__CASTHELPERS__UNBOX)
JITHELPER(CORINFO_HELP_UNBOX_TYPETEST, JIT_Unbox_TypeTest, METHOD__NIL)
JITHELPER(CORINFO_HELP_UNBOX_NULLABLE, JIT_Unbox_Nullable, METHOD__NIL)
DYNAMICJITHELPER(CORINFO_HELP_UNBOX_TYPETEST,NULL, METHOD__CASTHELPERS__UNBOX_TYPETEST)
DYNAMICJITHELPER(CORINFO_HELP_UNBOX_NULLABLE,NULL, METHOD__CASTHELPERS__UNBOX_NULLABLE)

JITHELPER(CORINFO_HELP_GETREFANY, JIT_GetRefAny, METHOD__NIL)
DYNAMICJITHELPER(CORINFO_HELP_ARRADDR_ST, NULL, METHOD__CASTHELPERS__STELEMREF)
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/JitQCallHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class MethodDesc;
extern "C" void * QCALLTYPE ResolveVirtualFunctionPointer(QCall::ObjectHandleOnStack obj, CORINFO_CLASS_HANDLE classHnd, CORINFO_METHOD_HANDLE methodHnd);
extern "C" CORINFO_GENERIC_HANDLE QCALLTYPE GenericHandleWorker(MethodDesc * pMD, MethodTable * pMT, LPVOID signature, DWORD dictionaryIndexAndSlot, Module* pModule);
extern "C" void QCALLTYPE InitClassHelper(MethodTable* pMT);
extern "C" void QCALLTYPE ThrowInvalidCastException(CORINFO_CLASS_HANDLE pTargetType, CORINFO_CLASS_HANDLE pSourceType);
extern "C" void QCALLTYPE GetThreadStaticsByMethodTable(QCall::ByteRefOnStack refHandle, MethodTable* pMT, bool gcStatic);
extern "C" void QCALLTYPE GetThreadStaticsByIndex(QCall::ByteRefOnStack refHandle, uint32_t staticBlockIndex, bool gcStatic);

Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/vm/comutilnative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1818,6 +1818,14 @@ FCIMPL2(MethodTable*, MethodTableNative::GetMethodTableMatchingParentClass, Meth
}
FCIMPLEND

FCIMPL1(MethodTable*, MethodTableNative::InstantiationArg0, MethodTable* mt);
{
FCALL_CONTRACT;

return mt->GetInstantiation()[0].AsMethodTable();
}
FCIMPLEND

extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, MethodTable* mtb)
{
QCALL_CONTRACT;
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/comutilnative.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ class MethodTableNative {
static FCDECL1(UINT32, GetNumInstanceFieldBytes, MethodTable* mt);
static FCDECL1(CorElementType, GetPrimitiveCorElementType, MethodTable* mt);
static FCDECL2(MethodTable*, GetMethodTableMatchingParentClass, MethodTable* mt, MethodTable* parent);
static FCDECL1(MethodTable*, InstantiationArg0, MethodTable* mt);
};

extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, MethodTable* mtb);
Expand Down
4 changes: 3 additions & 1 deletion src/coreclr/vm/corelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -1169,10 +1169,12 @@ DEFINE_METHOD(CASTHELPERS, CHKCASTANY, ChkCastAny, SM_Ptr
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_METHOD(CASTHELPERS, UNBOX, Unbox, SM_PtrVoid_Obj_RetRefByte)
DEFINE_METHOD(CASTHELPERS, UNBOX, Unbox, NoSig)
DEFINE_METHOD(CASTHELPERS, STELEMREF, StelemRef, SM_ArrObject_IntPtr_Obj_RetVoid)
DEFINE_METHOD(CASTHELPERS, LDELEMAREF, LdelemaRef, SM_ArrObject_IntPtr_PtrVoid_RetRefObj)
DEFINE_METHOD(CASTHELPERS, ARRAYTYPECHECK, ArrayTypeCheck, SM_Obj_Array_RetVoid)
DEFINE_METHOD(CASTHELPERS, UNBOX_NULLABLE, Unbox_Nullable, NoSig)
DEFINE_METHOD(CASTHELPERS, UNBOX_TYPETEST, Unbox_TypeTest, NoSig)

DEFINE_CLASS(VIRTUALDISPATCHHELPERS, CompilerServices, VirtualDispatchHelpers)
DEFINE_METHOD(VIRTUALDISPATCHHELPERS, VIRTUALFUNCTIONPOINTER, VirtualFunctionPointer, NoSig)
Expand Down
5 changes: 2 additions & 3 deletions src/coreclr/vm/ecalllist.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,8 @@ FCFuncEnd()
FCFuncStart(gCastHelpers)
FCFuncElement("IsInstanceOfAny_NoCacheLookup", ::IsInstanceOfAny_NoCacheLookup)
FCFuncElement("ChkCastAny_NoCacheLookup", ::ChkCastAny_NoCacheLookup)
FCFuncElement("Unbox_Helper", ::Unbox_Helper)
FCFuncElement("JIT_Unbox_TypeTest", ::JIT_Unbox_TypeTest)
FCFuncElement("WriteBarrier", ::WriteBarrier_Helper)
FCFuncElement("UnboxNullableValue", ::UnboxNullableValue)
FCFuncEnd()

FCFuncStart(gArrayFuncs)
Expand Down Expand Up @@ -356,13 +355,13 @@ FCFuncStart(gRuntimeHelpers)
FCFuncElement("AllocTailCallArgBufferWorker", TailCallHelp::AllocTailCallArgBufferWorker)
FCFuncElement("GetTailCallInfo", TailCallHelp::GetTailCallInfo)
FCFuncElement("Box", JIT_Box)
FCFuncElement("Unbox_Nullable", JIT_Unbox_Nullable)
FCFuncEnd()

FCFuncStart(gMethodTableFuncs)
FCFuncElement("GetNumInstanceFieldBytes", MethodTableNative::GetNumInstanceFieldBytes)
FCFuncElement("GetPrimitiveCorElementType", MethodTableNative::GetPrimitiveCorElementType)
FCFuncElement("GetMethodTableMatchingParentClass", MethodTableNative::GetMethodTableMatchingParentClass)
FCFuncElement("InstantiationArg0", MethodTableNative::InstantiationArg0)
FCFuncEnd()

FCFuncStart(gStubHelperFuncs)
Expand Down
Loading
Loading