From 113056f292aa75d4423cbcd9d4be94300791edd4 Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Tue, 29 Apr 2025 14:19:24 +0300 Subject: [PATCH 1/2] Convert JIT_Box* to C# --- .../src/System/MulticastDelegate.CoreCLR.cs | 4 +- .../Runtime/CompilerServices/CastHelpers.cs | 60 ++++--- .../RuntimeHelpers.CoreCLR.cs | 4 +- .../src/System/RuntimeHandles.cs | 24 +-- src/coreclr/inc/jithelpers.h | 10 +- src/coreclr/vm/amd64/JitHelpers_Slow.asm | 67 -------- src/coreclr/vm/arm/stubs.cpp | 1 - src/coreclr/vm/arm64/stubs.cpp | 1 - src/coreclr/vm/corelib.h | 2 + src/coreclr/vm/ecalllist.h | 2 +- src/coreclr/vm/gcheaputilities.h | 6 +- src/coreclr/vm/i386/jitinterfacex86.cpp | 147 +----------------- src/coreclr/vm/jithelpers.cpp | 95 ----------- src/coreclr/vm/jitinterface.h | 3 - src/coreclr/vm/jitinterfacegen.cpp | 8 - src/coreclr/vm/loongarch64/stubs.cpp | 1 - src/coreclr/vm/object.cpp | 4 +- src/coreclr/vm/object.h | 6 +- src/coreclr/vm/riscv64/stubs.cpp | 1 - src/coreclr/vm/runtimehandles.cpp | 44 ++++++ src/coreclr/vm/runtimehandles.h | 2 + 21 files changed, 126 insertions(+), 366 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index 0846f7060ce38f..9740d23843f42b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -164,10 +164,10 @@ private static bool TrySetSlot(object?[] a, int index, object o) return false; } - private MulticastDelegate NewMulticastDelegate(object[] invocationList, int invocationCount, bool thisIsMultiCastAlready) + private unsafe MulticastDelegate NewMulticastDelegate(object[] invocationList, int invocationCount, bool thisIsMultiCastAlready) { // First, allocate a new multicast delegate just like this one, i.e. same type as the this object - MulticastDelegate result = Unsafe.As(RuntimeTypeHandle.InternalAllocNoChecks((RuntimeType)GetType())); + MulticastDelegate result = Unsafe.As(RuntimeTypeHandle.InternalAllocNoChecks(RuntimeHelpers.GetMethodTable(this))); // Performance optimization - if this already points to a true multicast delegate, // copy _methodPtr and _methodPtrAux fields rather than calling into the EE to get them diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs index a441e4890f9df5..e30e94eaf415b0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs @@ -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 (nullableData == 0) + return null; + + // Allocate a new instance of the T in Nullable. + 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); + + // A null can be passed for boxing of a null ref. + _ = Unsafe.ReadUnaligned(ref unboxedData); + + object boxed = RuntimeTypeHandle.InternalAllocNoChecks(typeMT); + if (typeMT->ContainsGCPointers) + { + Buffer.BulkMoveWithWriteBarrier(ref boxed.GetRawData(), ref unboxedData, typeMT->GetNumInstanceFieldBytesIfContainsGCPointers()); + } + else + { + SpanHelpers.Memmove(ref boxed.GetRawData(), ref unboxedData, typeMT->GetNumInstanceFieldBytes()); + } + + return boxed; + } + // Helpers for Unboxing #if FEATURE_TYPEEQUIVALENCE [DebuggerHidden] @@ -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(ref nullableData)) - return null; - - // Allocate a new instance of the T in Nullable. - MethodTable* dstMT = srcMT->InstantiationArg0(); - object dst = RuntimeTypeHandle.InternalAlloc(dstMT); - - // Copy data from the Nullable. - 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] diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 629774bd0df2a2..2f4a401b18a689 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -445,8 +445,8 @@ internal static unsafe bool ObjectHasComponentSize(object obj) /// A reference to the data to box. /// A boxed instance of the value at . /// This method includes proper handling for nullable value types as well. - [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*. // diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index 8e03b99165d557..b8ae179ff25ce5 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -299,25 +299,27 @@ internal static object InternalAlloc(RuntimeType type) [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeTypeHandle_InternalAlloc")] private static unsafe partial void InternalAlloc(MethodTable* pMT, ObjectHandleOnStack result); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static object InternalAllocNoChecks(MethodTable* pMT) { - object? result = null; - InternalAllocNoChecks(pMT, ObjectHandleOnStack.Create(ref result)); - return result!; - } + return InternalAllocNoChecks_FastPath(pMT) ?? InternalAllocNoChecksWorker(pMT); - internal static object InternalAllocNoChecks(RuntimeType type) - { - Debug.Assert(!type.GetNativeTypeHandle().IsTypeDesc); - object? result = null; - InternalAllocNoChecks(type.GetNativeTypeHandle().AsMethodTable(), ObjectHandleOnStack.Create(ref result)); - GC.KeepAlive(type); - return result!; + [MethodImpl(MethodImplOptions.NoInlining)] + static object InternalAllocNoChecksWorker(MethodTable* pMT) + { + object? result = null; + InternalAllocNoChecks(pMT, ObjectHandleOnStack.Create(ref result)); + return result!; + } } [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeTypeHandle_InternalAllocNoChecks")] private static unsafe partial void InternalAllocNoChecks(MethodTable* pMT, ObjectHandleOnStack result); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern object? InternalAllocNoChecks_FastPath(MethodTable* pMT); + /// /// Given a RuntimeType, returns information about how to activate it via calli /// semantics. This method will ensure the type object is fully initialized within diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h index 3ea5ed3f7af3b7..35994841224f3f 100644 --- a/src/coreclr/inc/jithelpers.h +++ b/src/coreclr/inc/jithelpers.h @@ -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) diff --git a/src/coreclr/vm/amd64/JitHelpers_Slow.asm b/src/coreclr/vm/amd64/JitHelpers_Slow.asm index 63bc8cc43f1aec..8b1fbbb7f8d38f 100644 --- a/src/coreclr/vm/amd64/JitHelpers_Slow.asm +++ b/src/coreclr/vm/amd64/JitHelpers_Slow.asm @@ -40,8 +40,6 @@ EXTERN g_GCShadowEnd:QWORD 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 @@ -49,8 +47,6 @@ JIT_NewArr1 equ ?JIT_NewArr1@@YAPEAVObject@@PEAUCORINFO_CLASS_ST 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 @@ -197,69 +193,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 diff --git a/src/coreclr/vm/arm/stubs.cpp b/src/coreclr/vm/arm/stubs.cpp index e28af8bcd67aaa..4dd096adc50456 100644 --- a/src/coreclr/vm/arm/stubs.cpp +++ b/src/coreclr/vm/arm/stubs.cpp @@ -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); } diff --git a/src/coreclr/vm/arm64/stubs.cpp b/src/coreclr/vm/arm64/stubs.cpp index 751595b9f528b3..53fe10b5488b54 100644 --- a/src/coreclr/vm/arm64/stubs.cpp +++ b/src/coreclr/vm/arm64/stubs.cpp @@ -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); } diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index fe86ea43697e35..ba3c64b7e08375 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -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) diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 7fa6ae288bb697..f3102c0f4ac7d0 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -97,6 +97,7 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncElement("ContainsGenericVariables", RuntimeTypeHandle::ContainsGenericVariables) FCFuncElement("IsUnmanagedFunctionPointer", RuntimeTypeHandle::IsUnmanagedFunctionPointer) FCFuncElement("CompareCanonicalHandles", RuntimeTypeHandle::CompareCanonicalHandles) + FCFuncElement("InternalAllocNoChecks_FastPath", RuntimeTypeHandle::InternalAllocNoChecks_FastPath) FCFuncEnd() FCFuncStart(gMetaDataImport) @@ -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) diff --git a/src/coreclr/vm/gcheaputilities.h b/src/coreclr/vm/gcheaputilities.h index 8b40521c2377fa..03f3716eb773f7 100644 --- a/src/coreclr/vm/gcheaputilities.h +++ b/src/coreclr/vm/gcheaputilities.h @@ -248,9 +248,13 @@ class GCHeapUtilities { #endif // FEATURE_SVR_GC } - static bool UseThreadAllocationContexts() + bool UseThreadAllocationContexts() { +#if (defined(TARGET_X86) || defined(TARGET_AMD64)) && !defined(TARGET_UNIX) return s_useThreadAllocationContexts; +#else + return true; +#endif } #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP diff --git a/src/coreclr/vm/i386/jitinterfacex86.cpp b/src/coreclr/vm/i386/jitinterfacex86.cpp index 63d603e722513d..ee59b847cf689b 100644 --- a/src/coreclr/vm/i386/jitinterfacex86.cpp +++ b/src/coreclr/vm/i386/jitinterfacex86.cpp @@ -371,138 +371,6 @@ void *JIT_TrialAlloc::GenAllocSFast(Flags flags) return (void *)pStub->GetEntryPoint(); } - -void *JIT_TrialAlloc::GenBox(Flags flags) -{ - STANDARD_VM_CONTRACT; - - CPUSTUBLINKER sl; - - CodeLabel *noLock = sl.NewCodeLabel(); - CodeLabel *noAlloc = sl.NewCodeLabel(); - CodeLabel *nullRef = sl.NewCodeLabel(); - - // Save address of value to be boxed - sl.X86EmitPushReg(kEBX); - sl.Emit16(0xda8b); - - // Check for null ref - // test edx, edx - sl.X86EmitR2ROp(0x85, kEDX, kEDX); - - // je nullRef - sl.X86EmitCondJump(nullRef, X86CondCode::kJE); - - // Emit the main body of the trial allocator - EmitCore(&sl, noLock, noAlloc, flags); - - // Here we are at the end of the success case - - // Check whether the object contains pointers - // test [ecx]MethodTable.m_dwFlags,MethodTable::enum_flag_ContainsGCPointers - sl.X86EmitOffsetModRM(0xf7, (X86Reg)0x0, kECX, offsetof(MethodTable, m_dwFlags)); - sl.Emit32(MethodTable::enum_flag_ContainsGCPointers); - - CodeLabel *pointerLabel = sl.NewCodeLabel(); - - // jne pointerLabel - sl.X86EmitCondJump(pointerLabel, X86CondCode::kJNE); - - // We have no pointers - emit a simple inline copy loop - - // mov ecx, [ecx]MethodTable.m_BaseSize - sl.X86EmitOffsetModRM(0x8b, kECX, kECX, offsetof(MethodTable, m_BaseSize)); - - // sub ecx,12 - sl.X86EmitSubReg(kECX, 12); - - CodeLabel *loopLabel = sl.NewCodeLabel(); - - sl.EmitLabel(loopLabel); - - // mov edx,[ebx+ecx] - sl.X86EmitOp(0x8b, kEDX, kEBX, 0, kECX, 1); - - // mov [eax+ecx+4],edx - sl.X86EmitOp(0x89, kEDX, kEAX, 4, kECX, 1); - - // sub ecx,4 - sl.X86EmitSubReg(kECX, 4); - - // jg loopLabel - sl.X86EmitCondJump(loopLabel, X86CondCode::kJGE); - - sl.X86EmitPopReg(kEBX); - - sl.X86EmitReturn(0); - - // Arrive at this label if there are pointers in the object - sl.EmitLabel(pointerLabel); - - // Do call to CopyValueClassUnchecked(object, data, pMT) - -#ifdef UNIX_X86_ABI -#define STACK_ALIGN_PADDING 12 - // Make pad to align esp - sl.X86EmitSubEsp(STACK_ALIGN_PADDING); -#endif // UNIX_X86_ABI - - // Pass pMT (still in ECX) - sl.X86EmitPushReg(kECX); - - // Pass data (still in EBX) - sl.X86EmitPushReg(kEBX); - - // Save the address of the object just allocated - // mov ebx,eax - sl.Emit16(0xD88B); - - - // Pass address of first user byte in the newly allocated object - sl.X86EmitAddReg(kEAX, 4); - sl.X86EmitPushReg(kEAX); - - // call CopyValueClass - sl.X86EmitCall(sl.NewExternalCodeLabel((LPVOID) CopyValueClassUnchecked), 12); -#ifdef UNIX_X86_ABI - // Make pad to align esp - sl.X86EmitAddEsp(STACK_ALIGN_PADDING); -#undef STACK_ALIGN_PADDING -#endif // UNIX_X86_ABI - - // Restore the address of the newly allocated object and return it. - // mov eax,ebx - sl.Emit16(0xC38B); - - sl.X86EmitPopReg(kEBX); - - sl.X86EmitReturn(0); - - // Come here in case of no space or null ref - sl.EmitLabel(noAlloc); - sl.EmitLabel(nullRef); - - // Release the lock in the uniprocessor case - EmitNoAllocCode(&sl, flags); - - // Come here in case of failure to get the lock - sl.EmitLabel(noLock); - - // Restore the address of the value to be boxed - // mov edx,ebx - sl.Emit16(0xD38B); - - // pop ebx - sl.X86EmitPopReg(kEBX); - - // Jump to the slow version of JIT_Box - sl.X86EmitNearJump(sl.NewExternalCodeLabel((LPVOID) JIT_Box)); - - Stub *pStub = sl.Link(SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap(), NEWSTUB_FL_NONE, "Box"); - - return (void *)pStub->GetEntryPoint(); -} - void *JIT_TrialAlloc::GenAllocArray(Flags flags) { STANDARD_VM_CONTRACT; @@ -793,7 +661,6 @@ void InitJITHelpers1() static const LPCWSTR pHelperNames[ETW_NUM_JIT_HELPERS] = { W("@NewObject"), W("@NewObjectAlign8"), - W("@Box"), W("@NewArray1Object"), W("@NewArray1ValueType"), W("@NewArray1ObjectAlign8"), @@ -824,14 +691,12 @@ void InitJITHelpers1() SetJitHelperFunction(CORINFO_HELP_NEWSFAST, pMethodAddresses[0]); pMethodAddresses[1] = JIT_TrialAlloc::GenAllocSFast((JIT_TrialAlloc::Flags)(flags|JIT_TrialAlloc::ALIGN8 | JIT_TrialAlloc::ALIGN8OBJ)); SetJitHelperFunction(CORINFO_HELP_NEWSFAST_ALIGN8, pMethodAddresses[1]); - pMethodAddresses[2] = JIT_TrialAlloc::GenBox(flags); - SetJitHelperFunction(CORINFO_HELP_BOX, pMethodAddresses[2]); - pMethodAddresses[3] = JIT_TrialAlloc::GenAllocArray((JIT_TrialAlloc::Flags)(flags|JIT_TrialAlloc::OBJ_ARRAY)); - SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, pMethodAddresses[3]); - pMethodAddresses[4] = JIT_TrialAlloc::GenAllocArray(flags); - SetJitHelperFunction(CORINFO_HELP_NEWARR_1_VC, pMethodAddresses[4]); - pMethodAddresses[5] = JIT_TrialAlloc::GenAllocArray((JIT_TrialAlloc::Flags)(flags|JIT_TrialAlloc::ALIGN8)); - SetJitHelperFunction(CORINFO_HELP_NEWARR_1_ALIGN8, pMethodAddresses[5]); + pMethodAddresses[2] = JIT_TrialAlloc::GenAllocArray((JIT_TrialAlloc::Flags)(flags|JIT_TrialAlloc::OBJ_ARRAY)); + SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, pMethodAddresses[2]); + pMethodAddresses[3] = JIT_TrialAlloc::GenAllocArray(flags); + SetJitHelperFunction(CORINFO_HELP_NEWARR_1_VC, pMethodAddresses[3]); + pMethodAddresses[4] = JIT_TrialAlloc::GenAllocArray((JIT_TrialAlloc::Flags)(flags|JIT_TrialAlloc::ALIGN8)); + SetJitHelperFunction(CORINFO_HELP_NEWARR_1_ALIGN8, pMethodAddresses[4]); // If allocation logging is on, then we divert calls to FastAllocateString to an Ecall method, not this // generated method. Find this workaround in Ecall::Init() in ecall.cpp. diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index c4f6c032050126..4cad7fef302410 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -942,101 +942,6 @@ HCIMPLEND // VALUETYPE/BYREF HELPERS // //======================================================================== -/*************************************************************/ -HCIMPL2_RAW(Object*, JIT_Box_MP_FastPortable, CORINFO_CLASS_HANDLE type, void* unboxedData) -{ - CONTRACTL { - THROWS; - DISABLED(GC_TRIGGERS); - MODE_COOPERATIVE; - } CONTRACTL_END; - - if (unboxedData == nullptr) - { - // Tail call to the slow helper - return HCCALL2(JIT_Box, type, unboxedData); - } - - _ASSERTE(GCHeapUtilities::UseThreadAllocationContexts()); - ee_alloc_context* eeAllocContext = &t_runtime_thread_locals.alloc_context; - gc_alloc_context* allocContext = &eeAllocContext->m_GCAllocContext; - - TypeHandle typeHandle(type); - _ASSERTE(!typeHandle.IsTypeDesc()); // heap objects must have method tables - MethodTable *methodTable = typeHandle.AsMethodTable(); - // The fast helper should never be called for nullable types. - _ASSERTE(!methodTable->IsNullable()); - -#ifdef FEATURE_64BIT_ALIGNMENT - if (methodTable->RequiresAlign8()) - { - return HCCALL2(JIT_Box, type, unboxedData); - } -#endif - - SIZE_T size = methodTable->GetBaseSize(); - _ASSERTE(size % DATA_ALIGNMENT == 0); - - BYTE *allocPtr = allocContext->alloc_ptr; - _ASSERTE(allocPtr <= eeAllocContext->getCombinedLimit()); - if (size > static_cast(eeAllocContext->getCombinedLimit() - allocPtr)) - { - // Tail call to the slow helper - return HCCALL2(JIT_Box, type, unboxedData); - } - - allocContext->alloc_ptr = allocPtr + size; - - _ASSERTE(allocPtr != nullptr); - Object *object = reinterpret_cast(allocPtr); - _ASSERTE(object->HasEmptySyncBlockInfo()); - object->SetMethodTable(methodTable); - - // Copy the data into the object - CopyValueClass(object->UnBox(), unboxedData, methodTable); - - return object; -} -HCIMPLEND_RAW - -/*************************************************************/ -HCIMPL2(Object*, JIT_Box, CORINFO_CLASS_HANDLE type, void* unboxedData) -{ - FCALL_CONTRACT; - - OBJECTREF newobj = NULL; - HELPER_METHOD_FRAME_BEGIN_RET_NOPOLL(); // Set up a frame - GCPROTECT_BEGININTERIOR(unboxedData); - HELPER_METHOD_POLL(); - - // A null can be passed for boxing of a null ref. - if (unboxedData == NULL) - COMPlusThrow(kNullReferenceException); - - TypeHandle clsHnd(type); - - _ASSERTE(!clsHnd.IsTypeDesc()); // boxable types have method tables - - MethodTable *pMT = clsHnd.AsMethodTable(); - - _ASSERTE(pMT->IsFullyLoaded()); - - _ASSERTE (pMT->IsValueType() && !pMT->IsByRefLike()); - -#ifdef _DEBUG - if (g_pConfig->FastGCStressLevel()) { - GetThread()->DisableStressHeap(); - } -#endif // _DEBUG - - newobj = pMT->FastBox(&unboxedData); - - GCPROTECT_END(); - HELPER_METHOD_FRAME_END(); - return(OBJECTREFToObject(newobj)); -} -HCIMPLEND - /*************************************************************/ HCIMPL2(BOOL, JIT_IsInstanceOfException, CORINFO_CLASS_HANDLE type, Object* obj) { diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index 5d562dcbfbef6e..1b421ab3a20281 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -1092,9 +1092,6 @@ void DoGcStress (PT_CONTEXT regs, NativeCodeVersion nativeCodeVersion); // means that the caller does not care whether the string is pinned or not. STRINGREF* ConstructStringLiteral(CORINFO_MODULE_HANDLE scopeHnd, mdToken metaTok, void** ppPinnedString = nullptr); -FCDECL2(Object*, JIT_Box_MP_FastPortable, CORINFO_CLASS_HANDLE type, void* data); -FCDECL2(Object*, JIT_Box, CORINFO_CLASS_HANDLE type, void* data); - BOOL ObjIsInstanceOf(Object *pObject, TypeHandle toTypeHnd, BOOL throwCastException = FALSE); class InlinedCallFrame; diff --git a/src/coreclr/vm/jitinterfacegen.cpp b/src/coreclr/vm/jitinterfacegen.cpp index 1d55e308402fe3..2190f295b2a17a 100644 --- a/src/coreclr/vm/jitinterfacegen.cpp +++ b/src/coreclr/vm/jitinterfacegen.cpp @@ -20,13 +20,8 @@ #ifdef HOST_64BIT -// These are the multi-processor-optimized versions of the allocation helpers -// that must be written in assembly. -EXTERN_C Object* JIT_BoxFastMP (CORINFO_CLASS_HANDLE type, void* unboxedData); - // These are the single-processor-optimized versions of the allocation helpers. EXTERN_C Object* JIT_TrialAllocSFastSP(CORINFO_CLASS_HANDLE typeHnd_); -EXTERN_C Object* JIT_BoxFastUP (CORINFO_CLASS_HANDLE type, void* unboxedData); EXTERN_C Object* AllocateStringFastUP (CLR_I4 cch); EXTERN_C Object* JIT_NewArr1OBJ_UP (CORINFO_CLASS_HANDLE arrayMT, INT_PTR size); @@ -65,7 +60,6 @@ void InitJITHelpers1() #ifdef TARGET_UNIX SetJitHelperFunction(CORINFO_HELP_NEWSFAST, JIT_NewS_MP_FastPortable); SetJitHelperFunction(CORINFO_HELP_NEWSFAST_ALIGN8, JIT_NewS_MP_FastPortable); - SetJitHelperFunction(CORINFO_HELP_BOX, JIT_Box_MP_FastPortable); SetJitHelperFunction(CORINFO_HELP_NEWARR_1_VC, JIT_NewArr1VC_MP_FastPortable); SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1OBJ_MP_FastPortable); @@ -76,7 +70,6 @@ void InitJITHelpers1() { SetJitHelperFunction(CORINFO_HELP_NEWSFAST, JIT_NewS_MP_FastPortable); SetJitHelperFunction(CORINFO_HELP_NEWSFAST_ALIGN8, JIT_NewS_MP_FastPortable); - SetJitHelperFunction(CORINFO_HELP_BOX, JIT_Box_MP_FastPortable); SetJitHelperFunction(CORINFO_HELP_NEWARR_1_VC, JIT_NewArr1VC_MP_FastPortable); SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1OBJ_MP_FastPortable); @@ -90,7 +83,6 @@ void InitJITHelpers1() // InlineGetThread versions because there is no need to call GetThread SetJitHelperFunction(CORINFO_HELP_NEWSFAST, JIT_TrialAllocSFastSP); SetJitHelperFunction(CORINFO_HELP_NEWSFAST_ALIGN8, JIT_TrialAllocSFastSP); - SetJitHelperFunction(CORINFO_HELP_BOX, JIT_BoxFastUP); SetJitHelperFunction(CORINFO_HELP_NEWARR_1_VC, JIT_NewArr1VC_UP); SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1OBJ_UP); diff --git a/src/coreclr/vm/loongarch64/stubs.cpp b/src/coreclr/vm/loongarch64/stubs.cpp index 920a2d892aa0b2..e763157e07b1ac 100644 --- a/src/coreclr/vm/loongarch64/stubs.cpp +++ b/src/coreclr/vm/loongarch64/stubs.cpp @@ -891,7 +891,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); } diff --git a/src/coreclr/vm/object.cpp b/src/coreclr/vm/object.cpp index dc469d2baf6af0..7285b85a9a556e 100644 --- a/src/coreclr/vm/object.cpp +++ b/src/coreclr/vm/object.cpp @@ -348,7 +348,7 @@ void SetObjectReferenceUnchecked(OBJECTREF *dst,OBJECTREF ref) ErectWriteBarrier(dst, ref); } -void STDCALL CopyValueClassUnchecked(void* dest, void* src, MethodTable *pMT) +void CopyValueClassUnchecked(void* dest, void* src, MethodTable *pMT) { STATIC_CONTRACT_NOTHROW; @@ -395,7 +395,7 @@ void STDCALL CopyValueClassUnchecked(void* dest, void* src, MethodTable *pMT) // Copy value class into the argument specified by the argDest. // The destOffset is nonzero when copying values into Nullable, it is the offset // of the T value inside of the Nullable -void STDCALL CopyValueClassArgUnchecked(ArgDestination *argDest, void* src, MethodTable *pMT, int destOffset) +void CopyValueClassArgUnchecked(ArgDestination *argDest, void* src, MethodTable *pMT, int destOffset) { STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_GC_NOTRIGGER; diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index 663a4161f83917..ca924879b7939c 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -493,8 +493,8 @@ inline void ClearObjectReference(OBJECTREF* dst) // CopyValueClass sets a value class field -void STDCALL CopyValueClassUnchecked(void* dest, void* src, MethodTable *pMT); -void STDCALL CopyValueClassArgUnchecked(ArgDestination *argDest, void* src, MethodTable *pMT, int destOffset); +void CopyValueClassUnchecked(void* dest, void* src, MethodTable *pMT); +void CopyValueClassArgUnchecked(ArgDestination *argDest, void* src, MethodTable *pMT, int destOffset); inline void InitValueClass(void *dest, MethodTable *pMT) { @@ -2510,8 +2510,6 @@ typedef PTR_ContractExceptionObject CONTRACTEXCEPTIONREF; // non-nullable case. // // To do this we need to -// * Modify the boxing helper code:JIT_Box (we don't need a special one because -// the JIT inlines the common case, so this only gets call in uncommon cases) // * Make a new helper for the Unbox case (see code:JIT_Unbox_Nullable) // * Plumb the JIT to ask for what kind of Boxing helper is needed // (see code:CEEInfo.getBoxHelper, code:CEEInfo.getUnBoxHelper diff --git a/src/coreclr/vm/riscv64/stubs.cpp b/src/coreclr/vm/riscv64/stubs.cpp index 5a2928efac970b..c2e2d97e3ebdc4 100644 --- a/src/coreclr/vm/riscv64/stubs.cpp +++ b/src/coreclr/vm/riscv64/stubs.cpp @@ -808,7 +808,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); } diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp index 7a74f1cefd9313..95a2c8c08aa19f 100644 --- a/src/coreclr/vm/runtimehandles.cpp +++ b/src/coreclr/vm/runtimehandles.cpp @@ -1143,6 +1143,50 @@ FCIMPL2(FC_BOOL_RET, RuntimeTypeHandle::CompareCanonicalHandles, ReflectClassBas } FCIMPLEND +FCIMPL1(Object*, RuntimeTypeHandle::InternalAllocNoChecks_FastPath, MethodTable* pMT) +{ + FCALL_CONTRACT; + + _ASSERTE(pMT != nullptr); + + if (!GCHeapUtilities::UseThreadAllocationContexts()) + { + return NULL; + } + + if (pMT->HasFinalizer()) + { + return NULL; + } + +#ifdef FEATURE_64BIT_ALIGNMENT + if (pMT->RequiresAlign8()) + { + return NULL; + } +#endif + + SIZE_T size = pMT->GetBaseSize(); + _ASSERTE(size % DATA_ALIGNMENT == 0); + + ee_alloc_context* allocContext = &t_runtime_thread_locals.alloc_context; + BYTE* allocPtr = allocContext->m_GCAllocContext.alloc_ptr; + BYTE* limit = allocContext->getCombinedLimit(); + + if ((SIZE_T)(limit - allocPtr) < size) + { + return NULL; // Fall back to slow path in managed + } + + allocContext->m_GCAllocContext.alloc_ptr = allocPtr + size; + Object* obj = reinterpret_cast(allocPtr); + obj->SetMethodTable(pMT); + _ASSERTE(obj->HasEmptySyncBlockInfo()); + + return obj; +} +FCIMPLEND + FCIMPL1(FC_BOOL_RET, RuntimeTypeHandle::IsGenericVariable, PTR_ReflectClassBaseObject pTypeUNSAFE) { FCALL_CONTRACT; diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h index bf672af0f8900d..52dfc53d21b363 100644 --- a/src/coreclr/vm/runtimehandles.h +++ b/src/coreclr/vm/runtimehandles.h @@ -125,6 +125,7 @@ class RuntimeTypeHandle FCDECL1(FC_BOOL_RET, ContainsGenericVariables, PTR_ReflectClassBaseObject pType); static FCDECL2(FC_BOOL_RET, CompareCanonicalHandles, PTR_ReflectClassBaseObject pLeft, PTR_ReflectClassBaseObject pRight); + static FCDECL1(Object*, InternalAllocNoChecks_FastPath, MethodTable* pMT); static FCDECL1(EnregisteredTypeHandle, GetElementTypeHandle, EnregisteredTypeHandle th); static FCDECL1(INT32, GetNumVirtuals, ReflectClassBaseObject *pType); @@ -143,6 +144,7 @@ extern "C" BOOL QCALLTYPE RuntimeTypeHandle_IsEquivalentTo(QCall::TypeHandle rtT #endif // FEATURE_TYPEEQUIVALENCE extern "C" void QCALLTYPE RuntimeTypeHandle_CreateInstanceForAnotherGenericParameter(QCall::TypeHandle pTypeHandle, TypeHandle *pInstArray, INT32 cInstArray, QCall::ObjectHandleOnStack pInstantiatedObject); extern "C" void QCALLTYPE RuntimeTypeHandle_InternalAlloc(MethodTable* pMT, QCall::ObjectHandleOnStack allocated); + extern "C" void QCALLTYPE RuntimeTypeHandle_InternalAllocNoChecks(MethodTable* pMT, QCall::ObjectHandleOnStack allocated); extern "C" void* QCALLTYPE RuntimeTypeHandle_AllocateTypeAssociatedMemory(QCall::TypeHandle type, uint32_t size); From 293c57c880de9c25206463f2924760a00dca04aa Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Fri, 2 May 2025 12:13:17 -0700 Subject: [PATCH 2/2] Update src/coreclr/vm/gcheaputilities.h --- src/coreclr/vm/gcheaputilities.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/gcheaputilities.h b/src/coreclr/vm/gcheaputilities.h index 03f3716eb773f7..3a6aec2faab171 100644 --- a/src/coreclr/vm/gcheaputilities.h +++ b/src/coreclr/vm/gcheaputilities.h @@ -248,7 +248,7 @@ class GCHeapUtilities { #endif // FEATURE_SVR_GC } - bool UseThreadAllocationContexts() + static bool UseThreadAllocationContexts() { #if (defined(TARGET_X86) || defined(TARGET_AMD64)) && !defined(TARGET_UNIX) return s_useThreadAllocationContexts;