Skip to content

Convert JIT_Box* to C# #115134

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -517,6 +517,45 @@ private static void ArrayTypeCheck_Helper(object obj, void* elementType)
}
}

// Helpers for boxing
[DebuggerHidden]
internal static object? Box_Nullable(MethodTable* srcMT, ref byte nullableData)
{
Debug.Assert(srcMT->IsNullable);

// If 'hasValue' is false, return null.
if (!Unsafe.As<byte, bool>(ref nullableData))
return null;

// Allocate a new instance of the T in Nullable<T>.
MethodTable* dstMT = srcMT->InstantiationArg0();
ref byte srcValue = ref Unsafe.Add(ref nullableData, srcMT->NullableValueAddrOffset);

// Delegate to non-nullable boxing implementation
return Box(dstMT, ref srcValue);
}

[DebuggerHidden]
internal static object Box(MethodTable* typeMT, ref byte unboxedData)
{
Debug.Assert(typeMT != null);
Debug.Assert(typeMT->IsValueType);

object boxed = RuntimeTypeHandle.GetThreadLocalAllocInternal(typeMT, out uint bytes) ??
RuntimeTypeHandle.InternalAllocNoChecks(typeMT);

if (typeMT->ContainsGCPointers)
{
Buffer.BulkMoveWithWriteBarrier(ref boxed.GetRawData(), ref unboxedData, bytes);
}
else
{
SpanHelpers.Memmove(ref boxed.GetRawData(), ref unboxedData, bytes);
}

return boxed;
}

// Helpers for Unboxing
#if FEATURE_TYPEEQUIVALENCE
[DebuggerHidden]
Expand Down Expand Up @@ -615,27 +654,8 @@ internal static void Unbox_Nullable(ref byte destPtr, MethodTable* typeMT, objec
[DebuggerHidden]
internal static object? ReboxFromNullable(MethodTable* srcMT, object src)
{
Debug.Assert(srcMT->IsNullable);

ref byte nullableData = ref src.GetRawData();

// If 'hasValue' is false, return null.
if (!Unsafe.As<byte, bool>(ref nullableData))
return null;

// Allocate a new instance of the T in Nullable<T>.
MethodTable* dstMT = srcMT->InstantiationArg0();
object dst = RuntimeTypeHandle.InternalAlloc(dstMT);

// Copy data from the Nullable<T>.
ref byte srcData = ref Unsafe.Add(ref nullableData, srcMT->NullableValueAddrOffset);
ref byte dstData = ref RuntimeHelpers.GetRawData(dst);
if (dstMT->ContainsGCPointers)
Buffer.BulkMoveWithWriteBarrier(ref dstData, ref srcData, dstMT->GetNumInstanceFieldBytesIfContainsGCPointers());
else
SpanHelpers.Memmove(ref dstData, ref srcData, dstMT->GetNumInstanceFieldBytes());

return dst;
return Box_Nullable(srcMT, ref nullableData);
}

[DebuggerHidden]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -445,8 +445,8 @@ internal static unsafe bool ObjectHasComponentSize(object obj)
/// <param name="data">A reference to the data to box.</param>
/// <returns>A boxed instance of the value at <paramref name="data"/>.</returns>
/// <remarks>This method includes proper handling for nullable value types as well.</remarks>
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern unsafe object? Box(MethodTable* methodTable, ref byte data);
internal static unsafe object? Box(MethodTable* methodTable, ref byte data) =>
methodTable->IsNullable ? CastHelpers.Box_Nullable(methodTable, ref data) : CastHelpers.Box(methodTable, ref data);

// Given an object reference, returns its MethodTable*.
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ internal static object InternalAlloc(RuntimeType type)
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeTypeHandle_InternalAlloc")]
private static unsafe partial void InternalAlloc(MethodTable* pMT, ObjectHandleOnStack result);

[MethodImpl(MethodImplOptions.NoInlining)]
internal static object InternalAllocNoChecks(MethodTable* pMT)
{
object? result = null;
Expand Down Expand Up @@ -449,6 +450,9 @@ public ModuleHandle GetModuleHandle()
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool CompareCanonicalHandles(RuntimeType left, RuntimeType right);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern object? GetThreadLocalAllocInternal(MethodTable* pMT, out uint size);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern int GetArrayRank(RuntimeType type);

Expand Down
10 changes: 5 additions & 5 deletions src/coreclr/inc/jithelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,11 @@

JITHELPER(CORINFO_HELP_ISINSTANCEOF_EXCEPTION, JIT_IsInstanceOfException, METHOD__NIL)

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)
DYNAMICJITHELPER(CORINFO_HELP_UNBOX_TYPETEST,NULL, METHOD__CASTHELPERS__UNBOX_TYPETEST)
DYNAMICJITHELPER(CORINFO_HELP_UNBOX_NULLABLE,NULL, METHOD__CASTHELPERS__UNBOX_NULLABLE)
DYNAMICJITHELPER(CORINFO_HELP_BOX, NULL, METHOD__CASTHELPERS__BOX)
DYNAMICJITHELPER(CORINFO_HELP_BOX_NULLABLE, NULL, METHOD__CASTHELPERS__BOX_NULLABLE)
DYNAMICJITHELPER(CORINFO_HELP_UNBOX, NULL, METHOD__CASTHELPERS__UNBOX)
DYNAMICJITHELPER(CORINFO_HELP_UNBOX_TYPETEST, NULL, METHOD__CASTHELPERS__UNBOX_TYPETEST)
DYNAMICJITHELPER(CORINFO_HELP_UNBOX_NULLABLE, NULL, METHOD__CASTHELPERS__UNBOX_NULLABLE)

DYNAMICJITHELPER(CORINFO_HELP_GETREFANY, NULL, METHOD__TYPED_REFERENCE__GETREFANY)
DYNAMICJITHELPER(CORINFO_HELP_ARRADDR_ST, NULL, METHOD__CASTHELPERS__STELEMREF)
Expand Down
65 changes: 0 additions & 65 deletions src/coreclr/vm/amd64/JitHelpers_Slow.asm
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ endif

JIT_NEW equ ?JIT_New@@YAPEAVObject@@PEAUCORINFO_CLASS_STRUCT_@@@Z
CopyValueClassUnchecked equ ?CopyValueClassUnchecked@@YAXPEAX0PEAVMethodTable@@@Z
JIT_Box equ ?JIT_Box@@YAPEAVObject@@PEAUCORINFO_CLASS_STRUCT_@@PEAX@Z
g_pStringClass equ ?g_pStringClass@@3PEAVMethodTable@@EA
FramedAllocateString equ ?FramedAllocateString@@YAPEAVStringObject@@K@Z
JIT_NewArr1 equ ?JIT_NewArr1@@YAPEAVObject@@PEAUCORINFO_CLASS_STRUCT_@@_J@Z
Expand All @@ -50,7 +49,6 @@ INVALIDGCVALUE equ 0CCCCCCCDh

extern JIT_NEW:proc
extern CopyValueClassUnchecked:proc
extern JIT_Box:proc
extern g_pStringClass:QWORD
extern FramedAllocateString:proc
extern JIT_NewArr1:proc
Expand Down Expand Up @@ -197,69 +195,6 @@ LEAF_ENTRY JIT_TrialAllocSFastSP, _TEXT
jmp JIT_NEW
LEAF_END JIT_TrialAllocSFastSP, _TEXT

; HCIMPL2(Object*, JIT_Box, CORINFO_CLASS_HANDLE type, void* unboxedData)
NESTED_ENTRY JIT_BoxFastUP, _TEXT

; m_BaseSize is guaranteed to be a multiple of 8.
mov r8d, [rcx + OFFSET__MethodTable__m_BaseSize]

inc [g_global_alloc_lock]
jnz JIT_Box

mov rax, [g_global_alloc_context + OFFSETOF__ee_alloc_context__alloc_ptr] ; alloc_ptr
mov r10, [g_global_alloc_context + OFFSETOF__ee_alloc_context__m_CombinedLimit] ; m_CombinedLimit

add r8, rax

cmp r8, r10
ja NoAlloc

test rdx, rdx
je NullRef

mov qword ptr [g_global_alloc_context + OFFSETOF__ee_alloc_context__alloc_ptr], r8 ; update the alloc ptr
mov [rax], rcx
mov [g_global_alloc_lock], -1

; Check whether the object contains pointers
test dword ptr [rcx + OFFSETOF__MethodTable__m_dwFlags], MethodTable__enum_flag_ContainsGCPointers
jnz ContainsPointers

; We have no pointers - emit a simple inline copy loop

mov ecx, [rcx + OFFSET__MethodTable__m_BaseSize]
sub ecx, 18h ; sizeof(ObjHeader) + sizeof(Object) + last slot

CopyLoop:
mov r8, [rdx+rcx]
mov [rax+rcx+8], r8

sub ecx, 8
jge CopyLoop
REPRET

ContainsPointers:

; Do call to CopyValueClassUnchecked(object, data, pMT)

push_vol_reg rax
alloc_stack 20h
END_PROLOGUE

mov r8, rcx
lea rcx, [rax + 8]
call CopyValueClassUnchecked

add rsp, 20h
pop rax
ret

NoAlloc:
NullRef:
mov [g_global_alloc_lock], -1
jmp JIT_Box
NESTED_END JIT_BoxFastUP, _TEXT

LEAF_ENTRY AllocateStringFastUP, _TEXT

; We were passed the number of characters in ECX
Expand Down
1 change: 0 additions & 1 deletion src/coreclr/vm/arm/stubs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1652,7 +1652,6 @@ void InitJITHelpers1()
SetJitHelperFunction(CORINFO_HELP_NEWSFAST, JIT_NewS_MP_FastPortable);
SetJitHelperFunction(CORINFO_HELP_NEWARR_1_VC, JIT_NewArr1VC_MP_FastPortable);
SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1OBJ_MP_FastPortable);
SetJitHelperFunction(CORINFO_HELP_BOX, JIT_Box_MP_FastPortable);

ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateString_MP_FastPortable), ECall::FastAllocateString);
}
Expand Down
1 change: 0 additions & 1 deletion src/coreclr/vm/arm64/stubs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -878,7 +878,6 @@ void InitJITHelpers1()
SetJitHelperFunction(CORINFO_HELP_NEWSFAST_ALIGN8, JIT_NewS_MP_FastPortable);
SetJitHelperFunction(CORINFO_HELP_NEWARR_1_VC, JIT_NewArr1VC_MP_FastPortable);
SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1OBJ_MP_FastPortable);
SetJitHelperFunction(CORINFO_HELP_BOX, JIT_Box_MP_FastPortable);

ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateString_MP_FastPortable), ECall::FastAllocateString);
}
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/vm/corelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -1249,10 +1249,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, BOX, Box, NoSig)
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, BOX_NULLABLE, Box_Nullable, NoSig)
DEFINE_METHOD(CASTHELPERS, UNBOX_NULLABLE, Unbox_Nullable, NoSig)
DEFINE_METHOD(CASTHELPERS, UNBOX_TYPETEST, Unbox_TypeTest, NoSig)

Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/ecalllist.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ FCFuncStart(gCOMTypeHandleFuncs)
FCFuncElement("ContainsGenericVariables", RuntimeTypeHandle::ContainsGenericVariables)
FCFuncElement("IsUnmanagedFunctionPointer", RuntimeTypeHandle::IsUnmanagedFunctionPointer)
FCFuncElement("CompareCanonicalHandles", RuntimeTypeHandle::CompareCanonicalHandles)
FCFuncElement("GetThreadLocalAllocInternal", RuntimeTypeHandle::GetThreadLocalAllocInternal)
FCFuncEnd()

FCFuncStart(gMetaDataImport)
Expand Down Expand Up @@ -337,7 +338,6 @@ FCFuncStart(gRuntimeHelpers)
FCFuncElement("TryEnsureSufficientExecutionStack", ReflectionInvocation::TryEnsureSufficientExecutionStack)
FCFuncElement("AllocTailCallArgBufferWorker", TailCallHelp::AllocTailCallArgBufferWorker)
FCFuncElement("GetTailCallInfo", TailCallHelp::GetTailCallInfo)
FCFuncElement("Box", JIT_Box)
FCFuncEnd()

FCFuncStart(gMethodTableFuncs)
Expand Down
Loading
Loading